<?php
declare(strict_types=1);

namespace App\Modules\Subscriptions\Services;

use App\Core\App;
use App\Modules\Rules\Services\RuleEngine;

final class SubscriptionCalculator
{
    public static function calculateLateFine(int $memberId, string $financialYear): array
    {
        $db = App::getInstance()->db();

        $unpaidYears = $db->select(
            "SELECT financial_year, SUM(total_amount) as total, SUM(paid_amount) as paid
             FROM subscriptions WHERE member_id = ? AND status IN ('pending','overdue') AND financial_year <= ?
             GROUP BY financial_year ORDER BY financial_year ASC",
            [$memberId, $financialYear]
        );

        $totalFine = '0.00';
        $fineDetails = [];

        $maxYearsData = RuleEngine::get('LATE_SUB_FINE_MAX_YEARS');
        $maxYears = (int) ($maxYearsData['years'] ?? 5);
        $dropYearsData = RuleEngine::get('LATE_SUB_DROP_YEARS');
        $dropYears = (int) ($dropYearsData['years'] ?? 5);
        $graceData = RuleEngine::get('SUB_GRACE_MONTHS');
        $graceMonths = (int) ($graceData['months'] ?? 3);

        $currentFYStart = (int) explode('/', $financialYear)[0];

        foreach ($unpaidYears as $uy) {
            $subFYStart = (int) explode('/', $uy['financial_year'])[0];
            $yearsOverdue = $currentFYStart - $subFYStart;

            if ($yearsOverdue === 0) {
                $graceEnd = mktime(0, 0, 0, 7 + $graceMonths, 1, $subFYStart);
                if (time() < $graceEnd) {
                    $fineDetails[] = [
                        'financial_year'  => $uy['financial_year'],
                        'unpaid'          => bcsub($uy['total'], $uy['paid'], 2),
                        'fine_percentage' => '0',
                        'fine_amount'     => '0.00',
                        'years_overdue'   => 0,
                        'in_grace'        => true,
                    ];
                    continue;
                }
            }

            $unpaid = bcsub($uy['total'], $uy['paid'], 2);
            if (bccomp($unpaid, '0', 2) <= 0) continue;

            $penaltyLevel = $yearsOverdue + 1;
            if ($penaltyLevel > $maxYears) $penaltyLevel = $maxYears;

            $ruleCode = 'LATE_SUB_FINE_YEAR_' . $penaltyLevel;
            $fineData = RuleEngine::get($ruleCode);
            $finePct = $fineData['percentage_of_subscription'] ?? '0';

            // Fine is calculated on the full subscription amount (total_amount), not remaining unpaid
            $fineAmount = bcdiv(bcmul($uy['total'], $finePct, 4), '100', 2);
            $totalFine = bcadd($totalFine, $fineAmount, 2);

            $fineDetails[] = [
                'financial_year'  => $uy['financial_year'],
                'unpaid'          => $unpaid,
                'fine_percentage' => $finePct,
                'fine_amount'     => $fineAmount,
                'years_overdue'   => $yearsOverdue,
                'in_grace'        => false,
            ];
        }

        $yearCount = count(array_filter($fineDetails, fn($d) => !($d['in_grace'] ?? false)));
        $oldestOverdue = !empty($fineDetails) ? max(array_column($fineDetails, 'years_overdue')) : 0;

        // Check for CONSECUTIVE unpaid years (not just oldest overdue)
        $shouldDrop = false;
        if ($oldestOverdue >= $dropYears) {
            $unpaidStarts = array_map(fn($uy) => (int) explode('/', $uy['financial_year'])[0], $unpaidYears);
            sort($unpaidStarts);
            $consecutive = 1;
            $maxConsecutive = 1;
            for ($i = 1, $c = count($unpaidStarts); $i < $c; $i++) {
                if ($unpaidStarts[$i] === $unpaidStarts[$i - 1] + 1) {
                    $consecutive++;
                    if ($consecutive > $maxConsecutive) $maxConsecutive = $consecutive;
                } else {
                    $consecutive = 1;
                }
            }
            $shouldDrop = $maxConsecutive >= $dropYears;
        }

        return [
            'total_fine'     => $totalFine,
            'details'        => $fineDetails,
            'years_unpaid'   => count($unpaidYears),
            'years_overdue'  => $oldestOverdue,
            'should_drop'    => $shouldDrop,
            'max_years'      => $maxYears,
            'grace_months'   => $graceMonths,
        ];
    }

    public static function canReinstate(int $memberId): array
    {
        $db = App::getInstance()->db();

        $member = $db->selectOne(
            "SELECT status, updated_at FROM members WHERE id = ? AND status = 'dropped'",
            [$memberId]
        );
        if (!$member) {
            return ['eligible' => false, 'reason' => 'العضو ليس مسقطاً'];
        }

        $windowData = RuleEngine::get('REINSTATEMENT_WINDOW');
        $windowMonths = (int) ($windowData['months'] ?? 12);

        $droppedAt = strtotime($member['updated_at']);
        $deadline = strtotime("+{$windowMonths} months", $droppedAt);

        if (time() > $deadline) {
            return [
                'eligible' => false,
                'reason'   => 'انتهت مهلة إعادة العضوية (' . $windowMonths . ' شهر)',
                'dropped_at' => $member['updated_at'],
                'deadline'   => date('Y-m-d', $deadline),
            ];
        }

        $totalDebt = $db->selectOne(
            "SELECT COALESCE(SUM(total_amount - paid_amount + fine_amount), 0) as debt
             FROM subscriptions WHERE member_id = ? AND status IN ('pending','overdue')",
            [$memberId]
        );

        return [
            'eligible'    => true,
            'total_debt'  => $totalDebt['debt'] ?? '0.00',
            'dropped_at'  => $member['updated_at'],
            'deadline'    => date('Y-m-d', $deadline),
            'days_remaining' => max(0, (int) ceil(($deadline - time()) / 86400)),
        ];
    }
}
