/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Arquivo: src/features/patients/utils/patientFormatters.js
|
| Helpers de formatacao compartilhaveis entre PatientProntuario.vue (legacy)
| e MelissaPaciente.vue (Melissa nativo). Extraidos do PatientProntuario
| pra eliminar duplicacao quando MelissaPaciente substituir o legacy
| na Fase 8.
|--------------------------------------------------------------------------
*/

/**
 * Tenta varios formatos de data: ISO, DD/MM/YYYY, YYYY-MM-DD, etc.
 */
export function parseDateLoose(v) {
    if (!v) return null;
    if (v instanceof Date) return Number.isNaN(v.getTime()) ? null : v;
    const s = String(v).trim();
    if (!s) return null;
    // DD/MM/YYYY
    let m = s.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
    if (m) {
        const d = new Date(Number(m[3]), Number(m[2]) - 1, Number(m[1]));
        return Number.isNaN(d.getTime()) ? null : d;
    }
    // YYYY-MM-DD
    m = s.match(/^(\d{4})-(\d{2})-(\d{2})$/);
    if (m) {
        const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
        return Number.isNaN(d.getTime()) ? null : d;
    }
    const d = new Date(s);
    return Number.isNaN(d.getTime()) ? null : d;
}

export function dash(v) {
    const s = String(v ?? '').trim();
    return s || '—';
}

/**
 * Pega a primeira chave nao vazia de um objeto (snake_case ou camelCase).
 * Usado pra resolver discrepancias de schema (ex: 'data_nascimento' vs 'birth_date').
 */
export function pickField(obj, keys = []) {
    for (const k of keys) {
        const v = obj?.[k];
        if (v !== null && v !== undefined && String(v).trim()) return v;
    }
    return null;
}

export function onlyDigits(v) {
    return String(v ?? '').replace(/\D/g, '');
}

/**
 * Formata CPF: 00000000000 -> 000.000.000-00
 */
export function fmtCPF(v) {
    const d = onlyDigits(v);
    if (!d) return '—';
    if (d.length !== 11) return d;
    return `${d.slice(0, 3)}.${d.slice(3, 6)}.${d.slice(6, 9)}-${d.slice(9)}`;
}

/**
 * Formata RG (genericamente — varia por estado):
 *   00.000.000-0 / 0000000000 → mantem digitos com pontos a cada 3 a partir da direita.
 */
export function fmtRG(v) {
    const s = String(v ?? '').trim();
    if (!s) return '—';
    return s;
}

/**
 * Formata telefone celular pt-br: (XX) 9XXXX-XXXX ou (XX) XXXX-XXXX.
 */
export function fmtPhoneMobile(v) {
    const d = onlyDigits(v);
    if (!d) return '—';
    if (d.length === 11) return `(${d.slice(0, 2)}) ${d.slice(2, 7)}-${d.slice(7, 11)}`;
    if (d.length === 10) return `(${d.slice(0, 2)}) ${d.slice(2, 6)}-${d.slice(6, 10)}`;
    return d;
}

/**
 * Mapeia variantes de genero pra label legivel.
 */
export function fmtGender(v) {
    const s = String(v ?? '').trim();
    if (!s) return '—';
    const x = s.toLowerCase();
    if (['m', 'masc', 'masculino', 'male', 'man', 'homem'].includes(x)) return 'Masculino';
    if (['f', 'fem', 'feminino', 'female', 'woman', 'mulher'].includes(x)) return 'Feminino';
    if (['nb', 'nao-binario', 'não-binário', 'nonbinary', 'non-binary'].includes(x)) return 'Não-binário';
    if (['outro', 'other'].includes(x)) return 'Outro';
    return s;
}

/**
 * Mapeia variantes de estado civil pra label pt-br.
 */
export function fmtMarital(v) {
    const s = String(v ?? '').trim();
    if (!s) return '—';
    const x = s.toLowerCase();
    if (['solteiro', 'solteira', 'single'].includes(x)) return 'Solteiro(a)';
    if (['casado', 'casada', 'married'].includes(x)) return 'Casado(a)';
    if (['divorciado', 'divorciada', 'divorced'].includes(x)) return 'Divorciado(a)';
    if (['viuvo', 'viúva', 'viuvo(a)', 'widowed'].includes(x)) return 'Viúvo(a)';
    if (['uniao estavel', 'união estável', 'civil union'].includes(x)) return 'União estável';
    return s;
}

export function fmtDateBR(v) {
    const d = parseDateLoose(v);
    if (!d) return v ? dash(v) : '—';
    return `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}/${d.getFullYear()}`;
}

/**
 * Hora curta HH:MM (24h pt-br).
 */
export function fmtHourShort(iso) {
    if (!iso) return '';
    const d = new Date(iso);
    if (Number.isNaN(d.getTime())) return '';
    return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
}

/**
 * Dia da semana abreviado pt-br (seg/ter/qua...).
 */
export function fmtDayShort(iso) {
    if (!iso) return '';
    const d = new Date(iso);
    if (Number.isNaN(d.getTime())) return '';
    return d.toLocaleDateString('pt-BR', { weekday: 'short' }).replace('.', '');
}

export function fmtDateTimeBR(iso) {
    if (!iso) return '—';
    const d = new Date(iso);
    if (Number.isNaN(d.getTime())) return iso;
    const dd = String(d.getDate()).padStart(2, '0');
    const mm = String(d.getMonth() + 1).padStart(2, '0');
    const hh = String(d.getHours()).padStart(2, '0');
    const mi = String(d.getMinutes()).padStart(2, '0');
    return `${dd}/${mm}/${d.getFullYear()} ${hh}:${mi}`;
}

/**
 * Bytes -> string legivel (B/KB/MB/GB).
 */
export function fmtSize(bytes) {
    const b = Number(bytes) || 0;
    if (b < 1024) return `${b} B`;
    if (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;
    if (b < 1024 * 1024 * 1024) return `${(b / (1024 * 1024)).toFixed(1)} MB`;
    return `${(b / (1024 * 1024 * 1024)).toFixed(1)} GB`;
}

/**
 * Map de tipos de documento clinico pra label pt-br.
 */
export const DOC_TYPE_LABEL = {
    atestado: 'Atestado',
    receita: 'Receita',
    laudo: 'Laudo',
    encaminhamento: 'Encaminhamento',
    termo: 'Termo',
    termo_assinado: 'Termo assinado',
    relatorio: 'Relatório',
    declaracao: 'Declaração',
    outro: 'Outro'
};

/**
 * Map de dia da semana (0=Domingo) -> label pt-br.
 */
export const WEEKDAY_LABEL = ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'];
export const WEEKDAY_LABEL_SHORT = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'];

/**
 * Label legivel da regra de recorrencia.
 * Ex: "Toda segunda às 14:00", "A cada 2 semanas, terça às 09:00",
 *     "Quartas e sextas às 16:00", "Mensal no dia 15".
 */
export function fmtRecurrenceLabel(rule) {
    if (!rule) return '—';
    const time = String(rule.start_time || '').slice(0, 5);
    const interval = Number(rule.interval) || 1;

    if (rule.type === 'weekly' || (rule.type === 'biweekly' && interval === 1)) {
        const dow = (rule.weekdays || [])[0];
        if (dow == null) return time ? `Semanal às ${time}` : 'Semanal';
        const dayLbl = WEEKDAY_LABEL[dow] || '?';
        if (rule.type === 'biweekly') {
            return time ? `Quinzenal · ${dayLbl} às ${time}` : `Quinzenal · ${dayLbl}`;
        }
        return time ? `Toda ${dayLbl.toLowerCase()} às ${time}` : `Toda ${dayLbl.toLowerCase()}`;
    }
    if (rule.type === 'custom_weekdays') {
        const dows = (rule.weekdays || []).map((d) => WEEKDAY_LABEL_SHORT[d]).filter(Boolean);
        const dayList = dows.length ? dows.join(', ') : '?';
        return time ? `${dayList} às ${time}` : dayList;
    }
    if (rule.type === 'monthly') {
        return time ? `Mensal às ${time}` : 'Mensal';
    }
    if (rule.type === 'yearly') {
        return time ? `Anual às ${time}` : 'Anual';
    }
    return rule.type || 'Recorrência';
}

/**
 * Label pro fim da regra: "Sem data de fim", "Até DD/MM/YYYY", "N sessões no total".
 */
export function fmtRecurrenceFim(rule) {
    if (!rule) return '';
    if (rule.end_date) return `Até ${fmtDateBR(rule.end_date)}`;
    if (rule.max_occurrences) {
        const n = Number(rule.max_occurrences);
        return `${n} ${n === 1 ? 'sessão' : 'sessões'} no total`;
    }
    return 'Sem data de fim';
}

/**
 * Channel label pra conversa: whatsapp -> WhatsApp, sms -> SMS, email -> E-mail.
 */
export function chConvLabel(c) {
    const k = String(c || '').toLowerCase();
    if (k === 'whatsapp') return 'WhatsApp';
    if (k === 'sms') return 'SMS';
    if (k === 'email') return 'E-mail';
    return c || '';
}

export function fmtCurrency(v) {
    if (v === null || v === undefined || v === '') return '—';
    return `R$ ${Number(v).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
}

export function sessionDuration(inicio, fim) {
    if (!inicio || !fim) return null;
    const diff = new Date(fim) - new Date(inicio);
    if (diff <= 0) return null;
    const min = Math.round(diff / 60000);
    if (min < 60) return `${min} min`;
    const h = Math.floor(min / 60);
    const m = min % 60;
    return m ? `${h}h ${m}min` : `${h}h`;
}

/**
 * Data relativa em pt-BR.
 * Retorna: "agora", "ha 5 min", "ha 2 h", "ontem", "ha 3 dias",
 * "em 2 dias", "em 3 sem", ou data absoluta para >= 5 semanas.
 */
export function fmtRelative(iso) {
    if (!iso) return '—';
    const target = new Date(iso).getTime();
    if (Number.isNaN(target)) return '—';
    const diff = target - Date.now();
    const abs = Math.abs(diff);
    const past = diff < 0;
    const min = Math.round(abs / 60000);
    if (min < 1) return 'agora';
    if (min < 60) return past ? `há ${min} min` : `em ${min} min`;
    const h = Math.round(min / 60);
    if (h < 24) return past ? `há ${h} h` : `em ${h} h`;
    const d = Math.round(h / 24);
    if (d === 1) return past ? 'ontem' : 'amanhã';
    if (d < 7) return past ? `há ${d} dias` : `em ${d} dias`;
    const w = Math.round(d / 7);
    if (w < 5) return past ? `há ${w} sem` : `em ${w} sem`;
    return fmtDateBR(iso);
}

/**
 * Idade em anos com base em data de nascimento.
 */
export function calcAge(v) {
    const d = parseDateLoose(v);
    if (!d) return null;
    const now = new Date();
    let age = now.getFullYear() - d.getFullYear();
    const m = now.getMonth() - d.getMonth();
    if (m < 0 || (m === 0 && now.getDate() < d.getDate())) age--;
    return age;
}

/**
 * Status visual de uma sessao da agenda.
 */
export const STATUS_LABEL = {
    agendado: 'Agendado',
    realizado: 'Realizado',
    realizada: 'Realizado',
    faltou: 'Faltou',
    falta: 'Faltou',
    cancelado: 'Cancelado',
    cancelada: 'Cancelado',
    remarcado: 'Remarcado',
    bloqueado: 'Bloqueado'
};

/**
 * Determina o status financeiro de um lancamento:
 * - "pago": paid_at preenchido
 * - "vencido": due_date < hoje E paid_at vazio
 * - "pendente": demais casos com paid_at vazio
 */
export function recordStatus(r) {
    if (r?.paid_at) return 'pago';
    if (r?.due_date) {
        const ms = new Date(r.due_date + 'T23:59:59').getTime();
        if (!Number.isNaN(ms) && ms < Date.now()) return 'vencido';
    }
    return 'pendente';
}

export const RECORD_STATUS_LABEL = {
    pago: 'Pago',
    pendente: 'Pendente',
    vencido: 'Vencido'
};

/**
 * Mapeia variantes de payment_method pra label legivel.
 */
export function fmtPaymentMethod(v) {
    const s = String(v ?? '').toLowerCase();
    if (!s) return '';
    if (s === 'pix') return 'PIX';
    if (s === 'cartao' || s === 'cartão' || s === 'credit_card') return 'Cartão';
    if (s === 'dinheiro' || s === 'cash') return 'Dinheiro';
    if (s === 'boleto') return 'Boleto';
    if (s === 'transferencia' || s === 'transfer' || s === 'ted' || s === 'doc') return 'Transferência';
    if (s === 'convenio' || s === 'convênio') return 'Convênio';
    return v;
}

export const STATUS_SEVERITY = {
    agendado: 'info',
    realizado: 'success',
    realizada: 'success',
    faltou: 'danger',
    falta: 'danger',
    cancelado: 'warn',
    cancelada: 'warn',
    remarcado: 'secondary',
    bloqueado: 'secondary'
};

/**
 * Tag styling com contraste auto (texto preto/branco baseado em luminancia).
 */
function normalizeHexColor(c) {
    const s = String(c ?? '').trim();
    if (!s) return '';
    if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(s)) return s;
    if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(s)) return `#${s}`;
    return s;
}

function hexToRgb(hex) {
    const h = String(hex || '').replace('#', '').trim();
    if (!/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(h)) return null;
    const full = h.length === 3 ? h.split('').map((ch) => ch + ch).join('') : h;
    const r = parseInt(full.slice(0, 2), 16);
    const g = parseInt(full.slice(2, 4), 16);
    const b = parseInt(full.slice(4, 6), 16);
    if ([r, g, b].some((n) => Number.isNaN(n))) return null;
    return { r, g, b };
}

function relativeLuminance({ r, g, b }) {
    const srgb = [r, g, b].map((v) => v / 255).map((c) => (c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)));
    return 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2];
}

export function bestTextColor(bg) {
    const rgb = hexToRgb(normalizeHexColor(bg));
    if (!rgb) return '#0f172a';
    return relativeLuminance(rgb) < 0.45 ? '#ffffff' : '#0f172a';
}

export function tagStyle(t) {
    const bg = normalizeHexColor(t?.color || t?.cor);
    if (!bg) return {};
    return { background: bg, color: bestTextColor(bg), borderColor: 'transparent' };
}
