/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/features/agenda/services/agendaMappers.js
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
// Suporta dois tipos de linha:
//   1. Evento real  (agenda_eventos do banco) — is_occurrence = false/undefined
//   2. Ocorrência virtual (gerada por useRecurrence) — is_occurrence = true
//
// Em ambos os casos o shape de saída para o FullCalendar é idêntico.

// ─────────────────────────────────────────────────────────────────────────────
// mapAgendaEventosToCalendarEvents
// ─────────────────────────────────────────────────────────────────────────────

export function mapAgendaEventosToCalendarEvents(rows) {
    return (rows || []).map(_mapRow).filter(Boolean);
}

// ─────────────────────────────────────────────────────────────────────────────
// mapAgendaEventosToClinicResourceEvents
// ─────────────────────────────────────────────────────────────────────────────

export function mapAgendaEventosToClinicResourceEvents(rows) {
    return (rows || [])
        .map((r) => {
            const ev = _mapRow(r);
            if (!ev) return null;
            ev.resourceId = normalizeId(r?.owner_id ?? r?.terapeuta_id ?? null);
            return ev;
        })
        .filter(Boolean);
}

// ─────────────────────────────────────────────────────────────────────────────
// mapper interno
// ─────────────────────────────────────────────────────────────────────────────

function _mapRow(r) {
    if (!r) return null;

    const isOccurrence = !!r.is_occurrence;
    const isRealSession = !isOccurrence;

    const ownerId = normalizeId(r?.owner_id ?? r?.terapeuta_id ?? null);

    // commitment / cores
    const commitment = r.determined_commitments ?? r.commitment ?? null;
    const baseBg = commitment?.bg_color ? `#${commitment.bg_color}` : null;
    const baseTxt = commitment?.text_color ? `#${commitment.text_color}` : null;
    const statusBg = _statusBgColor(r.status);
    const bgColor = statusBg ?? baseBg ?? undefined;
    const txtColor = baseTxt ?? (statusBg ? '#ffffff' : undefined);

    // título
    const nomeP = r.patients?.nome_completo ?? r.paciente_nome ?? r.patient_name ?? '';
    const titleBase = r.titulo_custom || r.titulo || (nomeP ? nomeP : tituloFallback(r.tipo));
    const icon = _statusIcon(r.status, isOccurrence, !!r.recurrence_id);
    const title = `${icon}${titleBase}`;

    // recorrência — nova + fallback legada
    const recurrenceId = r.recurrence_id ?? null;
    const originalDate = r.original_date ?? r.recurrence_date ?? null;
    const exceptionType = r.exception_type ?? null;

    return {
        id: r.id ?? `occ::${recurrenceId}::${originalDate}`,
        title,
        start: r.inicio_em,
        end: r.fim_em,

        ...(bgColor && { backgroundColor: bgColor, borderColor: bgColor }),
        ...(txtColor && { textColor: txtColor }),

        extendedProps: {
            // identidade
            dbId: r.id ?? null,
            isOccurrence,
            isRealSession,

            // owner
            owner_id: ownerId,
            terapeuta_id: normalizeId(r?.terapeuta_id ?? null),

            // compromisso
            tipo: r.tipo ?? null,
            status: r.status ?? null,
            determined_commitment_id: r.determined_commitment_id ?? null,
            commitment_bg_color: bgColor ?? null,
            commitment_text_color: txtColor ?? null,

            // paciente
            patient_id: r.patient_id ?? null,
            paciente_id: r.patient_id ?? null, // alias para compatibilidade com dialog/form
            paciente_nome: nomeP,
            paciente_avatar: r.patients?.avatar_url ?? r.paciente_avatar ?? null,
            paciente_status: r.patients?.status ?? r.paciente_status ?? null,

            // campos
            observacoes: r.observacoes ?? null,
            titulo_custom: r.titulo_custom ?? null,
            extra_fields: r.extra_fields ?? null,
            modalidade: r.modalidade ?? null,

            // privacidade (clínica)
            visibility_scope: r.visibility_scope ?? null,
            masked: !!r.masked,

            // recorrência — NOVA arquitetura
            recurrence_id: recurrenceId,
            original_date: originalDate,
            exception_type: exceptionType,
            exception_id: r.exception_id ?? null,
            exception_reason: r.exception_reason ?? null,

            // recorrência — fallback LEGADA (não quebra enquanto migra)
            serie_id: r.serie_id ?? recurrenceId ?? null,
            serie_dia_semana: r.agenda_series?.dia_semana ?? r.serie_dia_semana ?? null,
            serie_hora: r.agenda_series?.hora_inicio ?? r.serie_hora ?? null,
            serie_duracao: r.agenda_series?.duracao_min ?? r.serie_duracao ?? null,
            serie_status: r.agenda_series?.status ?? r.serie_status ?? null,
            is_exception: r.is_exception ?? exceptionType != null,

            // financeiro
            price: r.price ?? null,
            billed: r.billed ?? false,
            billing_contract_id: r.billing_contract_id ?? null,
            insurance_plan_id: r.insurance_plan_id ?? null,
            insurance_guide_number: r.insurance_guide_number ?? null,
            insurance_value: r.insurance_value != null ? Number(r.insurance_value) : null,
            insurance_plan_service_id: r.insurance_plan_service_id ?? null,

            // timestamps
            inicio_em: r.inicio_em,
            fim_em: r.fim_em
        }
    };
}

// ─────────────────────────────────────────────────────────────────────────────
// buildNextSessions
// ─────────────────────────────────────────────────────────────────────────────

export function buildNextSessions(rows, now = new Date()) {
    const nowMs = now.getTime();
    return (rows || [])
        .filter((r) => new Date(r.fim_em).getTime() >= nowMs)
        .slice(0, 6)
        .map((r) => ({
            id: r.id,
            title: r.titulo || tituloFallback(r.tipo),
            startISO: r.inicio_em,
            endISO: r.fim_em,
            tipo: r.tipo,
            status: r.status,
            pacienteId: r.patient_id || null
        }));
}

// ─────────────────────────────────────────────────────────────────────────────
// calcDefaultSlotDuration
// ─────────────────────────────────────────────────────────────────────────────

export function calcDefaultSlotDuration(settings) {
    const min = (settings?.usar_granularidade_custom && settings?.granularidade_min) || 0 || settings?.admin_slot_visual_minutos || 30;
    return minutesToDuration(min);
}

// ─────────────────────────────────────────────────────────────────────────────
// buildWeeklyBreakBackgroundEvents — código original preservado integralmente
// ─────────────────────────────────────────────────────────────────────────────

export function buildWeeklyBreakBackgroundEvents(pausas, rangeStart, rangeEnd) {
    if (!Array.isArray(pausas) || pausas.length === 0) return [];

    const out = [];
    const dayMs = 24 * 60 * 60 * 1000;

    for (let ts = startOfDay(rangeStart).getTime(); ts < rangeEnd.getTime(); ts += dayMs) {
        const d = new Date(ts);
        const dow = d.getDay();

        for (const p of pausas) {
            const wd = normalizeWeekday(p?.weekday ?? p?.dia_semana);
            if (wd === null || wd !== dow) continue;

            const start = asTime(p?.start ?? p?.inicio ?? p?.from);
            const end = asTime(p?.end ?? p?.fim ?? p?.to);
            if (!start || !end) continue;

            out.push({
                id: `break-${ts}-${start}-${end}`,
                start: combineDateTimeISO(d, start),
                end: combineDateTimeISO(d, end),
                display: 'background',
                overlap: false,
                extendedProps: { kind: 'break', label: p?.label ?? 'Pausa' }
            });
        }
    }

    return out;
}

// ─────────────────────────────────────────────────────────────────────────────
// buildBloqueioBackgroundEvents
// Renderiza rows de public.agenda_bloqueios como background events cinza
// no FullCalendar. Suporta:
//   - Bloqueio de dia inteiro (hora_inicio/fim NULL) → background do dia todo
//   - Bloqueio com janela horária → background no intervalo
//   - Bloqueio recorrente semanal (recorrente=true + dia_semana 0-6) →
//     repetido em todas as ocorrências do dow no range
// ─────────────────────────────────────────────────────────────────────────────

export function buildBloqueioBackgroundEvents(bloqueios, rangeStart, rangeEnd) {
    if (!Array.isArray(bloqueios) || bloqueios.length === 0) return [];
    if (!rangeStart || !rangeEnd) return [];

    const out = [];
    const dayMs = 24 * 60 * 60 * 1000;
    const startMs = startOfDay(rangeStart).getTime();
    const endMs = rangeEnd.getTime();

    for (const b of bloqueios) {
        if (!b) continue;
        const titulo = b.titulo || 'Bloqueio';

        // Recorrente semanal: itera o range varrendo dias-da-semana iguais
        if (b.recorrente && b.dia_semana != null) {
            const dow = Number(b.dia_semana);
            if (!Number.isFinite(dow) || dow < 0 || dow > 6) continue;
            const hi = asTime(b.hora_inicio);
            const hf = asTime(b.hora_fim);
            for (let ts = startMs; ts < endMs; ts += dayMs) {
                const d = new Date(ts);
                if (d.getDay() !== dow) continue;
                if (hi && hf) {
                    out.push(_makeBloqueioEvent(b.id ?? null, d, hi, hf, titulo));
                } else {
                    out.push(_makeBloqueioDayEvent(b.id ?? null, d, titulo));
                }
            }
            continue;
        }

        // Não recorrente: usa data_inicio e (opcional) data_fim
        const di = _parseISODate(b.data_inicio);
        if (!di) continue;
        const df = _parseISODate(b.data_fim) ?? di;

        // Range de dias do bloqueio (inclusive)
        for (let cur = di.getTime(); cur <= df.getTime(); cur += dayMs) {
            const d = new Date(cur);
            // Filtra fora do range visível pra evitar lixo
            if (d.getTime() + dayMs < startMs || d.getTime() > endMs) continue;
            const hi = asTime(b.hora_inicio);
            const hf = asTime(b.hora_fim);
            if (hi && hf) {
                out.push(_makeBloqueioEvent(b.id ?? null, d, hi, hf, titulo));
            } else {
                out.push(_makeBloqueioDayEvent(b.id ?? null, d, titulo));
            }
        }
    }

    return out;
}

function _makeBloqueioEvent(id, date, timeStart, timeEnd, titulo) {
    const tag = id ?? `blq-${date.getTime()}-${timeStart}-${timeEnd}`;
    return {
        id: `blq-${tag}`,
        title: titulo,
        start: combineDateTimeISO(date, timeStart),
        end: combineDateTimeISO(date, timeEnd),
        display: 'background',
        overlap: false,
        backgroundColor: '#6b728033', // cinza ~20%
        extendedProps: { kind: 'bloqueio', bloqueioId: id, label: titulo }
    };
}

function _makeBloqueioDayEvent(id, date, titulo) {
    const yyyy = date.getFullYear();
    const mm = String(date.getMonth() + 1).padStart(2, '0');
    const dd = String(date.getDate()).padStart(2, '0');
    const dateStr = `${yyyy}-${mm}-${dd}`;
    const tag = id ?? `blq-${dateStr}`;
    return {
        id: `blq-day-${tag}`,
        title: titulo,
        start: dateStr,
        allDay: true,
        display: 'background',
        overlap: false,
        backgroundColor: '#6b728033',
        extendedProps: { kind: 'bloqueio', bloqueioId: id, label: titulo }
    };
}

function _parseISODate(s) {
    if (!s) return null;
    const m = String(s).match(/^(\d{4})-(\d{2})-(\d{2})/);
    if (!m) return null;
    return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
}

// ─────────────────────────────────────────────────────────────────────────────
// minutesToDuration / tituloFallback
// ─────────────────────────────────────────────────────────────────────────────

export function minutesToDuration(min) {
    const h = Math.floor(min / 60);
    const m = min % 60;
    return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:00`;
}

export function tituloFallback(tipo) {
    const t = String(tipo || '').toLowerCase();
    if (t.includes('sess')) return 'Sessão';
    if (t.includes('block') || t.includes('bloq')) return 'Bloqueio';
    if (t.includes('pessoal')) return 'Pessoal';
    if (t.includes('clin')) return 'Clínica';
    return 'Compromisso';
}

// ─────────────────────────────────────────────────────────────────────────────
// helpers de status
// ─────────────────────────────────────────────────────────────────────────────

function _statusBgColor(status) {
    const map = {
        agendado: '#3b82f6', // azul
        realizado: '#22c55e', // verde
        faltou: '#f97316', // laranja
        cancelado: '#ef4444', // vermelho
        remarcado: '#a855f7', // roxo
        bloqueado: '#6b7280' // cinza
    };
    return map[status] ?? null;
}

function _statusIcon(status, isOccurrence, hasSerie) {
    if (status === 'realizado') return '✓ ';
    if (status === 'faltou') return '✗ ';
    if (status === 'cancelado') return '∅ ';
    if (status === 'remarcado') return '↺ ';
    if (status === 'bloqueado') return '⊘ ';
    if (hasSerie || isOccurrence) return '↻ ';
    return '';
}

// ─────────────────────────────────────────────────────────────────────────────
// helpers internos — originais preservados
// ─────────────────────────────────────────────────────────────────────────────

function normalizeId(v) {
    if (v === null || v === undefined) return null;
    const s = String(v).trim();
    return s ? s : null;
}

function normalizeWeekday(value) {
    if (value === null || value === undefined) return null;
    const n = Number(value);
    if (Number.isNaN(n)) return null;
    if (n >= 0 && n <= 6) return n;
    if (n >= 1 && n <= 7) return n === 7 ? 0 : n;
    return null;
}

function asTime(v) {
    if (!v || typeof v !== 'string') return null;
    const s = v.trim();
    if (/^\d{2}:\d{2}$/.test(s)) return `${s}:00`;
    if (/^\d{2}:\d{2}:\d{2}$/.test(s)) return s;
    return null;
}

function startOfDay(d) {
    const x = new Date(d);
    x.setHours(0, 0, 0, 0);
    return x;
}

function combineDateTimeISO(date, timeHHMMSS) {
    const yyyy = date.getFullYear();
    const mm = String(date.getMonth() + 1).padStart(2, '0');
    const dd = String(date.getDate()).padStart(2, '0');
    return `${yyyy}-${mm}-${dd}T${timeHHMMSS}`;
}
