/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Arquivo: src/features/agenda/composables/useAgendaEventActions.js
| Data: 2026-05-04
|
| Watchers + handlers de save/delete extraídos do AgendaEventDialog.vue
| (A66 sub-sessão 1C-i). Contém SIDE-EFFECTS (supabase, confirm, emit, toast)
| — diferente do composer (1B) que é só state + computeds derivados.
|
| Escopo da 1C-i:
|   - Watcher do form.status (confirm dialog cancelado/remarcado + supabase update)
|   - Watcher do billingType (limpa campos por tipo)
|   - Watcher [paciente_id, dia] (detecta conflito do mesmo paciente no dia)
|   - onSave (monta payload + emit)
|   - onDelete (avulsa OU série com confirm)
|   - onEncerrarSerie (confirm de encerramento série inteira)
|
| Não inclui (vai pra 1C-ii):
|   - Watcher do props.modelValue (init form ao abrir — depende de loadPatients,
|     ensureServicesLoaded, loadInsurancePlans, _loadCommitmentItemsForEvent)
|   - Patient picker handlers (loadPatients, selectPaciente, ...)
|   - Billing/items handlers (addItem, removeItem, ...)
|   - Series pills handlers
|   - Slot selection
|
| Recebe via argumento:
|   composer        — resultado de useAgendaEventComposer (form, canSave, etc)
|   commitmentItems — ref<Item[]> dos serviços/billing
|   servicePickerSel — ref do select picker
|   selectedPlanService — ref do procedure de convênio
|   saveCommitmentItems — function de useCommitmentServices (callback do save)
|   props, emit     — do componente parent
|--------------------------------------------------------------------------
*/
import { ref, watch } from 'vue';
import { useToast } from 'primevue/usetoast';
import { useConfirm } from 'primevue/useconfirm';
import { supabase } from '@/lib/supabase/client';
import { tenantDb } from '@/lib/supabase/tenantClient';
import { labelStatusSessao } from './agendaEventHelpers';

const EVENTO_TIPO_SESSAO = 'sessao';

export function useAgendaEventActions({
    composer,
    commitmentItems,
    servicePickerSel,
    selectedPlanService,
    saveCommitmentItems,
    // chargeMode (Opção C1, 2026-05-13): ref string com modo de cobrança.
    // Valores: 'none' | 'session' (avulsa) | 'package' | 'per_session' (recorrente).
    // O emit('save') leva chargeMode no payload; handler em useMelissaAgenda
    // decide o que criar (financial_record | billing_contract | N events+records).
    // Substituiu o boolean gerarCobrancaAoSalvar.
    chargeMode,
    // packageStyle (2026-05-14): só relevante em chargeMode='package'.
    // Valores: 'upfront' (cria 1 financial_record total + materializa 1ª ocorrência)
    // | 'saldo' (só billing_contract, sem financial_record imediato — Cliniko).
    packageStyle,
    // paymentMethod (refatorado 2026-05-16): forma de recebimento quando
    // avulsa+session OU pacote+upfront. Valores: 'link' (Asaas, status pending)
    // | 'pix' | 'dinheiro' | 'deposito' | 'cartao_maquininha'. Status do
    // record é controlado pelo markPaidNow abaixo, não pela forma.
    paymentMethod,
    // markPaidNow (refatorado 2026-05-16): boolean. Quando true E método !== 'link',
    // handler marca o financial_record como paid (paciente pagou na hora).
    // Quando false, record nasce pending independente do método.
    markPaidNow,
    props,
    emit
}) {
    const toast = useToast();
    const confirm = useConfirm();

    // Refs internos compartilhados com o .vue (que ainda tem watchers
    // próprios em 1C-ii). Expostos no return pra leitura/escrita externa.
    const _skipStatusWatch = ref(false);
    const _prevStatus = ref(null);
    const _restoringConvenio = ref(false);
    const samePatientConflict = ref(null);

    // ────────────────────────────────────────────────────────────────────
    // 1. Watcher do form.status
    //    Fase 5 (2026-05-14): pra realizado/faltou/cancelado, emit
    //    `updateSeriesEvent` pro parent abrir o AgendaStatusChangeConfirmDialog
    //    (com regras de exceção, saldo de pacote, etc). Sem confirm.require
    //    aqui — o dialog do parent é a fonte canônica.
    //    Pra remarcado mantém path antigo (confirm.require simples).
    //    Se user cancelar o dialog: parent chama onReject pra reverter o form.
    // ────────────────────────────────────────────────────────────────────
    watch(
        () => composer.form.value?.status,
        async (newVal, oldVal) => {
            if (_skipStatusWatch.value) return;
            if (!composer.isEdit.value || !composer.form.value?.id) return;

            const isStatusComDialog = ['realizado', 'faltou', 'cancelado'].includes(newVal);
            const isRemarcado = newVal === 'remarcado';
            if (!isStatusComDialog && !isRemarcado) return;

            _prevStatus.value = oldVal;

            // Fase 5: emit pro parent abrir AgendaStatusChangeConfirmDialog.
            // Parent decide o que fazer e chama onReject() se user cancelar.
            if (isStatusComDialog) {
                const formId = composer.form.value.id;
                const isVirtual = !!composer.form.value.is_occurrence || (typeof formId === 'string' && formId.startsWith('rec::'));
                emit('updateSeriesEvent', {
                    id: isVirtual ? null : formId,
                    status: newVal,
                    recurrence_date:
                        composer.form.value.recurrence_date ||
                        composer.form.value.original_date ||
                        String(composer.form.value.inicio_em || '').slice(0, 10),
                    inicio_em: composer.form.value.inicio_em,
                    fim_em: composer.form.value.fim_em,
                    is_virtual: isVirtual,
                    // Form completo — handler usa pra resolver recurrence_id, billing_contract_id, etc
                    row: { ...composer.form.value },
                    // Callback pra reverter status no form se user cancelar o dialog do parent.
                    // _skipStatusWatch evita loop recursivo no watcher.
                    onReject: () => {
                        _skipStatusWatch.value = true;
                        composer.form.value.status = _prevStatus.value;
                        Promise.resolve().then(() => {
                            _skipStatusWatch.value = false;
                        });
                    }
                });
                return;
            }

            // Path legacy pra 'remarcado': confirm.require simples + UPDATE direto.
            confirm.require({
                header: 'Remarcar sessão',
                message: 'Tem certeza que deseja marcar esta sessão para remarcação? O status será salvo imediatamente.',
                icon: 'pi pi-refresh',
                acceptLabel: 'Sim, confirmar',
                rejectLabel: 'Não',
                acceptSeverity: 'warn',
                accept: async () => {
                    try {
                        const formId = composer.form.value.id;
                        const isVirtual = !!composer.form.value.is_occurrence || (typeof formId === 'string' && formId.startsWith('rec::'));

                        if (isVirtual) {
                            emit('updateSeriesEvent', {
                                id: null,
                                status: newVal,
                                recurrence_date: composer.form.value.recurrence_date || composer.form.value.original_date || String(composer.form.value.inicio_em || '').slice(0, 10),
                                inicio_em: composer.form.value.inicio_em,
                                fim_em: composer.form.value.fim_em,
                                is_virtual: true,
                                row: { ...composer.form.value }
                            });
                            toast.add({ severity: 'success', summary: 'Status atualizado', detail: `Sessão marcada como ${labelStatusSessao(newVal)}.`, life: 3000 });
                            return;
                        }
                        const { data, error } = await tenantDb().from('agenda_eventos').update({ status: newVal }).eq('id', formId).select().single();
                        if (error) throw error;
                        toast.add({ severity: 'success', summary: 'Status atualizado', detail: `Sessão marcada como ${labelStatusSessao(newVal)}.`, life: 3000 });
                        emit('updated', data);
                    } catch (e) {
                        toast.add({ severity: 'error', summary: 'Erro', detail: e?.message || 'Não foi possível atualizar o status.', life: 4000 });
                        composer.form.value.status = _prevStatus.value;
                    }
                },
                reject: () => {
                    composer.form.value.status = _prevStatus.value;
                }
            });
        }
    );

    // ────────────────────────────────────────────────────────────────────
    // 2. Watcher do billingType — quando troca tipo (gratuito/particular/
    //    convenio), limpa campos dos outros tipos pra não vazar valores.
    // ────────────────────────────────────────────────────────────────────
    watch(composer.billingType, (val) => {
        if (val === 'gratuito') {
            commitmentItems.value = [];
            composer.form.value.price = 0;
            composer.form.value.insurance_plan_id = null;
            composer.form.value.insurance_guide_number = null;
            composer.form.value.insurance_value = null;
            if (selectedPlanService) selectedPlanService.value = null;
        }
        if (val === 'particular') {
            composer.form.value.insurance_plan_id = null;
            composer.form.value.insurance_guide_number = null;
            composer.form.value.insurance_value = null;
            if (selectedPlanService) selectedPlanService.value = null;
        }
        if (val === 'convenio') {
            commitmentItems.value = [];
            if (servicePickerSel) servicePickerSel.value = null;
        }
    });

    // ────────────────────────────────────────────────────────────────────
    // 3. Watcher [paciente_id, dia] — detecta se o paciente já tem outra
    //    sessão no mesmo dia. Não bloqueia o save (só informa via UI).
    // ────────────────────────────────────────────────────────────────────
    watch(
        () => [composer.form.value.paciente_id, composer.form.value.dia?.toString()],
        async () => {
            const pid = composer.form.value.paciente_id;
            samePatientConflict.value = null;
            if (!pid || !composer.isSessionEvent.value || !composer.visible.value) return;

            const d = composer.form.value.dia ? new Date(composer.form.value.dia) : new Date();
            const dayStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).toISOString();
            const dayEnd = new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1).toISOString();

            let q = tenantDb().from('agenda_eventos')
                .select('id, inicio_em, fim_em, titulo')
                .eq('patient_id', pid)
                .gte('inicio_em', dayStart)
                .lt('inicio_em', dayEnd)
                .limit(1);

            if (composer.form.value.id) q = q.neq('id', composer.form.value.id);
            const { data } = await q.maybeSingle();
            samePatientConflict.value = data || null;
        }
    );

    // ────────────────────────────────────────────────────────────────────
    // Helpers internos (puros) pra montar payload — extraídos pra serem
    // testáveis e reutilizáveis. Não dependem de refs reativos diretos,
    // recebem o form como argumento.
    // ────────────────────────────────────────────────────────────────────
    function buildSavePayload({ form, requiresPatient, isSessionEvent, computedTitulo, inicioISO, fimISO }) {
        return {
            owner_id: form.owner_id,
            terapeuta_id: form.terapeuta_id,
            paciente_id: requiresPatient ? form.paciente_id : null,
            patient_id: requiresPatient ? form.paciente_id : null,
            tipo: EVENTO_TIPO_SESSAO,
            status: form.status || 'agendado',
            titulo: computedTitulo || null,
            modalidade: form.modalidade || null,
            observacoes: form.observacoes || null,
            inicio_em: inicioISO,
            fim_em: fimISO,
            determined_commitment_id: form.commitment_id || null,
            titulo_custom: form.titulo_custom || null,
            extra_fields: Object.keys(form.extra_fields || {}).length ? form.extra_fields : null,
            price: isSessionEvent ? (form.price ?? null) : null,
            insurance_plan_id: isSessionEvent ? (form.insurance_plan_id ?? null) : null,
            insurance_guide_number: isSessionEvent ? (form.insurance_guide_number ?? null) : null,
            insurance_value: isSessionEvent ? (form.insurance_value ?? null) : null,
            insurance_plan_service_id: isSessionEvent ? (form.insurance_plan_service_id ?? null) : null
        };
    }

    function buildRecorrenciaPayload({
        recorrenciaType,
        diaSemanaRecorrencia,
        diasSelecionados,
        startTime,
        duracaoMin,
        dataFimCalculada,
        qtdSessoesEfetiva,
        serieValorMode,
        commitmentItemsList,
        ocorrenciasComConflito
    }) {
        if (recorrenciaType === 'avulsa') return null;
        return {
            tipo: 'recorrente',
            tipoFreq: recorrenciaType,
            diaSemana: diaSemanaRecorrencia,
            diasSemana: diasSelecionados,
            horaInicio: startTime ? `${startTime}:00` : null,
            duracaoMin,
            dataFim: dataFimCalculada ? dataFimCalculada.toISOString() : null,
            qtdSessoes: qtdSessoesEfetiva,
            serieValorMode,
            commitmentItems: commitmentItemsList.slice(),
            conflitos: ocorrenciasComConflito
                .filter((o) => o.conflict)
                .map((o) => ({ date: o.date.toISOString().slice(0, 10), conflict: o.conflict }))
        };
    }

    // ────────────────────────────────────────────────────────────────────
    // 4. onSave — valida (canSave + timeConflict), monta payload e emite.
    // ────────────────────────────────────────────────────────────────────
    function onSave() {
        if (!composer.canSave.value) return;

        if (composer.timeConflict.value) {
            toast.add({
                severity: 'warn',
                summary: 'Conflito de horário',
                detail: `${composer.timeConflict.value}. Ajuste o horário ou a duração.`,
                life: 4500
            });
            return;
        }

        const inicioISO = composer.inicioDateTime.value?.toISOString() || null;
        const fimISO = composer.fimDateTime.value?.toISOString() || null;

        const payload = buildSavePayload({
            form: composer.form.value,
            requiresPatient: composer.requiresPatient.value,
            isSessionEvent: composer.isSessionEvent.value,
            computedTitulo: composer.computedTitulo.value,
            inicioISO,
            fimISO
        });

        // serieValorMode e similars não estão no composer (1B); são lidos
        // do .vue via props.eventActionsExtras se passados, ou null como
        // default. 1C-i: assumimos null se não fornecido pra simplificar.
        const recorrencia = composer.isSessionEvent.value
            ? buildRecorrenciaPayload({
                  recorrenciaType: composer.recorrenciaType.value,
                  diaSemanaRecorrencia: composer.diaSemanaRecorrencia.value,
                  diasSelecionados: composer.diasSelecionados.value,
                  startTime: composer.form.value.startTime,
                  duracaoMin: composer.form.value.duracaoMin,
                  dataFimCalculada: composer.dataFimCalculada.value,
                  qtdSessoesEfetiva: composer.qtdSessoesEfetiva.value,
                  serieValorMode: 'multiplicar', // default; .vue pode passar outro via _serieValorMode
                  commitmentItemsList: commitmentItems.value,
                  ocorrenciasComConflito: composer.ocorrenciasComConflito.value
              })
            : null;

        // Escopo de edição — só quando edita série existente
        const emitEditMode = composer.hasSerie.value ? composer.editScope.value : null;
        const emitRecurrenceId = composer.hasSerie.value
            ? props.eventRow?.recurrence_id ?? props.eventRow?.serie_id ?? null
            : null;
        const emitOriginalDate = composer.hasSerie.value ? props.eventRow?.original_date ?? null : null;

        emit('save', {
            id: composer.form.value.id,
            payload,
            recorrencia,
            editMode: emitEditMode,
            recurrence_id: emitRecurrenceId,
            original_date: emitOriginalDate,
            // _occurrenceMode: flag pra distinguir save do 2o dialog empilhado
            // (editar UMA ocorrencia) do save do dialog pai. Handler decide qual
            // dialog fechar — sem isso, fechava sempre o pai. 2026-05-12.
            _occurrenceMode: !!props.occurrenceMode,
            // chargeMode (Opção C1, 2026-05-13): handler decide entre criar
            // financial_record (avulsa+session), billing_contract (recorrente+package)
            // ou materializar N ocorrências + N records (recorrente+per_session).
            // UI no .vue garante valores válidos por modo.
            chargeMode: chargeMode?.value ?? 'none',
            // packageStyle (2026-05-14): handler em useMelissaAgenda usa pra
            // decidir entre upfront (1 record total + materializa 1ª) ou
            // saldo (só contrato).
            packageStyle: packageStyle?.value ?? 'upfront',
            // paymentMethod + markPaidNow (refatorado 2026-05-16): substituem
            // o antigo paymentSettlement. Handler aplica payment_method (sempre)
            // e status=paid+paid_at apenas quando markPaidNow=true && method!='link'.
            paymentMethod: paymentMethod?.value ?? 'link',
            markPaidNow: markPaidNow?.value === true,
            // legado — mantido para compatibilidade
            serie_id: props.eventRow?.serie_id ?? null,
            serviceItems: composer.isSessionEvent.value ? commitmentItems.value.slice() : null,
            onSaved: composer.isSessionEvent.value
                ? async (eventId, { markCustomized = false } = {}) => {
                      await saveCommitmentItems(eventId, commitmentItems.value, { markCustomized });
                  }
                : null
        });
    }

    // ────────────────────────────────────────────────────────────────────
    // 5. onDelete — avulsa: confirm simples + emit(id).
    //    Série: confirm com escopo (somente_este/seguintes/todos) + emit({id, editMode}).
    // ────────────────────────────────────────────────────────────────────
    function onDelete() {
        if (!composer.form.value.id) return;

        if (composer.hasSerie.value) {
            const isTodos = composer.editScope.value === 'todos';
            confirm.require({
                header: isTodos ? 'Encerrar toda a série' : 'Excluir sessão recorrente',
                message: isTodos
                    ? 'Todos os agendamentos futuros da série serão removidos permanentemente. Esta sessão será mantida como avulsa. Esta ação é irreversível.'
                    : 'Esta sessão faz parte de uma série. O que deseja remover?',
                icon: isTodos ? 'pi pi-trash' : 'pi pi-exclamation-triangle',
                acceptClass: 'p-button-danger',
                acceptLabel: isTodos
                    ? 'Sim, encerrar série'
                    : composer.editScopeOptions.value.find((o) => o.value === composer.editScope.value)?.label || 'Excluir',
                rejectLabel: 'Cancelar',
                accept: () =>
                    emit('delete', {
                        id: composer.form.value.id,
                        editMode: composer.editScope.value,
                        recurrence_id: props.eventRow?.recurrence_id ?? props.eventRow?.serie_id ?? null,
                        original_date: props.eventRow?.original_date ?? null,
                        serie_id: props.eventRow?.serie_id ?? null
                    })
            });
            return;
        }

        confirm.require({
            header: 'Excluir compromisso',
            message: 'Tem certeza? Essa ação não pode ser desfeita.',
            icon: 'pi pi-exclamation-triangle',
            acceptClass: 'p-button-danger',
            acceptLabel: 'Excluir',
            rejectLabel: 'Cancelar',
            accept: () => emit('delete', composer.form.value.id)
        });
    }

    // ────────────────────────────────────────────────────────────────────
    // 6. onEncerrarSerie — confirm explícito de encerramento total da série.
    //    Diferente do onDelete em 'todos' porque pode ser chamado direto
    //    de um botão dedicado, sem depender de editScope.
    // ────────────────────────────────────────────────────────────────────
    function onEncerrarSerie() {
        confirm.require({
            header: 'Encerrar toda a série',
            message:
                'Todos os agendamentos da série serão removidos permanentemente, incluindo exceções e recorrências. Esta sessão será mantida como avulsa. Esta ação é irreversível.',
            icon: 'pi pi-trash',
            acceptClass: 'p-button-danger',
            acceptLabel: 'Sim, encerrar série',
            rejectLabel: 'Cancelar',
            accept: () =>
                emit('delete', {
                    id: composer.form.value.id,
                    editMode: 'todos',
                    recurrence_id: props.eventRow?.recurrence_id ?? props.eventRow?.serie_id ?? null,
                    original_date: props.eventRow?.original_date ?? null,
                    serie_id: props.eventRow?.serie_id ?? null
                })
        });
    }

    return {
        // refs internos (expostos pra .vue ler/escrever em watchers próprios)
        _skipStatusWatch,
        _prevStatus,
        _restoringConvenio,
        samePatientConflict,
        // helpers de payload (públicos pra teste isolado)
        buildSavePayload,
        buildRecorrenciaPayload,
        // handlers
        onSave,
        onDelete,
        onEncerrarSerie
    };
}
