<?php

namespace App\Services;

use App\Enums\IncomeCategory;
use App\Models\Expense;
use App\Models\Income;
use InvalidArgumentException;

class FinanceAutoLogger
{
    /*==============================================================
    | REGISTRAR INGRESOS
    ===============================================================*/
    public function logIncome(array $data): void
    {
        if (! isset($data['category'])) {
            throw new InvalidArgumentException('Income category is required.');
        }

        $categoryValue = $data['category'];

        // Convertir a Enum para validación
        $category = $categoryValue instanceof IncomeCategory
            ? $categoryValue
            : IncomeCategory::tryFrom($categoryValue);

        if (! $category) {
            throw new InvalidArgumentException(
                "Categoría de ingreso inválida: '{$categoryValue}'. ".
                'Categorías válidas: '.implode(', ', IncomeCategory::values())
            );
        }

        // Validar integridad según tipo de categoría
        $this->validateIncomeIntegrity($category, $data);

        Income::updateOrCreate(
            [
                'company_id' => $data['company_id'],
                'payment_id' => $data['payment_id'] ?? null,
                'category' => $category->value,
            ],
            [
                'user_id' => $data['user_id'],
                'collector_user_id' => $data['collector_user_id'] ?? null,
                'amount' => $data['amount'],
                'credit_id' => $data['credit_id'] ?? null,
                'method' => $data['method'] ?? null,
                'operation_date' => $data['operation_date'],
                'reference' => $data['reference'] ?? null,
                'notes' => $data['notes'] ?? null,
                'metadata' => $data['metadata'] ?? [],
            ]
        );
    }

    /*==============================================================
    | VALIDACIÓN DE INTEGRIDAD DE INGRESOS
    ===============================================================*/
    protected function validateIncomeIntegrity(IncomeCategory $category, array $data): void
    {
        // Categorías vinculadas a pagos requieren payment_id y credit_id
        if ($category->isPaymentLinked()) {
            if (empty($data['payment_id'])) {
                throw new InvalidArgumentException(
                    "La categoría '{$category->getLabel()}' requiere payment_id."
                );
            }

            if (empty($data['credit_id'])) {
                throw new InvalidArgumentException(
                    "La categoría '{$category->getLabel()}' requiere credit_id para trazabilidad."
                );
            }
        }

        // Categorías operativas NO pueden tener payment_id ni credit_id
        if ($category->isOperational()) {
            if (! empty($data['credit_id'])) {
                throw new InvalidArgumentException(
                    "La categoría operativa '{$category->getLabel()}' no puede tener credit_id."
                );
            }

            if (! empty($data['payment_id'])) {
                throw new InvalidArgumentException(
                    "La categoría operativa '{$category->getLabel()}' no puede tener payment_id."
                );
            }
        }
    }

    /*==============================================================
    | REGISTRAR EGRESOS
    ===============================================================*/
    public function logExpense(array $data): Expense
    {
        // Determinar expense_type basado en la categoría
        $expenseType = $this->resolveExpenseType($data['category']);

        return Expense::create([
            'company_id' => $data['company_id'],
            'user_id' => $data['user_id'],
            'collector_user_id' => $data['collector_user_id'] ?? null,
            'amount' => $data['amount'],
            'category' => $data['category'],
            'expense_type' => $expenseType,
            'affects_cash' => $data['affects_cash'] ?? true,
            'credit_id' => $data['credit_id'] ?? null,
            'financial_operation_id' => $data['financial_operation_id'] ?? null,
            'method' => $data['method'] ?? null,
            'operation_date' => $data['operation_date'],
            'reference' => $data['reference'] ?? null,
            'notes' => $data['notes'] ?? null,
            'metadata' => $data['metadata'] ?? [],
            // Lo envían el endpoint de sync batch y el endpoint directo de la PWA
            // (PWA-002), para deduplicar reintentos. Null si el cliente no manda clave.
            'idempotency_key' => $data['idempotency_key'] ?? null,
        ]);
    }

    /**
     * Resuelve el expense_type basado en la categoría.
     */
    protected function resolveExpenseType(string $category): string
    {
        return match ($category) {
            'disbursement' => 'disbursement',
            'financial_adjustment', 'refinance_adjustment', 'extension_adjustment' => 'financial_adjustment',
            default => 'operational',
        };
    }

    /*==============================================================
    | EGRESOS DE CRÉDITO
    |
    | IMPORTANTE: Estas operaciones usan query builder para bypass
    | de los eventos del modelo Expense, que bloquean modificaciones
    | a egresos de desembolso. Esto es INTENCIONAL para operaciones
    | controladas por el sistema, mientras que la UI sigue bloqueada.
    ===============================================================*/
    public function getCreditExpense(int $creditId, int $companyId): ?Expense
    {
        return Expense::where('credit_id', $creditId)
            ->where('company_id', $companyId)
            ->where('category', 'disbursement')
            ->first();
    }

    /**
     * Actualiza el egreso de desembolso de un crédito.
     *
     * BYPASS: Usa query builder para evitar la protección del modelo
     * que bloquea actualizaciones manuales a egresos de desembolso.
     * Solo debe usarse desde operaciones controladas del sistema.
     */
    public function updateCreditExpense(int $creditId, int $companyId, array $data): void
    {
        // Solo permitir campos seguros para actualización
        $allowedFields = ['amount', 'operation_date', 'collector_user_id', 'notes', 'metadata'];
        $updateData = array_intersect_key($data, array_flip($allowedFields));

        if (empty($updateData)) {
            return;
        }

        // Serializar metadata si es array
        if (isset($updateData['metadata']) && is_array($updateData['metadata'])) {
            $updateData['metadata'] = json_encode($updateData['metadata']);
        }

        $updateData['updated_at'] = now();

        // Query builder bypass - evita eventos del modelo
        Expense::where('credit_id', $creditId)
            ->where('company_id', $companyId)
            ->where('category', 'disbursement')
            ->update($updateData);
    }

    /**
     * Elimina el egreso de desembolso de un crédito.
     *
     * BYPASS: Usa query builder para evitar la protección del modelo
     * que bloquea eliminaciones de egresos financieros.
     * Solo debe usarse desde operaciones controladas del sistema
     * (ej: cancelación de crédito sin capital pagado).
     */
    public function deleteCreditExpense(int $creditId, int $companyId): void
    {
        // Query builder bypass - evita eventos del modelo
        Expense::where('credit_id', $creditId)
            ->where('company_id', $companyId)
            ->where('category', 'disbursement')
            ->delete();
    }
}
