/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/features/agenda/composables/useCommitmentServices.js
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
// CRUD de commitment_services — itens de serviço vinculados a um evento.
// CRUD de recurrence_rule_services — template de serviços de uma regra de recorrência.
//
// Interface pública:
//   loadItems(eventId)                     → Array<CommitmentItem>
//   saveItems(eventId, items, opts?)       → void  (delete+insert; opts.markCustomized marca services_customized no evento)
//   loadRuleItems(ruleId)                  → Array<CommitmentItem>
//   saveRuleItems(ruleId, items)           → void  (delete+insert no template da regra)
//   loadItemsOrTemplate(eventId, ruleId)   → Array<CommitmentItem>  (próprios ou template)
//   propagateToSerie(ruleId, items, opts?) → void  (ocorrências materializadas com services_customized=false)

import { supabase } from '@/lib/supabase/client';

import { tenantDb } from '@/lib/supabase/tenantClient';
// Shape interno de CommitmentItem:
// {
//   service_id:    uuid,
//   service_name:  string,   // display only — não gravado no banco
//   quantity:      number,
//   unit_price:    number,   // snapshot de services.price no momento da adição
//   discount_pct:  number,
//   discount_flat: number,
//   final_price:   number,
// }

/** Mapeia uma linha do banco para CommitmentItem (compartilhado entre commitment_services e recurrence_rule_services) */
function _mapRow(r) {
    return {
        service_id: r.service_id,
        service_name: r.services?.name ?? '',
        quantity: Number(r.quantity),
        unit_price: Number(r.unit_price),
        discount_pct: Number(r.discount_pct ?? 0),
        discount_flat: Number(r.discount_flat ?? 0),
        final_price: Number(r.final_price)
    };
}

export function useCommitmentServices() {
    // ── Carregar itens de um evento ──────────────────────────────────────
    async function loadItems(eventId) {
        if (!eventId) return [];

        const { data, error } = await tenantDb().from('commitment_services').select('service_id, quantity, unit_price, discount_pct, discount_flat, final_price, services(name)').eq('commitment_id', eventId).order('created_at', { ascending: true });

        if (error) throw error;
        return (data || []).map(_mapRow);
    }

    // ── Salvar itens de um evento ────────────────────────────────────────
    // Estratégia: DELETE dos itens existentes + INSERT dos novos.
    // Garante idempotência em edições sem risco de duplicatas.
    //
    // opts.markCustomized = true: após salvar, marca services_customized = true
    // no agenda_eventos correspondente, impedindo que edições do evento raiz
    // sobrescrevam os serviços desta ocorrência individual.
    async function saveItems(eventId, items, { markCustomized = false } = {}) {
        if (!eventId) throw new Error('eventId é obrigatório para salvar commitment_services.');

        // 1. Remove itens existentes deste evento
        const { error: deleteError } = await tenantDb().from('commitment_services').delete().eq('commitment_id', eventId);

        if (deleteError) throw deleteError;

        // 2. Insere os novos itens (se houver)
        if (items?.length) {
            const rows = items.map((item) => ({
                commitment_id: eventId,
                service_id: item.service_id,
                quantity: item.quantity,
                unit_price: item.unit_price,
                discount_pct: item.discount_pct ?? 0,
                discount_flat: item.discount_flat ?? 0,
                final_price: item.final_price
            }));

            const { error: insertError } = await tenantDb().from('commitment_services').insert(rows);

            if (insertError) throw insertError;
        }

        // 3. Marca a ocorrência como customizada (impede sobrescrita por edições do raiz)
        if (markCustomized) {
            const { error: updateError } = await tenantDb().from('agenda_eventos').update({ services_customized: true }).eq('id', eventId);

            if (updateError) throw updateError;
        }
    }

    // ── Carregar template de serviços de uma regra ───────────────────────
    // Retorna os itens armazenados em recurrence_rule_services para a regra.
    async function loadRuleItems(ruleId) {
        if (!ruleId) return [];

        const { data, error } = await tenantDb().from('recurrence_rule_services').select('service_id, quantity, unit_price, discount_pct, discount_flat, final_price, services(name)').eq('rule_id', ruleId).order('created_at', { ascending: true });

        if (error) throw error;
        return (data || []).map(_mapRow);
    }

    // ── Salvar template de serviços de uma regra ─────────────────────────
    // Estratégia: DELETE + INSERT — mesmo padrão de saveItems.
    // Chamado ao criar uma recorrência com serviços ou ao editar o evento
    // raiz com escopo 'todos' / 'este_e_seguintes'.
    async function saveRuleItems(ruleId, items) {
        if (!ruleId) throw new Error('ruleId é obrigatório para salvar recurrence_rule_services.');

        const { error: deleteError } = await tenantDb().from('recurrence_rule_services').delete().eq('rule_id', ruleId);

        if (deleteError) throw deleteError;

        if (!items?.length) return;

        const rows = items.map((item) => ({
            rule_id: ruleId,
            service_id: item.service_id,
            quantity: item.quantity,
            unit_price: item.unit_price,
            discount_pct: item.discount_pct ?? 0,
            discount_flat: item.discount_flat ?? 0,
            final_price: item.final_price
        }));

        const { error: insertError } = await tenantDb().from('recurrence_rule_services').insert(rows);

        if (insertError) throw insertError;
    }

    // ── Carregar itens próprios ou herdar template da regra ──────────────
    // Retorna os commitment_services do evento se existirem.
    // Se o evento não tiver itens próprios e ruleId for fornecido,
    // retorna o template da regra (ocorrência ainda não customizada).
    async function loadItemsOrTemplate(eventId, ruleId, { allowEmpty = false } = {}) {
        const own = await loadItems(eventId);
        if (own.length > 0) return own;
        if (allowEmpty) return [];
        if (ruleId) return loadRuleItems(ruleId);
        return [];
    }

    // ── Propagar itens para ocorrências materializadas da série ──────────
    // Atualiza commitment_services nos agenda_eventos com recurrence_id = ruleId
    // onde services_customized = false (não foram editados individualmente).
    //
    // Invariante adicional (2026-05-12, padrão SimplePractice): NUNCA propaga
    // para eventos que já têm financial_record emitido (status pending/paid/
    // overdue). Cobranças emitidas são imutáveis — ajustes só via fluxo do
    // Financeiro. Sem isso, mudar o template da regra mudaria silenciosamente
    // o valor referenciado por uma cobrança já entregue ao paciente.
    //
    // opts.fromDate: string ISO 'YYYY-MM-DD' — limita a ocorrências a partir
    //   dessa data inclusive (escopo 'este_e_seguintes'). null = todas da série.
    // opts.ignoreCustomized: bypass do filtro services_customized=false
    //   (escopo 'todos_sem_excecao' operacional — NÃO afeta filtro financeiro).
    async function propagateToSerie(ruleId, items, { fromDate = null, ignoreCustomized = false } = {}) {
        if (!ruleId) return;

        // Busca IDs das ocorrências materializadas elegíveis
        let q = tenantDb().from('agenda_eventos').select('id').eq('recurrence_id', ruleId);

        if (!ignoreCustomized) {
            q = q.eq('services_customized', false);
        }

        if (fromDate) {
            q = q.gte('inicio_em', fromDate);
        }

        const { data: events, error: queryError } = await q;
        if (queryError) throw queryError;
        if (!events?.length) return;

        // Filtra OUT eventos que já têm financial_record emitido. Uma query
        // em batch evita N round-trips. Status considerados imutáveis: pending,
        // paid, overdue. cancelled é ok propagar (record foi descartado).
        const eventIds = events.map((e) => e.id);
        const { data: lockedEvents, error: frErr } = await tenantDb().from('financial_records')
            .select('agenda_evento_id')
            .in('agenda_evento_id', eventIds)
            .in('status', ['pending', 'paid', 'overdue']);
        if (frErr) throw frErr;
        const lockedSet = new Set((lockedEvents || []).map((r) => r.agenda_evento_id));
        const eligibleEvents = events.filter((ev) => !lockedSet.has(ev.id));

        if (!eligibleEvents.length) return;

        // Para cada evento elegível: delete + insert (padrão idempotente)
        for (const ev of eligibleEvents) {
            const { error: delErr } = await tenantDb().from('commitment_services').delete().eq('commitment_id', ev.id);
            if (delErr) throw delErr;

            if (items?.length) {
                const rows = items.map((item) => ({
                    commitment_id: ev.id,
                    service_id: item.service_id,
                    quantity: item.quantity,
                    unit_price: item.unit_price,
                    discount_pct: item.discount_pct ?? 0,
                    discount_flat: item.discount_flat ?? 0,
                    final_price: item.final_price
                }));
                const { error: insErr } = await tenantDb().from('commitment_services').insert(rows);
                if (insErr) throw insErr;
            }
        }
    }

    return {
        loadItems,
        saveItems,
        loadRuleItems,
        saveRuleItems,
        loadItemsOrTemplate,
        propagateToSerie
    };
}
