<?php

declare(strict_types=1);

namespace App\Services\Credits\Operations;

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

/**
 * Operación de REFINANCIACIÓN de crédito.
 *
 * DEFINICIÓN:
 * Creación de nuevo crédito que INCLUYE deuda anterior + capital adicional.
 * HAY DESEMBOLSO de efectivo (solo el capital adicional).
 *
 * ECUACIÓN FINANCIERA:
 *   nuevo_principal = saldo_pendiente + capital_adicional
 *   cash_out = capital_adicional (SOLO lo nuevo)
 *   compensated_amount = saldo_pendiente
 *
 * ESTADOS:
 *   Entrada: active, delayed, overdue
 *   Salida original: refinanced
 *   Salida nuevo: active
 */
class RefinanceCreditOperation extends BaseCreditOperation
{
    /**
     * Ejecuta la refinanciación del crédito.
     *
     * PRECONDICIÓN: Validación ya realizada por CreditOperationValidator
     *
     * @param  Credit  $credit  Crédito a refinanciar (locked for update)
     * @param  RefinanceCreditData  $data  Datos de la refinanciación
     * @param  Money  $remainingBalance  Saldo pendiente calculado
     */
    public function execute(
        Credit $credit,
        RefinanceCreditData $data,
        Money $remainingBalance,
    ): CreditOperationResult {
        // 1. Calcular nuevo principal (saldo + capital adicional)
        $newPrincipal = $remainingBalance->add($data->additionalCapital);

        // 2. Crear nuevo crédito hijo con el nuevo principal
        $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: $data->newInterestRate,
        );

        // 3. Registrar desembolso SOLO del capital adicional
        $expense = $this->financeAutoLogger->logExpense([
            'company_id' => $newCredit->company_id,
            'user_id' => auth()->id(),
            'collector_user_id' => $newCredit->collector_user_id,
            'amount' => $data->additionalCapital->value(),
            'credit_id' => $newCredit->id,
            'category' => 'disbursement',
            'operation_date' => $data->startDate ?? now(),
            'notes' => sprintf(
                'Desembolso por refinanciación - Capital adicional del crédito #%d',
                $credit->id
            ),
            'metadata' => [
                'source' => 'credit_refinanced',
                'original_credit_id' => $credit->id,
                'previous_balance' => $remainingBalance->value(),
                'additional_capital' => $data->additionalCapital->value(),
            ],
        ]);

        // 4. Cerrar crédito original con estado 'refinanced'
        $this->closeOriginalCredit($credit, Credit::STATUS_REFINANCED);

        // 5. Registrar en credit_restructure_logs
        $restructureLog = $this->logRestructure(
            originalCredit: $credit,
            newCredit: $newCredit,
            type: CreditRestructureLog::TYPE_REFINANCE,
            previousBalance: $remainingBalance,
            newPrincipal: $newPrincipal,
            additionalCapital: $data->additionalCapital,
            capitalizedInterest: Money::zero(),
            newInterestRate: $data->newInterestRate,
            newInstallmentCount: $data->newInstallmentsCount,
            newPeriodicity: $data->periodicity,
            reason: $data->reason,
            metadata: [
                'operation' => 'refinance',
                'input_data' => $data->toArray(),
                'expense_id' => $expense->id,
            ],
        );

        // 6. Registrar operación financiera
        $financialOperation = $this->logFinancialOperation(
            originalCredit: $credit,
            newCredit: $newCredit,
            operationType: 'credit_refinance',
            creditAmount: $newPrincipal,
            compensatedAmount: $remainingBalance,
            cashOut: $data->additionalCapital,
            expenseId: $expense->id,
            notes: sprintf(
                'Refinanciación - Saldo anterior: %s, Capital adicional: %s',
                $remainingBalance->formatWithCurrency(),
                $data->additionalCapital->formatWithCurrency()
            ),
        );

        // 7. Registrar auditoría
        $this->logAudit(
            creditId: $credit->id,
            action: CreditAuditEvent::CREDIT_REFINANCED,
            data: array_merge($data->toArray(), [
                'previous_balance' => $remainingBalance->value(),
                'new_principal' => $newPrincipal->value(),
                'cash_out' => $data->additionalCapital->value(),
                'new_credit_id' => $newCredit->id,
                'expense_id' => $expense->id,
            ]),
            newCreditId: $newCredit->id,
        );

        // 8. Construir y retornar resultado
        return $this->buildResult(
            originalCredit: $credit,
            newCredit: $newCredit,
            restructureLog: $restructureLog,
            financialOperation: $financialOperation,
            previousBalance: $remainingBalance,
            newPrincipal: $newPrincipal,
            cashOut: $data->additionalCapital,
            compensatedAmount: $remainingBalance,
            operationType: 'refinance',
        );
    }
}
