<?php

declare(strict_types=1);

namespace App\DTOs;

use App\ValueObjects\Money;
use Carbon\Carbon;
use InvalidArgumentException;

/**
 * DTO para reestructuración de crédito con plan de cuotas personalizado.
 *
 * Permite definir cuotas con montos irregulares, a diferencia de la
 * reestructuración estándar que calcula cuotas iguales.
 *
 * Ejemplo de uso:
 *   - 6 cuotas de 170,000
 *   - 1 cuota final de 180,000
 *   - Total: 1,200,000 (debe coincidir con saldo pendiente)
 */
readonly class RestructureWithCustomPlanData
{
    /** @var CustomInstallmentData[] */
    public array $installments;

    public Money $capitalizedInterest;

    /**
     * @param  int  $creditId  ID del crédito a reestructurar
     * @param  CustomInstallmentData[]  $installments  Lista de cuotas personalizadas
     * @param  Money|float  $capitalizedInterest  Intereses a capitalizar (default 0)
     * @param  string|null  $newInterestRate  Nueva tasa de interés (opcional)
     * @param  string|null  $periodicity  Nueva periodicidad (opcional)
     * @param  string|null  $reason  Razón de la reestructuración
     */
    public function __construct(
        public int $creditId,
        array $installments,
        Money|float $capitalizedInterest = 0,
        public ?string $newInterestRate = null,
        public ?string $periodicity = null,
        public ?string $reason = null,
    ) {
        if (empty($installments)) {
            throw new InvalidArgumentException('Debe proporcionar al menos una cuota.');
        }

        // Validar que todos los elementos sean CustomInstallmentData
        foreach ($installments as $index => $installment) {
            if (! $installment instanceof CustomInstallmentData) {
                throw new InvalidArgumentException(
                    "El elemento en posición {$index} no es una instancia de CustomInstallmentData."
                );
            }
        }

        // Ordenar por número de cuota
        usort($installments, fn ($a, $b) => $a->number <=> $b->number);
        $this->installments = $installments;

        $this->capitalizedInterest = $capitalizedInterest instanceof Money
            ? $capitalizedInterest
            : Money::of($capitalizedInterest);
    }

    /**
     * Crea instancia desde array (útil para requests/formularios).
     *
     * @param  array  $data  Array con keys: credit_id, installments, etc.
     */
    public static function fromArray(array $data): self
    {
        $installments = array_map(
            fn ($item) => CustomInstallmentData::fromArray($item),
            $data['installments'] ?? []
        );

        return new self(
            creditId: (int) $data['credit_id'],
            installments: $installments,
            capitalizedInterest: (float) ($data['capitalized_interest'] ?? 0),
            newInterestRate: $data['new_interest_rate'] ?? null,
            periodicity: $data['periodicity'] ?? null,
            reason: $data['reason'] ?? null,
        );
    }

    /**
     * Número total de cuotas en el plan.
     */
    public function installmentsCount(): int
    {
        return count($this->installments);
    }

    /**
     * Suma total de todas las cuotas.
     */
    public function totalAmount(): Money
    {
        return array_reduce(
            $this->installments,
            fn (Money $carry, CustomInstallmentData $i) => $carry->add($i->totalAmount),
            Money::zero()
        );
    }

    /**
     * Fecha de la primera cuota.
     */
    public function firstDueDate(): Carbon
    {
        return $this->installments[0]->dueDate;
    }

    /**
     * Fecha de la última cuota.
     */
    public function lastDueDate(): Carbon
    {
        return $this->installments[count($this->installments) - 1]->dueDate;
    }

    /**
     * Tipo de operación para logs y auditoría.
     */
    public function operationType(): string
    {
        return 'restructure_custom_plan';
    }

    /**
     * Convierte a array para persistencia/serialización.
     */
    public function toArray(): array
    {
        return [
            'credit_id' => $this->creditId,
            'installments_count' => $this->installmentsCount(),
            'total_amount' => $this->totalAmount()->value(),
            'installments' => array_map(fn ($i) => $i->toArray(), $this->installments),
            'capitalized_interest' => $this->capitalizedInterest->value(),
            'new_interest_rate' => $this->newInterestRate,
            'periodicity' => $this->periodicity,
            'reason' => $this->reason,
        ];
    }
}
