<?php

namespace App\Services;

use App\Models\LoanProduct;
use Carbon\CarbonImmutable;
use InvalidArgumentException;

class RepaymentPeriodService
{
    /** @param list<string|CarbonImmutable> $customDates @return list<CarbonImmutable> */
    public function dueDates(LoanProduct $product, CarbonImmutable $startDate, array $customDates = []): array
    {
        if ($product->repayment_frequency === 'custom') {
            if ($customDates === []) throw new InvalidArgumentException('Custom repayment frequency requires custom due dates.');
            $dates = collect($customDates)->map(fn ($date) => $date instanceof CarbonImmutable ? $date : CarbonImmutable::parse($date))->sort()->values();
            if ($dates->contains(fn (CarbonImmutable $date) => $date->lessThanOrEqualTo($startDate))) throw new InvalidArgumentException('Custom due dates must be after the loan start date.');
            if ($dates->unique(fn (CarbonImmutable $d) => $d->toDateString())->count() !== $dates->count()) throw new InvalidArgumentException('Custom due dates must be unique.');
            return $dates->all();
        }

        $maturity = match ($product->duration_unit) {
            'days' => $startDate->addDays($product->duration),
            'weeks' => $startDate->addWeeks($product->duration),
            'months' => $startDate->addMonthsNoOverflow($product->duration),
            default => throw new InvalidArgumentException('Unsupported duration unit.'),
        };

        if ($product->repayment_frequency === 'lump_sum') return [$maturity->addDays($product->grace_period_days)];

        $dates = []; $cursor = $startDate; $guard = 0;
        while ($cursor->lessThan($maturity)) {
            $next = match ($product->repayment_frequency) {
                'daily' => $cursor->addDay(),
                'weekly' => $cursor->addWeek(),
                'biweekly' => $cursor->addWeeks(2),
                'monthly' => $cursor->addMonthNoOverflow(),
                default => throw new InvalidArgumentException('Unsupported repayment frequency.'),
            };
            if ($next->greaterThan($maturity)) $next = $maturity;
            $dates[] = $next->addDays($product->grace_period_days);
            $cursor = $next;
            if (++$guard > 5000) throw new InvalidArgumentException('Repayment schedule exceeds 5000 installments.');
        }
        return $dates;
    }
}
