/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Arquivo: src/features/financeiro/services/billingContractsRepository.js
|
| Repository de billing_contracts — pacotes/contratos de cobrança (upfront,
| pagamento por sessão, etc).
|
| Gotcha conhecido (memória project_billing_contracts_no_updated_at):
|   billing_contracts NÃO tem coluna updated_at. UPDATE com updated_at falha
|   silently em Promise.allSettled. Repository NÃO inclui updated_at em patches.
|--------------------------------------------------------------------------
*/
import { supabase } from '@/lib/supabase/client';
import { tenantDb } from '@/lib/supabase/tenantClient';
import { useTenantStore } from '@/stores/tenantStore';
import { assertTenantId, getUid } from './_tenantGuards';
import { BILLING_CONTRACT_SELECT } from './financialSelects';

function resolveTenantId(tenantIdArg) {
    const tenantStore = useTenantStore();
    const tenantId = tenantIdArg || tenantStore.activeTenantId || tenantStore.tenantId;
    assertTenantId(tenantId);
    return tenantId;
}

/**
 * Lista contratos ativos do paciente.
 */
export async function listForPatient(patientId, { tenantId, includeDeleted = false } = {}) {
    if (!patientId) return [];
    const tid = resolveTenantId(tenantId);

    let q = tenantDb().from('billing_contracts').select(BILLING_CONTRACT_SELECT).eq('patient_id', patientId).order('created_at', { ascending: false });

    if (!includeDeleted) q = q.is('deleted_at', null);

    const { data, error } = await q;
    if (error) throw error;
    return data || [];
}

/**
 * Lê contrato por id. Refresh FRESH do banco — usado pelo orchestrator antes
 * de UPDATE pra evitar race condition (memória project_agenda_reverse_transitions).
 */
export async function getById(contractId, { tenantId } = {}) {
    if (!contractId) throw new Error('contractId obrigatório.');
    const tid = resolveTenantId(tenantId);

    const { data, error } = await tenantDb().from('billing_contracts').select(BILLING_CONTRACT_SELECT).eq('id', contractId).maybeSingle();

    if (error) throw error;
    return data || null;
}

/**
 * Cria contrato.
 */
export async function create(payload) {
    if (!payload?.patient_id) throw new Error('patient_id obrigatório.');
    if (!payload?.charging_style) throw new Error('charging_style obrigatório.');

    const uid = await getUid();
    const tid = resolveTenantId(payload.tenantId);

    const row = {
        owner_id: uid,
        patient_id: payload.patient_id,
        charging_style: payload.charging_style,
        sessions_total: payload.sessions_total ?? null,
        sessions_used: 0,
        total_amount: payload.total_amount != null ? Number(payload.total_amount) : null,
        status: payload.status || 'active',
        start_date: payload.start_date || null,
        end_date: payload.end_date || null
    };

    const { data, error } = await tenantDb().from('billing_contracts').insert([row]).select(BILLING_CONTRACT_SELECT).single();

    if (error) throw error;
    return data;
}

/**
 * Update — NÃO INCLUI updated_at (tabela não tem essa coluna — gotcha conhecido).
 */
export async function update(contractId, patch, { tenantId } = {}) {
    if (!contractId) throw new Error('contractId obrigatório.');
    const tid = resolveTenantId(tenantId);

    // eslint-disable-next-line no-unused-vars
    const { updated_at: _dropped, ...safePatch } = patch || {};

    const { data, error } = await tenantDb().from('billing_contracts').update(safePatch).eq('id', contractId).select(BILLING_CONTRACT_SELECT).single();

    if (error) throw error;
    return data;
}

/**
 * Incrementa sessions_used em 1 (consume).
 * Lê FRESH antes do UPDATE pra evitar race.
 */
export async function incrementSessionsUsed(contractId, { tenantId } = {}) {
    const current = await getById(contractId, { tenantId });
    if (!current) throw new Error('Contrato não encontrado.');
    const newCount = (Number(current.sessions_used) || 0) + 1;
    return update(contractId, { sessions_used: newCount }, { tenantId });
}

/**
 * Decrementa sessions_used (reverse). Lê FRESH antes.
 */
export async function decrementSessionsUsed(contractId, { tenantId } = {}) {
    const current = await getById(contractId, { tenantId });
    if (!current) throw new Error('Contrato não encontrado.');
    const newCount = Math.max(0, (Number(current.sessions_used) || 0) - 1);
    return update(contractId, { sessions_used: newCount }, { tenantId });
}

/**
 * Busca records cross-week por recurrence_id (memória project_cross_week_propagation).
 * Útil pra bulk-load de pacote upfront.
 */
export async function findRecordsByRecurrence(recurrenceId, { tenantId } = {}) {
    if (!recurrenceId) return [];
    const tid = resolveTenantId(tenantId);

    const { data, error } = await tenantDb().from('financial_records').select('id, status, agenda_evento_id, billing_contract_id').is('deleted_at', null).not('agenda_evento_id', 'is', null);
    // NOTE: filter por recurrence_id requer join — fica como TODO no orchestrator
    // (memória project_cross_week_propagation: query records cross-week por recurrence_id).

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