/*
 * useMelissaEventos — composables que carregam eventos reais da agenda
 * --------------------------------------------------
 * Pattern espelhado de `src/features/agenda/pages/AgendaTerapeutaPage.vue`
 * (loadMonthSearchRows ~ linha 539).
 *
 * Exporta dois composables:
 *   - useMelissaEventosSemana(refDateRef) — semana de refDate (ref<Date>)
 *   - useMelissaEventosHoje()             — apenas o dia atual
 *
 * Forma normalizada do evento:
 *   {
 *     id, tipo, status, titulo,
 *     pacienteNome, modalidade, descricao,
 *     color, label,
 *     inicio_em, fim_em,
 *     startH, endH,        // decimais (9.5 = 09:30) — usado pelo layout
 *     dateKey              // 'YYYY-MM-DD' pra agrupar por dia
 *   }
 *
 * Sem auth/tenant: retorna [] silencioso (UI segue funcional).
 *
 * Inclui ocorrências virtuais de recorrência (via useRecurrence.loadAndExpand)
 * mescladas com linhas reais — assim widgets ("Hoje", mini-cal, "Ver todas")
 * mostram sessões futuras de séries semanais/quinzenais mesmo antes da
 * materialização. Falha do expand não bloqueia: fallback silencioso pra reais.
 */
import { ref, watch, onMounted, computed } from 'vue';
import { supabase } from '@/lib/supabase/client';
import { tenantDb } from '@/lib/supabase/tenantClient';
import { useTenantStore } from '@/stores/tenantStore';
import { useMelissaCacheStore, MELISSA_CACHE_TTL } from '@/stores/melissaCacheStore';
import { useRecurrence } from '@/features/agenda/composables/useRecurrence';

// ── Cores por tipo/status (consistente com o resto do Melissa) ──
function pickColor(tipo, status) {
    const s = String(status || '').toLowerCase();
    if (s === 'realizado' || s === 'realizada') return '#10b981';
    if (s === 'faltou') return '#ef4444';
    if (s === 'cancelado' || s === 'cancelada') return '#94a3b8';

    const t = String(tipo || '').toLowerCase();
    if (t === 'supervisao' || t === 'supervisão') return '#a855f7';
    if (t === 'reuniao' || t === 'reunião') return '#0ea5e9';
    if (t === 'bloqueio') return '#64748b';
    return '#6366f1'; // sessao default
}

function isoToDecimalHour(iso) {
    const d = new Date(iso);
    return d.getHours() + d.getMinutes() / 60;
}

function normalizeEvent(r) {
    // Linhas reais (joined query) trazem `patients.nome_completo`; ocorrências
    // virtuais (loadAndExpand) trazem `paciente_nome`/`patient_name`. Aceita os 3
    // pra que a mesma normalização sirva pros dois tipos sem perder o rótulo.
    const pacNome = r.patients?.nome_completo || r.paciente_nome || r.patient_name || '';
    const pacStatus = r.patients?.status || r.paciente_status || '';
    return {
        id: r.id,
        tipo: r.tipo || 'sessao',
        status: r.status || '',
        titulo: r.titulo || r.titulo_custom || '',
        patient_id: r.patient_id || null,
        pacienteNome: pacNome,
        // Status do paciente — usado pelo MelissaAgenda pra marcar visualmente
        // eventos de paciente Inativo/Arquivado (borda tracejada + opacidade).
        paciente_status: pacStatus,
        modalidade: r.modalidade || '',
        descricao: r.observacoes || '',
        color: pickColor(r.tipo, r.status),
        label: pacNome || r.titulo || r.titulo_custom || '—',
        inicio_em: r.inicio_em,
        fim_em: r.fim_em,
        startH: isoToDecimalHour(r.inicio_em),
        endH: isoToDecimalHour(r.fim_em),
        dateKey: String(r.inicio_em || '').slice(0, 10),
        price: r.price != null ? Number(r.price) : 0,
        billed: !!r.billed,
        // Flag pra consumidores diferenciarem materializada vs virtual de recorrência
        // (UI pode optar por badge, click handler pode materializar antes de editar).
        is_occurrence: !!r.is_occurrence,
        recurrence_id: r.recurrence_id ?? null
    };
}

// ── Helper interno: garante uid + tenant + faz a query ──
// Carrega linhas reais de agenda_eventos no range + expande recorrências
// virtuais (loadAndExpand). Falha da expansão não bloqueia o fetch: cai pra
// só-reais com aviso no console — UI continua funcional.
async function _fetchRange(start, end) {
    const tenantStore = useTenantStore();
    const { data: userData } = await supabase.auth.getUser();
    const userId = userData?.user?.id || null;
    if (typeof tenantStore.ensureLoaded === 'function') {
        await tenantStore.ensureLoaded();
    }
    const tid = tenantStore.activeTenantId || tenantStore.tenantId || null;

    if (!userId || !tid) return [];

    // Recurrence_id/date inclusos no select pra mergeWithStoredSessions dedupar
    // ocorrências já materializadas (sessões reais ganham precedência sobre virtuais).
    const { data, error } = await tenantDb().from('agenda_eventos')
        .select('id, tipo, status, titulo, inicio_em, fim_em, modalidade, observacoes, patient_id, price, billed, recurrence_id, recurrence_date, patients!agenda_eventos_patient_id_fkey(nome_completo, status)')
        .eq('owner_id', userId)
        .is('mirror_of_event_id', null)
        .gte('inicio_em', start.toISOString())
        .lt('inicio_em', end.toISOString())
        .order('inicio_em', { ascending: true });

    if (error) throw error;
    const realRows = data || [];

    // Expansão de recorrências — falha silenciosa não derruba a UI.
    let virtualOccs = [];
    try {
        const { loadAndExpand } = useRecurrence();
        const expanded = await loadAndExpand(userId, start, end, realRows, tid);
        virtualOccs = expanded.filter((r) => r.is_occurrence);
    } catch (e) {
        // eslint-disable-next-line no-console
        console.warn('[useMelissaEventos] recurrence expand falhou:', e);
    }

    return [...realRows, ...virtualOccs].map(normalizeEvent);
}

// ── Range helpers ──────────────────────────────────────────────
function rangeSemana(refDate) {
    const ref = new Date(refDate);
    const dow = ref.getDay(); // 0=dom, 1=seg
    const diff = dow === 0 ? -6 : 1 - dow;
    const segunda = new Date(ref);
    segunda.setDate(ref.getDate() + diff);
    segunda.setHours(0, 0, 0, 0);

    // domingo final → usa dia seguinte 00:00 com `lt` pra incluir tudo até 23:59:59.999
    const apósDomingo = new Date(segunda);
    apósDomingo.setDate(segunda.getDate() + 7);
    return { start: segunda, end: apósDomingo };
}

function rangeHoje() {
    const inicio = new Date();
    inicio.setHours(0, 0, 0, 0);
    const fim = new Date(inicio);
    fim.setDate(inicio.getDate() + 1); // amanhã 00:00
    return { start: inicio, end: fim };
}

// ── COMPOSABLE 1: semana visível (MelissaAgenda) ──────────────
export function useMelissaEventosSemana(refDateRef) {
    const eventos = ref([]);
    const loading = ref(false);
    const error = ref(null);

    async function fetch() {
        loading.value = true;
        error.value = null;
        try {
            const { start, end } = rangeSemana(refDateRef.value);
            eventos.value = await _fetchRange(start, end);
        } catch (e) {
            error.value = e?.message || 'Erro ao carregar agenda';
            eventos.value = [];
            // eslint-disable-next-line no-console
            console.warn('[useMelissaEventosSemana]', e);
        } finally {
            loading.value = false;
        }
    }

    onMounted(fetch);
    watch(refDateRef, fetch);

    // Helper computado: agrupa por dateKey ('YYYY-MM-DD')
    const eventosPorDia = computed(() => {
        const map = {};
        for (const ev of eventos.value) {
            (map[ev.dateKey] ||= []).push(ev);
        }
        return map;
    });

    return { eventos, eventosPorDia, loading, error, refetch: fetch };
}

// ── COMPOSABLE 3: range arbitrário (FullCalendar passa via datesSet) ──
// Usado pelo MelissaAgenda — refetcha sempre que start/end mudam (mudança
// de view, navegação prev/next/today). Cobre Day/Week/Month/List sem custo.
export function useMelissaEventosRange(startRef, endRef) {
    const eventos = ref([]);
    const loading = ref(false);
    const error = ref(null);

    async function fetch() {
        const s = startRef.value;
        const e = endRef.value;
        if (!s || !e) { eventos.value = []; return; }

        loading.value = true;
        error.value = null;
        try {
            eventos.value = await _fetchRange(new Date(s), new Date(e));
        } catch (err) {
            error.value = err?.message || 'Erro ao carregar agenda';
            eventos.value = [];
            // eslint-disable-next-line no-console
            console.warn('[useMelissaEventosRange]', err);
        } finally {
            loading.value = false;
        }
    }

    onMounted(fetch);
    watch([startRef, endRef], fetch);

    return { eventos, loading, error, refetch: fetch };
}

// ── Busca server-side: por nome de paciente ou título do evento ──
// Usado pela busca da toolbar do MelissaAgenda. Procura sem range temporal
// (acha sessões fora do que está visível no FC). Limite de 20 resultados,
// ordenados por inicio_em DESC (mais recente primeiro).
//
// Acento-insensitive: troca cada vogal/c do termo por um character class
// que casa com todas as variantes ("andre" vira "[aáàâã]ndr[eéèê]") e usa
// `imatch` (operador POSIX `~*` do Postgres — case-insensitive regex).
// Estratégia evita depender da extensão `unaccent` no DB.
function _buildAccentInsensitivePattern(term) {
    // Escapa regex specials primeiro pra não quebrar o pattern.
    const escaped = String(term || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    const map = {
        a: '[aáàâãAÁÀÂÃ]', e: '[eéèêEÉÈÊ]', i: '[iíìîIÍÌÎ]',
        o: '[oóòôõOÓÒÔÕ]', u: '[uúùûUÚÙÛ]', c: '[cçCÇ]',
        A: '[aáàâãAÁÀÂÃ]', E: '[eéèêEÉÈÊ]', I: '[iíìîIÍÌÎ]',
        O: '[oóòôõOÓÒÔÕ]', U: '[uúùûUÚÙÛ]', C: '[cçCÇ]'
    };
    return escaped.split('').map((ch) => map[ch] || ch).join('');
}

export async function searchEventosByText(termo) {
    const term = String(termo || '').trim();
    if (term.length < 2) return [];

    const tenantStore = useTenantStore();
    const { data: userData } = await supabase.auth.getUser();
    const userId = userData?.user?.id || null;
    if (typeof tenantStore.ensureLoaded === 'function') {
        await tenantStore.ensureLoaded();
    }
    const tid = tenantStore.activeTenantId || tenantStore.tenantId || null;
    if (!userId || !tid) return [];

    const SELECT = 'id, tipo, status, titulo, inicio_em, fim_em, modalidade, observacoes, patient_id, price, billed, patients!agenda_eventos_patient_id_fkey(nome_completo, status)';
    const pattern = _buildAccentInsensitivePattern(term);

    try {
        const [byPatient, byTitle] = await Promise.all([
            tenantDb().from('agenda_eventos')
                .select(SELECT.replace('patients!agenda_eventos_patient_id_fkey', 'patients!inner!agenda_eventos_patient_id_fkey'))
                .eq('owner_id', userId)
                .is('mirror_of_event_id', null)
                .filter('patients.nome_completo', 'imatch', pattern)
                .order('inicio_em', { ascending: false })
                .limit(20),
            tenantDb().from('agenda_eventos')
                .select(SELECT)
                .eq('owner_id', userId)
                .is('mirror_of_event_id', null)
                .filter('titulo', 'imatch', pattern)
                .order('inicio_em', { ascending: false })
                .limit(20)
        ]);
        if (byPatient.error) throw byPatient.error;
        if (byTitle.error) throw byTitle.error;

        const merged = [...(byPatient.data || []), ...(byTitle.data || [])];
        const seen = new Set();
        const unique = [];
        for (const r of merged) {
            if (seen.has(r.id)) continue;
            seen.add(r.id);
            unique.push(r);
        }
        unique.sort((a, b) => String(b.inicio_em).localeCompare(String(a.inicio_em)));
        return unique.slice(0, 20).map(normalizeEvent);
    } catch (e) {
        // eslint-disable-next-line no-console
        console.warn('[searchEventosByText]', e);
        return [];
    }
}

// ── COMPOSABLE 4: todas as sessões de um paciente (sem range) ──
// Usado pelo banner "Ver todas" da MelissaAgenda quando o usuário
// quer escapar do range visível e ver o histórico completo do
// paciente selecionado. Diferente do useMelissaEventosRange, aqui
// filtramos por patient_id e ignoramos qualquer range temporal.
//
// Retorna eventos ordenados por inicio_em DESC (mais recente primeiro)
// — coerente com listas de "histórico" no resto do sistema.
export function useMelissaTodasSessoesPaciente() {
    const eventos = ref([]);
    const loading = ref(false);
    const error = ref(null);

    async function fetch(patientId) {
        if (!patientId) { eventos.value = []; return; }
        loading.value = true;
        error.value = null;
        try {
            const tenantStore = useTenantStore();
            const { data: userData } = await supabase.auth.getUser();
            const userId = userData?.user?.id || null;
            if (typeof tenantStore.ensureLoaded === 'function') {
                await tenantStore.ensureLoaded();
            }
            const tid = tenantStore.activeTenantId || tenantStore.tenantId || null;
            if (!userId || !tid) { eventos.value = []; return; }

            const { data, error: err } = await tenantDb().from('agenda_eventos')
                .select('id, tipo, status, titulo, inicio_em, fim_em, modalidade, observacoes, patient_id, price, billed, recurrence_id, recurrence_date, patients!agenda_eventos_patient_id_fkey(nome_completo, status)')
                .eq('owner_id', userId)
                .eq('patient_id', patientId)
                .is('mirror_of_event_id', null)
                .order('inicio_em', { ascending: false });

            if (err) throw err;
            const realRows = data || [];

            // Expansão de recorrências do paciente — range padrão -6mo → +12mo
            // (cobre histórico recente + ~1 ano de série semanal/quinzenal futura).
            let virtualOccs = [];
            try {
                const { loadAndExpand } = useRecurrence();
                const now = new Date();
                const rangeStart = new Date(now.getFullYear(), now.getMonth() - 6, 1);
                const rangeEnd = new Date(now.getFullYear() + 1, now.getMonth(), 1);
                const expanded = await loadAndExpand(userId, rangeStart, rangeEnd, realRows, tid);
                virtualOccs = expanded.filter((r) => r.is_occurrence && r.patient_id === patientId);
            } catch (rec) {
                // eslint-disable-next-line no-console
                console.warn('[useMelissaTodasSessoesPaciente] recurrence expand falhou:', rec);
            }

            const merged = [...realRows, ...virtualOccs];
            merged.sort((a, b) => String(b.inicio_em).localeCompare(String(a.inicio_em)));
            eventos.value = merged.map(normalizeEvent);
        } catch (e) {
            error.value = e?.message || 'Erro ao carregar sessões';
            eventos.value = [];
            // eslint-disable-next-line no-console
            console.warn('[useMelissaTodasSessoesPaciente]', e);
        } finally {
            loading.value = false;
        }
    }

    function reset() {
        eventos.value = [];
        error.value = null;
    }

    return { eventos, loading, error, fetch, reset };
}

// ── COMPOSABLE 2: apenas hoje (MelissaLayout) ──────────────────
// opts: { autoFetch=true } — passar false pra adiar o fetch inicial
// (MelissaLayout faz isso quando a URL inicial já tem uma seção, pra
// não competir com o fetch da seção que vai cobrir o resumo).
export function useMelissaEventosHoje(opts = {}) {
    const autoFetch = opts.autoFetch !== false;
    const cache = useMelissaCacheStore();
    const eventos = ref([]);
    const loading = ref(false);
    const error = ref(null);

    async function _doFetch(cacheKey) {
        const { start, end } = rangeHoje();
        const data = await _fetchRange(start, end);
        cache.set('eventosHoje', data, cacheKey);
        eventos.value = data;
        return data;
    }

    // useCache=true (boot/auto): stale-while-revalidate.
    // useCache=false (refetch pós-mutation: status sessão, etc): força.
    async function _fetch({ useCache = true } = {}) {
        const today = new Date();
        // Cache key amarra ao dia — depois de 00:00 vira automaticamente outro slot.
        const cacheKey = `${today.getFullYear()}-${today.getMonth()}-${today.getDate()}`;

        if (useCache) {
            const cached = cache.get('eventosHoje', cacheKey, MELISSA_CACHE_TTL.eventosHoje);
            if (cached) {
                eventos.value = cached;
                _doFetch(cacheKey).catch((e) => {
                    // eslint-disable-next-line no-console
                    console.warn('[useMelissaEventosHoje] revalidate', e);
                });
                return;
            }
        } else {
            cache.invalidate('eventosHoje');
        }

        loading.value = true;
        error.value = null;
        try {
            await _doFetch(cacheKey);
        } catch (e) {
            error.value = e?.message || 'Erro ao carregar agenda';
            eventos.value = [];
            // eslint-disable-next-line no-console
            console.warn('[useMelissaEventosHoje]', e);
        } finally {
            loading.value = false;
        }
    }

    if (autoFetch) onMounted(() => _fetch({ useCache: true }));

    return {
        eventos,
        loading,
        error,
        // refetch força query nova (após status update etc).
        refetch: () => _fetch({ useCache: false }),
        // fetchCached é stale-while-revalidate (idle/defer).
        fetchCached: () => _fetch({ useCache: true })
    };
}
