<?php

declare(strict_types=1);

namespace App\Services\Credits\Operations;

use App\DTOs\CreditOperationResult;
use App\Enums\CreditAuditEvent;
use App\Models\Credit;
use App\Models\CreditAuditLog;
use App\Models\CreditRestructureLog;
use App\Models\FinancialOperation;
use App\Services\CollectorCreditOrderService;
use App\Services\CreditCalculator;
use App\Services\CreditManager;
use App\Services\FinanceAutoLogger;
use App\ValueObjects\Money;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;

/**
 * Clase base abstracta para operaciones de crédito.
 *
 * Proporciona métodos comunes para:
 * - Creación de créditos hijos
 * - Registro de auditoría
 * - Registro de operaciones financieras
 * - Cierre del crédito original
 */
abstract class BaseCreditOperation
{
    public function __construct(
        protected readonly CreditManager $creditManager,
        protected readonly CreditCalculator $creditCalculator,
        protected readonly FinanceAutoLogger $financeAutoLogger,
    ) {}

    /**
     * Cierra el crédito original con el estado específico de la operación.
     */
    protected function closeOriginalCredit(Credit $credit, string $newStatus): void
    {
        $credit->update(['status' => $newStatus]);
    }

    /**
     * Crea el nuevo crédito hijo con los parámetros dados.
     */
    protected function createChildCredit(
        Credit $originalCredit,
        Money $principal,
        int $installmentsCount,
        ?string $periodicity,
        ?Carbon $startDate,
        ?Carbon $firstDueDateOverride,
        ?int $dueDay1,
        ?int $dueDay2,
        ?string $interestRate = null,
    ): Credit {
        $newCredit = Credit::create([
            'company_id' => $originalCredit->company_id,
            'client_id' => $originalCredit->client_id,
            'collector_user_id' => $originalCredit->collector_user_id,
            'created_by_user_id' => Auth::id(),
            'parent_credit_id' => $originalCredit->id,

            'status' => Credit::STATUS_ACTIVE,
            'amount' => $principal->value(),
            'interest_rate' => $interestRate ?? $originalCredit->interest_rate,
            'installments_count' => $installmentsCount,
            'periodicity' => $periodicity ?? $originalCredit->periodicity,

            'start_date' => $startDate ?? now(),
            'first_due_date_override' => $firstDueDateOverride,
            'due_day_1' => $dueDay1 ?? $originalCredit->due_day_1,
            'due_day_2' => $dueDay2 ?? $originalCredit->due_day_2,
            'due_date' => null, // Se calcula al generar cuotas
        ]);

        // Generar cuotas del nuevo crédito
        $this->creditCalculator->generateInstallments($newCredit);

        // Refrescar estado
        $newCredit->refreshStatus();

        // Heredar la posición en la ruta del collector
        if ($originalCredit->collector_user_id) {
            app(CollectorCreditOrderService::class)->replaceCredit(
                $originalCredit->company_id,
                $originalCredit->id,
                $newCredit->id,
            );
        }

        return $newCredit;
    }

    /**
     * Registra en credit_restructure_logs.
     */
    protected function logRestructure(
        Credit $originalCredit,
        Credit $newCredit,
        string $type,
        Money $previousBalance,
        Money $newPrincipal,
        Money $additionalCapital,
        Money $capitalizedInterest,
        ?string $newInterestRate,
        int $newInstallmentCount,
        ?string $newPeriodicity,
        ?string $reason,
        array $metadata = [],
    ): CreditRestructureLog {
        return CreditRestructureLog::create([
            'original_credit_id' => $originalCredit->id,
            'new_credit_id' => $newCredit->id,
            'company_id' => $originalCredit->company_id,
            'performed_by_user_id' => Auth::id(),
            'type' => $type,
            'previous_balance' => $previousBalance->value(),
            'new_principal' => $newPrincipal->value(),
            'new_interest_rate' => $newInterestRate ?? $originalCredit->interest_rate,
            'new_installment_count' => $newInstallmentCount,
            'new_periodicity' => $newPeriodicity ?? $originalCredit->periodicity,
            'additional_capital' => $additionalCapital->value(),
            'capitalized_interest' => $capitalizedInterest->value(),
            'reason' => $reason,
            'metadata' => array_merge($metadata, [
                'executed_at' => now()->toIso8601String(),
                'user_id' => Auth::id(),
            ]),
        ]);
    }

    /**
     * Registra en financial_operations.
     */
    protected function logFinancialOperation(
        Credit $originalCredit,
        Credit $newCredit,
        string $operationType,
        Money $creditAmount,
        Money $compensatedAmount,
        Money $cashOut,
        ?int $expenseId = null,
        ?string $notes = null,
    ): FinancialOperation {
        return FinancialOperation::create([
            'company_id' => $originalCredit->company_id,
            'user_id' => Auth::id(),
            'operation_type' => $operationType,
            'source_credit_id' => $originalCredit->id,
            'target_credit_id' => $newCredit->id,
            'credit_amount' => $creditAmount->value(),
            'compensated_amount' => $compensatedAmount->value(),
            'cash_out' => $cashOut->value(),
            'cash_in' => 0,
            'expense_id' => $expenseId,
            'notes' => $notes,
        ]);
    }

    /**
     * Registra en credit_audit_logs.
     */
    protected function logAudit(
        int $creditId,
        CreditAuditEvent $action,
        array $data,
        ?int $newCreditId = null,
    ): void {
        $credit = Credit::select('company_id')->find($creditId);
        $user = Auth::user();

        CreditAuditLog::create([
            'company_id' => $credit?->company_id ?? $user->company_id,
            'credit_id' => $creditId,
            'user_id' => $user?->id,
            'action' => $action->value,
            'data' => [
                'input' => $data,
                'new_credit_id' => $newCreditId,
                'executed_at' => now()->toIso8601String(),
            ],
        ]);
    }

    /**
     * Construye el resultado de la operación.
     */
    protected function buildResult(
        Credit $originalCredit,
        Credit $newCredit,
        CreditRestructureLog $restructureLog,
        FinancialOperation $financialOperation,
        Money $previousBalance,
        Money $newPrincipal,
        Money $cashOut,
        Money $compensatedAmount,
        string $operationType,
    ): CreditOperationResult {
        return new CreditOperationResult(
            originalCredit: $originalCredit->fresh(),
            newCredit: $newCredit->fresh(),
            restructureLog: $restructureLog,
            financialOperation: $financialOperation,
            previousBalance: $previousBalance,
            newPrincipal: $newPrincipal,
            cashOut: $cashOut,
            compensatedAmount: $compensatedAmount,
            operationType: $operationType,
        );
    }
}
