<?php

declare(strict_types=1);

namespace App\Services\Credits\Operations;

use App\DTOs\CreditOperationResult;
use App\DTOs\ExtendWithInterestData;
use App\Enums\CreditAuditEvent;
use App\Models\Credit;
use App\Models\CreditRestructureLog;
use App\ValueObjects\Money;

/**
 * Operación de EXTENSIÓN CON INTERESES ADICIONALES (Prórroga con Réditos).
 *
 * DEFINICIÓN:
 * Extensión del plazo de pago para un crédito CON PAGOS DE CAPITAL,
 * aplicando intereses adicionales sobre el saldo pendiente.
 *
 * DIFERENCIA CON EXTENSIÓN SIMPLE:
 * - Extensión simple: Usa assertNoCapitalPaid() - NO permite pagos de capital
 * - Esta operación: Específicamente diseñada PARA créditos con pagos de capital
 *
 * ECUACIÓN FINANCIERA:
 *   remaining_balance = suma de (total_amount - amount_paid) de cuotas no pagadas
 *   additional_interest = remaining_balance × interest_rate × additional_periods
 *   new_principal = remaining_balance + additional_interest
 *   cash_out = 0 (no hay desembolso)
 *
 * FLUJO DE DATOS:
 *   1. Crédito original con pagos parciales/totales en algunas cuotas
 *   2. Se calcula el saldo pendiente real (remaining_balance)
 *   3. Se calcula el interés adicional según períodos solicitados
 *   4. Se crea nuevo crédito hijo con new_principal = balance + interest
 *   5. Original se marca como 'extended'
 *   6. Cuotas pagadas del original quedan INTACTAS (frozen)
 *
 * ESTADOS:
 *   Entrada: active, delayed, overdue (CON pagos de capital permitidos)
 *   Salida original: extended
 *   Salida nuevo: active
 *
 * CASO DE USO REAL (Credit #53 - Yulisa Rodriguez):
 *   - Capital original: 500,000
 *   - Total: 700,000 (10 cuotas × 70,000)
 *   - Pagado: 6 cuotas × 70,000 = 420,000
 *   - Pendiente: 280,000
 *   - Solicitud: +1 mes de intereses
 *   - Cálculo: 280,000 × 40% = 112,000 (interés adicional)
 *   - Nuevo total: 280,000 + 112,000 = 392,000
 */
class ExtendWithInterestOperation extends BaseCreditOperation
{
    /**
     * Ejecuta la extensión con intereses adicionales.
     *
     * PRECONDICIÓN: Validación ya realizada por CreditOperationValidator
     *
     * @param  Credit  $credit  Crédito a extender (locked for update)
     * @param  ExtendWithInterestData  $data  Datos de la extensión
     * @param  Money  $remainingBalance  Saldo pendiente calculado
     */
    public function execute(
        Credit $credit,
        ExtendWithInterestData $data,
        Money $remainingBalance,
    ): CreditOperationResult {
        // 1. Determinar la tasa de interés a aplicar
        $interestRate = $data->newInterestRate !== null
            ? (float) $data->newInterestRate
            : (float) $credit->interest_rate;

        // 2. Calcular interés adicional
        // Fórmula: saldo × (tasa/100) × períodos_adicionales
        $additionalInterest = $this->calculateAdditionalInterest(
            $remainingBalance,
            $interestRate,
            $data->additionalInterestPeriods
        );

        // 3. Calcular nuevo principal (saldo + interés adicional)
        $newPrincipal = $remainingBalance->add($additionalInterest);

        // 4. Crear nuevo crédito hijo con el nuevo principal
        // IMPORTANTE: El interés adicional YA está incluido en el principal (capitalizado),
        // por lo tanto el crédito hijo debe tener interest_rate = 0 para que NO se calcule
        // interés adicional sobre el monto. El total a pagar = principal = saldo + interés capitalizado.
        //
        // Ejemplo:
        //   - Saldo pendiente: 280,000
        //   - Interés adicional (1 período × 25%): 70,000
        //   - Nuevo principal: 350,000 (ya incluye el interés)
        //   - interest_rate: 0 (para que 350,000 / 10 cuotas = 35,000 c/u)
        $newCredit = $this->createChildCredit(
            originalCredit: $credit,
            principal: $newPrincipal,
            installmentsCount: $data->newInstallmentsCount,
            periodicity: $data->periodicity,
            startDate: $data->startDate,
            firstDueDateOverride: $data->firstDueDateOverride,
            dueDay1: $data->dueDay1,
            dueDay2: $data->dueDay2,
            interestRate: '0', // El interés ya está capitalizado en el principal
        );

        // 5. Cerrar crédito original con estado 'extended'
        $this->closeOriginalCredit($credit, Credit::STATUS_EXTENDED);

        // 6. Registrar en credit_restructure_logs
        // NOTA: El nuevo crédito tiene interest_rate = 0 porque el interés ya está capitalizado
        // en el principal. La tasa usada para calcular el interés adicional se guarda en metadata.
        $restructureLog = $this->logRestructure(
            originalCredit: $credit,
            newCredit: $newCredit,
            type: CreditRestructureLog::TYPE_EXTENSION,
            previousBalance: $remainingBalance,
            newPrincipal: $newPrincipal,
            additionalCapital: Money::zero(),
            capitalizedInterest: $additionalInterest, // Registramos el interés adicional
            newInterestRate: '0', // El nuevo crédito tiene tasa 0 (interés ya capitalizado)
            newInstallmentCount: $data->newInstallmentsCount,
            newPeriodicity: $data->periodicity,
            reason: $data->reason,
            metadata: [
                'operation' => 'extension_with_interest',
                'additional_interest_periods' => $data->additionalInterestPeriods,
                'interest_rate_applied' => $interestRate, // Tasa usada para calcular el interés adicional
                'additional_interest_calculated' => $additionalInterest->value(),
                'original_balance' => $remainingBalance->value(),
                'interest_capitalized' => true, // Flag que indica que el interés fue capitalizado
                'new_credit_interest_rate' => 0, // El nuevo crédito no genera interés adicional
                'input_data' => $data->toArray(),
                'allows_capital_payments' => true,
            ],
        );

        // 7. Registrar operación financiera (sin movimiento de caja)
        // IMPORTANTE: Para que cash_out = 0, debemos establecer compensated_amount = credit_amount
        // Esto representa que toda la nueva deuda está "compensada" por la operación:
        // - El saldo pendiente original (280,000) se transfiere
        // - El interés adicional (112,000) se capitaliza (no es efectivo, es cargo contable)
        // - Total compensado = 392,000 = nuevo principal
        // - Cash out = 392,000 - 392,000 = 0 ✓
        $financialOperation = $this->logFinancialOperation(
            originalCredit: $credit,
            newCredit: $newCredit,
            operationType: 'credit_extension',
            creditAmount: $newPrincipal,
            compensatedAmount: $newPrincipal, // Toda la deuda está compensada, sin cash out
            cashOut: Money::zero(),
            expenseId: null, // No hay desembolso
            notes: sprintf(
                'Extensión con réditos adicionales - Saldo original: %s, Interés adicional (%d períodos): %s, Nuevo total: %s - Sin movimiento de caja (interés capitalizado)',
                $remainingBalance->format(),
                $data->additionalInterestPeriods,
                $additionalInterest->format(),
                $newPrincipal->format()
            ),
        );

        // 8. Registrar auditoría con evento específico
        $this->logAudit(
            creditId: $credit->id,
            action: CreditAuditEvent::CREDIT_EXTENDED_WITH_INTEREST,
            data: array_merge($data->toArray(), [
                'previous_balance' => $remainingBalance->value(),
                'additional_interest' => $additionalInterest->value(),
                'new_principal' => $newPrincipal->value(),
                'new_credit_id' => $newCredit->id,
                'paid_installments_frozen' => $this->countPaidInstallments($credit),
            ]),
            newCreditId: $newCredit->id,
        );

        // 9. Construir y retornar resultado
        return $this->buildResult(
            originalCredit: $credit,
            newCredit: $newCredit,
            restructureLog: $restructureLog,
            financialOperation: $financialOperation,
            previousBalance: $remainingBalance,
            newPrincipal: $newPrincipal,
            cashOut: Money::zero(),
            compensatedAmount: $newPrincipal, // Incluye saldo + interés capitalizado
            operationType: 'extension_with_interest',
        );
    }

    /**
     * Calcula el interés adicional sobre el saldo pendiente.
     *
     * Fórmula: saldo × (tasa/100) × períodos
     *
     * @param  Money  $balance  Saldo pendiente
     * @param  float  $interestRate  Tasa de interés (ej: 40 para 40%)
     * @param  int  $periods  Número de períodos (meses/semanas según periodicidad)
     */
    private function calculateAdditionalInterest(
        Money $balance,
        float $interestRate,
        int $periods
    ): Money {
        if ($periods <= 0) {
            return Money::zero();
        }

        // BC Math (regla "no floats"): saldo × (tasa/100) × períodos (#96).
        return $balance->percentage($interestRate)->multiply($periods);
    }

    /**
     * Cuenta las cuotas pagadas del crédito original.
     * Útil para auditoría.
     */
    private function countPaidInstallments(Credit $credit): int
    {
        return $credit->installments()
            ->where('status', 'paid')
            ->count();
    }
}
