<?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
    {
        $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;

        $offerOverrides = BoardOfferService::getInstallmentOverrides();
        if (!empty($offerOverrides)) {
            if (isset($offerOverrides['max_months'])) $maxMonths = $offerOverrides['max_months'];
            if (isset($offerOverrides['interest_rate'])) $annualRate = $offerOverrides['interest_rate'];
        }

        $errors = [];

        // Validate down payment >= 25%
        $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 (!empty($errors)) {
            return ['success' => false, 'errors' => $errors];
        }

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

        // Flat simple interest: remaining × (annual_rate/100) × (months/12)
        $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);

        // Per-instalment: equal share of interest, flat principal
        $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) // absorb rounding
                : $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,
            ];
        }

        $avgMonthly = $monthlyPayment;

        // Cash settlement date (30 days from start)
        $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'     => $avgMonthly,
            'start_date'          => $startDate,
            'cash_settlement_date'=> $cashSettlementDate,
            'cash_days'           => $cashDays,
            '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);
        }

        // Only first pending item's interest is due (current month)
        $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.
     *
     * Business rule: interest waiver is only valid when every single pending/overdue installment
     * is being settled in the same transaction. Partial settlement is not allowed here.
     *
     * Returns:
     *  - pending_items        : the unpaid schedule rows
     *  - items_count          : how many will be settled
     *  - total_original_due   : sum of amount (principal+interest) across all pending rows
     *  - remaining_principal  : sum of principal only — what the member actually pays
     *  - total_interest_waived: sum of interest across all pending rows — fully waived
     *  - settlement_amount    : equals remaining_principal (what is charged)
     */
    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,
        ];
    }
}