<?php
declare(strict_types=1);

namespace App\Modules\Installments\Services;

use App\Modules\Rules\Services\RuleEngine;
use App\Modules\Members\Services\BoardOfferService;

final class InstallmentCalculator
{
    /**
     * Calculate a full installment plan with schedule.
     */
    public static function calculate(string $totalAmount, string $downPayment, int $months, ?string $startDate = null, array $offerOverrides = []): array
    {
        $interestData = RuleEngine::get('INSTALLMENT_INTEREST_RATE');
        $annualRate = $interestData['percentage'] ?? '22.00';

        $minDownData = RuleEngine::get('INSTALLMENT_MIN_DOWN_PAYMENT');
        $minDownPct = $minDownData['percentage'] ?? '25.00';

        $maxMonthsData = RuleEngine::get('INSTALLMENT_MAX_MONTHS');
        $maxMonths = $maxMonthsData['months'] ?? 30;

        // Apply offer overrides (passed directly or from active board offer)
        if (empty($offerOverrides)) {
            $offerOverrides = BoardOfferService::getInstallmentOverrides();
        }
        if (!empty($offerOverrides)) {
            if (isset($offerOverrides['max_months'])) $maxMonths = $offerOverrides['max_months'];
            if (isset($offerOverrides['interest_rate'])) $annualRate = $offerOverrides['interest_rate'];
            if (isset($offerOverrides['min_down_pct'])) $minDownPct = $offerOverrides['min_down_pct'];
        }

        $graceType = $offerOverrides['grace_type'] ?? null;
        $graceMonths = (int) ($offerOverrides['grace_months'] ?? 0);
        $postGraceRate = $offerOverrides['post_grace_rate'] ?? null;

        // Mode B: full_free_under_n — 0% if months <= graceMonths
        if ($graceType === 'full_free_under_n' && $months <= $graceMonths) {
            $annualRate = '0.00';
            $graceType = null;
            $graceMonths = 0;
        }

        $errors = [];

        $minDown = bcdiv(bcmul($totalAmount, $minDownPct, 4), '100', 2);
        if (bccomp($downPayment, $minDown, 2) < 0) {
            $errors[] = "الحد الأدنى للمقدم {$minDownPct}% = " . number_format((float) $minDown, 2) . ' ج.م';
        }

        if (bccomp($downPayment, $totalAmount, 2) >= 0) {
            $errors[] = 'المقدم لا يمكن أن يكون أكبر من أو يساوي المبلغ الكلي';
        }

        if ($months < 1 || $months > $maxMonths) {
            $errors[] = "عدد الأقساط يجب أن يكون بين 1 و {$maxMonths} شهر";
        }

        if ($graceType === 'first_n_months' && $graceMonths >= $months) {
            $errors[] = 'أشهر السماح يجب أن تكون أقل من إجمالي الأقساط';
        }

        if (!empty($errors)) {
            return ['success' => false, 'errors' => $errors];
        }

        $remaining = bcsub($totalAmount, $downPayment, 2);
        $startDate = $startDate ?: date('Y-m-d');

        // Mode A: first_n_months grace — interest only on post-grace period
        if ($graceType === 'first_n_months' && $graceMonths > 0) {
            return self::calculateWithGrace($remaining, $months, $annualRate, $graceMonths, $postGraceRate, $startDate, $totalAmount, $downPayment, $minDownPct, $maxMonths);
        }

        // Standard flat simple interest
        $totalInterest = bcmul(
            bcmul($remaining, bcdiv($annualRate, '100', 10), 10),
            bcdiv((string) $months, '12', 10),
            2
        );
        $totalWithInterest = bcadd($remaining, $totalInterest, 2);
        $monthlyPayment = bcdiv($totalWithInterest, (string) $months, 2);

        $monthlyPrincipal = bcdiv($remaining, (string) $months, 2);
        $monthlyInterest = bcdiv($totalInterest, (string) $months, 2);

        $schedule = [];
        $runningBalance = $remaining;

        for ($i = 1; $i <= $months; $i++) {
            $isLast = ($i === $months);
            $principal = $isLast ? $runningBalance : $monthlyPrincipal;
            $interest = $isLast
                ? bcsub($totalWithInterest, bcadd($remaining, bcsub($totalInterest, $monthlyInterest, 2), 2), 2)
                : $monthlyInterest;
            if (bccomp($interest, '0', 2) < 0) $interest = '0.00';
            $amount = bcadd($principal, $interest, 2);

            $runningBalance = bcsub($runningBalance, $principal, 2);
            if (bccomp($runningBalance, '0', 2) < 0) $runningBalance = '0.00';

            $dueDate = date('Y-m-d', strtotime($startDate . " +{$i} months"));

            $schedule[] = [
                'number'          => $i,
                'due_date'        => $dueDate,
                'amount'          => $amount,
                'principal'       => $principal,
                'interest'        => $interest,
                'remaining_after' => $runningBalance,
                'is_grace'        => false,
            ];
        }

        $cashWindowData = RuleEngine::get('CASH_PAYMENT_WINDOW');
        $cashDays = $cashWindowData['days'] ?? 30;
        $cashSettlementDate = date('Y-m-d', strtotime($startDate . " +{$cashDays} days"));

        return [
            'success'             => true,
            'total_amount'        => $totalAmount,
            'down_payment'        => $downPayment,
            'remaining_balance'   => $remaining,
            'interest_rate'       => $annualRate,
            'total_interest'      => $totalInterest,
            'total_with_interest' => $totalWithInterest,
            'number_of_months'    => $months,
            'monthly_payment'     => $monthlyPayment,
            'start_date'          => $startDate,
            'cash_settlement_date'=> $cashSettlementDate,
            'cash_days'           => $cashDays,
            'grace_months'        => 0,
            'grace_type'          => null,
            'schedule'            => $schedule,
        ];
    }

    private static function calculateWithGrace(string $remaining, int $months, string $annualRate, int $graceMonths, ?string $postGraceRate, string $startDate, string $totalAmount, string $downPayment, string $minDownPct, int $maxMonths): array
    {
        $effectiveRate = $postGraceRate ?? $annualRate;
        $postGraceMonths = $months - $graceMonths;

        // Interest calculated only on post-grace months
        $totalInterest = bcmul(
            bcmul($remaining, bcdiv($effectiveRate, '100', 10), 10),
            bcdiv((string) $postGraceMonths, '12', 10),
            2
        );
        $totalWithInterest = bcadd($remaining, $totalInterest, 2);

        // Equal principal across ALL months
        $monthlyPrincipal = bcdiv($remaining, (string) $months, 2);
        // Interest spread across post-grace months only
        $monthlyInterest = $postGraceMonths > 0 ? bcdiv($totalInterest, (string) $postGraceMonths, 2) : '0.00';

        $schedule = [];
        $runningBalance = $remaining;

        for ($i = 1; $i <= $months; $i++) {
            $isLast = ($i === $months);
            $isGrace = ($i <= $graceMonths);

            $principal = $isLast ? $runningBalance : $monthlyPrincipal;
            $interest = $isGrace ? '0.00' : $monthlyInterest;

            if ($isLast && !$isGrace) {
                // Absorb rounding in last post-grace month
                $paidInterest = bcmul($monthlyInterest, (string) ($postGraceMonths - 1), 2);
                $interest = bcsub($totalInterest, $paidInterest, 2);
                if (bccomp($interest, '0', 2) < 0) $interest = '0.00';
            }

            $amount = bcadd($principal, $interest, 2);

            $runningBalance = bcsub($runningBalance, $principal, 2);
            if (bccomp($runningBalance, '0', 2) < 0) $runningBalance = '0.00';

            $dueDate = date('Y-m-d', strtotime($startDate . " +{$i} months"));

            $schedule[] = [
                'number'          => $i,
                'due_date'        => $dueDate,
                'amount'          => $amount,
                'principal'       => $principal,
                'interest'        => $interest,
                'remaining_after' => $runningBalance,
                'is_grace'        => $isGrace,
            ];
        }

        $avgMonthly = bcdiv($totalWithInterest, (string) $months, 2);

        $cashWindowData = RuleEngine::get('CASH_PAYMENT_WINDOW');
        $cashDays = $cashWindowData['days'] ?? 30;
        $cashSettlementDate = date('Y-m-d', strtotime($startDate . " +{$cashDays} days"));

        return [
            'success'             => true,
            'total_amount'        => $totalAmount,
            'down_payment'        => $downPayment,
            'remaining_balance'   => $remaining,
            'interest_rate'       => $effectiveRate,
            'total_interest'      => $totalInterest,
            'total_with_interest' => $totalWithInterest,
            'number_of_months'    => $months,
            'monthly_payment'     => $avgMonthly,
            'start_date'          => $startDate,
            'cash_settlement_date'=> $cashSettlementDate,
            'cash_days'           => $cashDays,
            'grace_months'        => $graceMonths,
            'grace_type'          => 'first_n_months',
            'schedule'            => $schedule,
        ];
    }

    /**
     * Calculate early payoff: remaining principal with zero further interest.
     */
    public static function calculateEarlyPayoff(int $planId): array
    {
        $remainingPrincipal = '0.00';
        $db = \App\Core\App::getInstance()->db();
        $items = $db->select(
            "SELECT * FROM installment_schedule WHERE installment_plan_id = ? AND status IN ('pending','overdue') ORDER BY installment_number ASC",
            [$planId]
        );

        foreach ($items as $item) {
            $remainingPrincipal = bcadd($remainingPrincipal, $item['principal'], 2);
        }

        $currentInterest = '0.00';
        if (!empty($items)) {
            $currentInterest = $items[0]['interest'] ?? '0.00';
        }

        $totalDue = bcadd($remainingPrincipal, $currentInterest, 2);

        return [
            'remaining_principal' => $remainingPrincipal,
            'current_interest'    => $currentInterest,
            'total_early_payoff'  => $totalDue,
            'items_remaining'     => count($items),
            'interest_saved'      => bcsub(
                array_reduce($items, fn($carry, $i) => bcadd($carry, $i['interest'], 2), '0.00'),
                $currentInterest,
                2
            ),
        ];
    }

    /**
     * Calculate early settlement: member pays ALL remaining principal now, ALL future interest is waived.
     */
    public static function calculateEarlySettlement(int $planId): array
    {
        $db = \App\Core\App::getInstance()->db();

        $items = $db->select(
            "SELECT * FROM installment_schedule
             WHERE installment_plan_id = ? AND status IN ('pending','overdue')
             ORDER BY installment_number ASC",
            [$planId]
        );

        if (empty($items)) {
            return [
                'success' => false,
                'error'   => 'لا توجد أقساط معلقة لهذه الخطة',
            ];
        }

        $totalOriginalDue = '0.00';
        $remainingPrincipal = '0.00';
        $totalInterestWaived = '0.00';

        foreach ($items as $item) {
            $totalOriginalDue = bcadd($totalOriginalDue, (string) $item['amount'], 2);
            $remainingPrincipal = bcadd($remainingPrincipal, (string) $item['principal'], 2);
            $totalInterestWaived = bcadd($totalInterestWaived, (string) $item['interest'], 2);
        }

        return [
            'success'              => true,
            'pending_items'        => $items,
            'items_count'          => count($items),
            'total_original_due'   => $totalOriginalDue,
            'remaining_principal'  => $remainingPrincipal,
            'total_interest_waived'=> $totalInterestWaived,
            'settlement_amount'    => $remainingPrincipal,
        ];
    }
}
