/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Arquivo: src/features/financeiro/services/financialRecordsRepository.js
|
| Repository de financial_records. Extraído de src/composables/useFinancialRecords.js.
| Pattern canônico — ver blueprints/repository-blueprint.md.
|
| Cobre: list (com filtros), getById, createForSession (RPC), createManual,
|        markAsPaid (RPC), markAsUnpaid, cancel, update.
|
| RPC `create_financial_record_for_session` (existe no banco) é o caminho
| ÚNICO de criação a partir de sessão — idempotente, ignora cancelled
| (memória project_rpc_idempotency_cancelled).
|--------------------------------------------------------------------------
*/
import { supabase } from '@/lib/supabase/client';
import { tenantDb } from '@/lib/supabase/tenantClient';
import { useTenantStore } from '@/stores/tenantStore';
import { assertTenantId, getUid } from './_tenantGuards';
import { FINANCIAL_RECORD_SELECT, flattenFinancialRecord } from './financialSelects';

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

/**
 * Lista lançamentos com filtros + paginação.
 *
 * @param {Object} [filters]
 * @param {string} [filters.tenantId]
 * @param {string} [filters.status]    - 'pending'|'paid'|'overdue'|'partial'|'cancelled'|'refunded'
 * @param {string} [filters.type]      - 'receita'|'despesa'
 * @param {string} [filters.patient_id]
 * @param {string} [filters.due_date_from]
 * @param {string} [filters.due_date_to]
 * @param {number} [filters.limit=50]
 * @param {number} [filters.offset=0]
 * @returns {Promise<{rows: Array, total: number}>}
 */
export async function list(filters = {}) {
    const tid = resolveTenantId(filters.tenantId);
    const limit = filters.limit ?? 50;
    const offset = filters.offset ?? 0;

    let q = tenantDb().from('financial_records')
        .select(FINANCIAL_RECORD_SELECT, { count: 'exact' })
        
        .is('deleted_at', null)
        .order('due_date', { ascending: false })
        .range(offset, offset + limit - 1);

    if (filters.status) q = q.eq('status', filters.status);
    if (filters.type) q = q.eq('type', filters.type);
    if (filters.patient_id) q = q.eq('patient_id', filters.patient_id);
    if (filters.due_date_from) q = q.gte('due_date', filters.due_date_from);
    if (filters.due_date_to) q = q.lte('due_date', filters.due_date_to);

    const { data, error, count } = await q;
    if (error) throw error;
    return {
        rows: (data || []).map(flattenFinancialRecord),
        total: count ?? 0
    };
}

/**
 * Lê um record por id.
 */
export async function getById(recordId, { tenantId } = {}) {
    if (!recordId) throw new Error('recordId obrigatório.');
    const tid = resolveTenantId(tenantId);

    const { data, error } = await tenantDb().from('financial_records').select(FINANCIAL_RECORD_SELECT).eq('id', recordId).maybeSingle();

    if (error) throw error;
    return data ? flattenFinancialRecord(data) : null;
}

/**
 * Busca records ativos vinculados a um evento da agenda (status pending|overdue|paid).
 * Usado pelo orchestrator pra checar idempotência antes de criar.
 */
export async function listByEvent(eventId, { tenantId } = {}) {
    if (!eventId) return [];
    const tid = resolveTenantId(tenantId);

    const { data, error } = await tenantDb().from('financial_records').select(FINANCIAL_RECORD_SELECT).eq('agenda_evento_id', eventId).is('deleted_at', null);

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

/**
 * Cria cobrança a partir de sessão via RPC idempotente.
 * RPC `create_financial_record_for_session` ignora cancelled/refunded — pode
 * chamar 2× sem regerar (memória project_rpc_idempotency_cancelled).
 */
export async function createForSession(payload) {
    if (!payload) throw new Error('Payload vazio.');
    if (!payload.patient_id) throw new Error('patient_id obrigatório.');
    if (!payload.agenda_evento_id) throw new Error('agenda_evento_id obrigatório.');
    if (payload.amount == null) throw new Error('amount obrigatório.');
    if (!payload.due_date) throw new Error('due_date obrigatório.');

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

    const { data, error } = await supabase.rpc('create_financial_record_for_session', {
        p_tenant_id: tid,
        p_owner_id: uid,
        p_patient_id: payload.patient_id,
        p_agenda_evento_id: payload.agenda_evento_id,
        p_amount: Number(payload.amount),
        p_due_date: payload.due_date
    });

    if (error) throw error;
    return data;
}

/**
 * Cria lançamento manual avulso (sem sessão). INSERT direto, não via RPC.
 */
export async function createManual(payload) {
    if (!payload) throw new Error('Payload vazio.');
    if (payload.amount == null || Number.isNaN(Number(payload.amount))) {
        throw new Error('Valor inválido.');
    }
    if (!payload.due_date) throw new Error('due_date obrigatório.');

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

    const discount = Number(payload.discount_amount ?? 0);
    const amount = Number(payload.amount);

    const row = {
        owner_id: uid,
        patient_id: payload.patient_id ?? null,
        agenda_evento_id: null,
        type: payload.type || 'receita',
        amount,
        discount_amount: discount,
        final_amount: amount - discount,
        status: payload.status || 'pending',
        due_date: payload.due_date,
        payment_method: payload.payment_method || null,
        description: payload.description ? String(payload.description).trim() || null : null,
        notes: payload.notes ? String(payload.notes).trim() || null : null
    };

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

    if (error) throw error;
    return flattenFinancialRecord(data);
}

/**
 * Marca record como pago via RPC (server-side timestamps + audit).
 */
export async function markAsPaid(recordId, paymentMethod, { tenantId } = {}) {
    if (!recordId) throw new Error('recordId obrigatório.');
    const tid = resolveTenantId(tenantId);

    // RPC retorna jsonb (objeto único) — `data` é o financial_record, não array.
    const { data, error } = await supabase.rpc('mark_as_paid', {
        p_tenant_id: tid,
        p_financial_record_id: recordId,
        p_payment_method: paymentMethod
    });

    if (error) throw error;
    return data;
}

/**
 * Reverte status pago → pending (UPDATE direto). Mantém payment_method/paid_at
 * limpos pra reconciliação manual.
 */
export async function markAsUnpaid(recordId, { tenantId } = {}) {
    if (!recordId) throw new Error('recordId obrigatório.');
    const tid = resolveTenantId(tenantId);

    const { error } = await tenantDb().from('financial_records')
        .update({
            status: 'pending',
            paid_at: null,
            payment_method: null,
            updated_at: new Date().toISOString()
        })
        .eq('id', recordId)
        ;

    if (error) throw error;
}

/**
 * Cancela record (soft — status='cancelled').
 */
export async function cancel(recordId, { tenantId, reason } = {}) {
    if (!recordId) throw new Error('recordId obrigatório.');
    const tid = resolveTenantId(tenantId);

    const patch = { status: 'cancelled', updated_at: new Date().toISOString() };
    if (reason) patch.notes = String(reason).trim() || null;

    const { error } = await tenantDb().from('financial_records').update(patch).eq('id', recordId);

    if (error) throw error;
}

/**
 * Atualiza campos arbitrários (use com cautela — não atualiza status/paid_at via aqui).
 */
export async function update(recordId, patch, { tenantId } = {}) {
    if (!recordId) throw new Error('recordId obrigatório.');
    if (!patch) throw new Error('Patch vazio.');
    const tid = resolveTenantId(tenantId);

    const safePatch = { ...patch, updated_at: new Date().toISOString() };

    const { data, error } = await tenantDb().from('financial_records').update(safePatch).eq('id', recordId).select(FINANCIAL_RECORD_SELECT).single();

    if (error) throw error;
    return flattenFinancialRecord(data);
}
