/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/composables/useAgendaFinanceiro.js
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/

/**
 * useAgendaFinanceiro
 *
 * Camada de orquestração entre agenda e financeiro.
 * Não modifica useAgendaEvents diretamente — recebe a função de update
 * como parâmetro para manter o desacoplamento.
 *
 * Uso:
 *   const { handleStatusChange, gerarCobrancaManual } = useAgendaFinanceiro()
 *
 *   // No handler de save do componente pai:
 *   await handleStatusChange(eventoOriginal, novoStatus, agendaEvents.update)
 */

import { ref } from 'vue';
import { supabase } from '@/lib/supabase/client';
import { tenantDb } from '@/lib/supabase/tenantClient';
import { useTenantStore } from '@/stores/tenantStore';

// ─── Cache de exceções financeiras (vive enquanto o módulo estiver carregado) ─
// Chave: `${tenantId}:${exceptionType}` → FinancialException | null
const _exceptionsCache = new Map();

// ─── helper ──────────────────────────────────────────────────────────────────
async function getUid() {
    const { data, error } = await supabase.auth.getUser();
    if (error) throw error;
    const uid = data?.user?.id;
    if (!uid) throw new Error('Usuário não autenticado.');
    return uid;
}

// ─── mapeamento: status anterior → tipo de exceção a consultar ───────────────
const STATUS_TO_EXCEPTION = {
    faltou: 'patient_no_show',
    cancelado: 'patient_cancellation'
};

// ─── calcular valor cobrado por charge_mode ───────────────────────────────────
function calcChargeAmount(originalAmount, rule) {
    if (!rule || rule.charge_mode === 'none') return 0;
    if (rule.charge_mode === 'full') return originalAmount;
    if (rule.charge_mode === 'fixed_fee') return rule.charge_value ?? 0;
    if (rule.charge_mode === 'percentage') {
        const pct = rule.charge_pct ?? 0;
        return parseFloat(((originalAmount * pct) / 100).toFixed(2));
    }
    return originalAmount;
}

// ─── composable ──────────────────────────────────────────────────────────────
export function useAgendaFinanceiro() {
    const loading = ref(false);
    const error = ref(null);

    // ── getFinancialExceptionRule ─────────────────────────────────────────────
    /**
     * Busca a regra de exceção financeira para um tipo, com cache em memória.
     * Prioridade: regra própria do owner > regra global do tenant (owner_id IS NULL)
     *
     * @param {string} tenantId
     * @param {'patient_no_show'|'patient_cancellation'|'professional_cancellation'} exceptionType
     * @returns {Promise<Object|null>}
     */
    async function getFinancialExceptionRule(tenantId, exceptionType) {
        const cacheKey = `${tenantId}:${exceptionType}`;
        if (_exceptionsCache.has(cacheKey)) return _exceptionsCache.get(cacheKey);

        const uid = await getUid();

        const { data, error: err } = await tenantDb().from('financial_exceptions')
            .select('*')
            
            .eq('exception_type', exceptionType)
            .or(`owner_id.eq.${uid},owner_id.is.null`)
            .order('owner_id', { ascending: false, nullsLast: true }) // owner próprio tem prioridade
            .limit(1)
            .maybeSingle();

        if (err) {
            console.warn('[useAgendaFinanceiro] getFinancialExceptionRule:', err.message);
            return null;
        }

        _exceptionsCache.set(cacheKey, data ?? null);
        return data ?? null;
    }

    // ── gerarCobrancaManual ───────────────────────────────────────────────────
    /**
     * Gera cobrança para uma sessão existente com `billed = false`.
     * Chama a RPC `create_financial_record_for_session`.
     *
     * @param {Object} evento  - linha de agenda_eventos (com campo price)
     * @returns {Promise<{ok: boolean, data?: Object, error?: string}>}
     */
    async function gerarCobrancaManual(evento) {
        if (evento.billing_contract_id) {
            // sessão de pacote — não gera cobrança individual
            return { ok: false, error: 'Sessão faz parte de um pacote. Cobrança gerenciada pelo contrato.' };
        }

        const tenantStore = useTenantStore();
        const tenantId = tenantStore.activeTenantId;
        if (!tenantId) return { ok: false, error: 'Tenant não identificado.' };

        const ownerId = await getUid();

        loading.value = true;
        error.value = null;

        try {
            const amount = evento.price ?? 0;
            const dueDate = evento.inicio_em ? new Date(evento.inicio_em).toISOString().slice(0, 10) : new Date().toISOString().slice(0, 10);

            const { data, error: err } = await supabase.rpc('create_financial_record_for_session', {
                p_tenant_id: tenantId,
                p_owner_id: ownerId,
                p_patient_id: evento.patient_id ?? evento.paciente_id ?? null,
                p_agenda_evento_id: evento.id,
                p_amount: amount,
                p_due_date: dueDate
            });

            if (err) throw err;

            return { ok: true, data };
        } catch (e) {
            error.value = e?.message || 'Erro ao gerar cobrança.';
            return { ok: false, error: error.value };
        } finally {
            loading.value = false;
        }
    }

    // ── handleStatusChange ────────────────────────────────────────────────────
    /**
     * Orquestra a mudança de status de uma sessão + consequências financeiras.
     *
     * @param {Object}   evento         - linha atual de agenda_eventos (ANTES da mudança)
     * @param {string}   novoStatus     - novo status a aplicar
     * @param {Function} agendaUpdateFn - função que aplica o update na agenda (ex: agendaEvents.update)
     *                                    signature: (id, patch) => Promise<void>
     * @returns {Promise<{ok: boolean, error?: string}>}
     */
    async function handleStatusChange(evento, novoStatus, agendaUpdateFn) {
        // bloqueios e sessões de pacote não têm cobrança individual
        const ignorar = evento.tipo !== 'sessao' || !!evento.billing_contract_id;
        const statusAnterior = evento.status;

        loading.value = true;
        error.value = null;

        try {
            // 1. Aplica o update na agenda sempre (fonte da verdade é a agenda)
            await agendaUpdateFn(evento.id, { status: novoStatus });

            if (ignorar) return { ok: true };
            if (statusAnterior === novoStatus) return { ok: true };

            // 2. Lógica financeira por transição
            const tenantStore = useTenantStore();
            const tenantId = tenantStore.activeTenantId;

            // ── faltou / cancelado → consultar exceção financeira ──────────────
            const exceptionType = STATUS_TO_EXCEPTION[novoStatus];

            if (exceptionType) {
                const rule = await getFinancialExceptionRule(tenantId, exceptionType);

                if (!rule || rule.charge_mode === 'none') {
                    // Cancelar cobrança existente, se houver
                    if (evento.billed) {
                        const { data: existingRec } = await tenantDb().from('financial_records').select('id, status').eq('agenda_evento_id', evento.id).in('status', ['pending', 'overdue']).maybeSingle();

                        if (existingRec) {
                            await tenantDb().from('financial_records').update({ status: 'cancelled', updated_at: new Date().toISOString() }).eq('id', existingRec.id);
                        }
                    }
                    return { ok: true };
                }

                // charge_mode != 'none' → ajustar valor da cobrança existente ou criar nova
                const chargeAmount = calcChargeAmount(evento.price ?? 0, rule);

                if (evento.billed) {
                    // Atualiza o valor da cobrança existente
                    const { data: existingRec } = await tenantDb().from('financial_records').select('id').eq('agenda_evento_id', evento.id).in('status', ['pending', 'overdue']).maybeSingle();

                    if (existingRec) {
                        await tenantDb().from('financial_records')
                            .update({
                                amount: chargeAmount,
                                final_amount: chargeAmount,
                                updated_at: new Date().toISOString()
                            })
                            .eq('id', existingRec.id);
                    }
                } else if (chargeAmount > 0) {
                    // Sessão sem cobrança: gera uma nova com o valor ajustado
                    await gerarCobrancaManual({ ...evento, price: chargeAmount });
                }

                return { ok: true };
            }

            // ── remarcado → atualizar due_date da cobrança existente ────────────
            if (novoStatus === 'remarcado' && evento.billed) {
                // due_date mantém a data da sessão original por enquanto
                // (a nova data virá quando a sessão for reagendada)
                return { ok: true };
            }

            // ── agendado → realizado: nenhuma ação financeira automática ────────
            return { ok: true };
        } catch (e) {
            error.value = e?.message || 'Erro ao processar mudança de status.';
            return { ok: false, error: error.value };
        } finally {
            loading.value = false;
        }
    }

    // ── invalidar cache (use quando o usuário altera exceções financeiras) ───
    function invalidateExceptionsCache() {
        _exceptionsCache.clear();
    }

    return {
        loading,
        error,
        handleStatusChange,
        gerarCobrancaManual,
        getFinancialExceptionRule,
        invalidateExceptionsCache
    };
}
