<?php

declare(strict_types=1);

namespace App\Services\Credits\Operations;

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

/**
 * Operación de REESTRUCTURACIÓN de crédito.
 *
 * DEFINICIÓN:
 * Modificación de condiciones del crédito SIN dinero nuevo.
 * Puede incluir: cambio de tasa, periodicidad, capitalización de intereses vencidos.
 *
 * ECUACIÓN FINANCIERA:
 *   nuevo_principal = saldo_pendiente + intereses_capitalizados
 *   cash_out = 0 (no hay desembolso)
 *   compensated_amount = saldo_pendiente
 *
 * ESTADOS:
 *   Entrada: active, delayed, overdue (típicamente overdue)
 *   Salida original: restructured
 *   Salida nuevo: active
 */
class RestructureCreditOperation extends BaseCreditOperation
{
    /**
     * Ejecuta la reestructuración del crédito.
     *
     * PRECONDICIÓN: Validación ya realizada por CreditOperationValidator
     *
     * @param  Credit  $credit  Crédito a reestructurar (locked for update)
     * @param  RestructureCreditData  $data  Datos de la reestructuración
     * @param  Money  $remainingBalance  Saldo pendiente calculado
     */
    public function execute(
        Credit $credit,
        RestructureCreditData $data,
        Money $remainingBalance,
    ): CreditOperationResult {
        // 1. Calcular nuevo principal (saldo + intereses capitalizados)
        $newPrincipal = $remainingBalance->add($data->capitalizedInterest);

        // 2. Crear nuevo crédito hijo con nuevas condiciones
        $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,
            // #117: sin tasa explícita → 0 (redistribuir el saldo sin recargar interés, como #90).
            interestRate: $data->newInterestRate ?? '0',
        );

        // 3. Cerrar crédito original con estado 'restructured'
        $this->closeOriginalCredit($credit, Credit::STATUS_RESTRUCTURED);

        // 4. Registrar en credit_restructure_logs
        $restructureLog = $this->logRestructure(
            originalCredit: $credit,
            newCredit: $newCredit,
            type: CreditRestructureLog::TYPE_RESTRUCTURE,
            previousBalance: $remainingBalance,
            newPrincipal: $newPrincipal,
            additionalCapital: Money::zero(),
            capitalizedInterest: $data->capitalizedInterest,
            newInterestRate: $data->newInterestRate ?? '0',
            newInstallmentCount: $data->newInstallmentsCount,
            newPeriodicity: $data->periodicity,
            reason: $data->reason,
            metadata: [
                'operation' => 'restructure',
                'input_data' => $data->toArray(),
                'conditions_changed' => [
                    'interest_rate' => $data->newInterestRate !== null,
                    'periodicity' => $data->periodicity !== null,
                    'interest_capitalized' => $data->capitalizedInterest->isPositive(),
                ],
            ],
        );

        // 5. Registrar operación financiera (sin movimiento de caja)
        $financialOperation = $this->logFinancialOperation(
            originalCredit: $credit,
            newCredit: $newCredit,
            operationType: 'credit_restructure',
            creditAmount: $newPrincipal,
            compensatedAmount: $remainingBalance,
            cashOut: Money::zero(),
            expenseId: null,
            notes: sprintf(
                'Reestructuración - Saldo: %s, Intereses capitalizados: %s, Nuevo principal: %s',
                $remainingBalance->formatWithCurrency(),
                $data->capitalizedInterest->formatWithCurrency(),
                $newPrincipal->formatWithCurrency()
            ),
        );

        // 6. Registrar auditoría
        $this->logAudit(
            creditId: $credit->id,
            action: CreditAuditEvent::CREDIT_RESTRUCTURED,
            data: array_merge($data->toArray(), [
                'previous_balance' => $remainingBalance->value(),
                'new_principal' => $newPrincipal->value(),
                'capitalized_interest' => $data->capitalizedInterest->value(),
                'new_credit_id' => $newCredit->id,
            ]),
            newCreditId: $newCredit->id,
        );

        // 7. Construir y retornar resultado
        return $this->buildResult(
            originalCredit: $credit,
            newCredit: $newCredit,
            restructureLog: $restructureLog,
            financialOperation: $financialOperation,
            previousBalance: $remainingBalance,
            newPrincipal: $newPrincipal,
            cashOut: Money::zero(),
            compensatedAmount: $remainingBalance,
            operationType: 'restructure',
        );
    }
}
