<?php
declare(strict_types=1);

namespace App\Modules\Subscriptions\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;

final class OverdueFineApplicator
{
    public static function run(): array
    {
        $db = App::getInstance()->db();

        $currentFY = self::currentFinancialYear();
        $results = ['fines_applied' => 0, 'members_dropped' => 0, 'errors' => []];

        $members = $db->select(
            "SELECT DISTINCT s.member_id
             FROM subscriptions s
             JOIN members m ON m.id = s.member_id AND m.is_archived = 0 AND m.status = 'active'
             WHERE s.status IN ('pending','overdue') AND s.financial_year < ?",
            [$currentFY]
        );

        foreach ($members as $row) {
            $memberId = (int) $row['member_id'];
            try {
                $calc = SubscriptionCalculator::calculateLateFine($memberId, $currentFY);

                foreach ($calc['details'] as $detail) {
                    if (($detail['in_grace'] ?? false)) continue;
                    if (bccomp($detail['fine_amount'], '0', 2) <= 0) continue;

                    self::distributeFineForYear($db, $memberId, $detail['financial_year'], $detail['fine_amount']);
                }

                if (bccomp($calc['total_fine'], '0', 2) > 0) {
                    $results['fines_applied']++;
                }

                if ($calc['should_drop']) {
                    $db->query(
                        "UPDATE members SET status = 'dropped', updated_at = NOW() WHERE id = ? AND status = 'active'",
                        [$memberId]
                    );
                    $results['members_dropped']++;

                    EventBus::dispatch('member.dropped', [
                        'member_id'   => $memberId,
                        'reason'      => 'تأخر عن سداد الاشتراك السنوي ' . $calc['years_overdue'] . ' سنوات متتالية',
                        'years_unpaid' => $calc['years_unpaid'],
                    ]);

                    Logger::info("Member dropped for non-payment", [
                        'member_id' => $memberId,
                        'years'     => $calc['years_overdue'],
                    ]);
                }
            } catch (\Throwable $e) {
                $results['errors'][] = "Member #{$memberId}: " . $e->getMessage();
                Logger::error("OverdueFineApplicator error", ['member_id' => $memberId, 'error' => $e->getMessage()]);
            }
        }

        return $results;
    }

    public static function applyForMember(int $memberId): array
    {
        $db = App::getInstance()->db();
        $currentFY = self::currentFinancialYear();

        $calc = SubscriptionCalculator::calculateLateFine($memberId, $currentFY);

        foreach ($calc['details'] as $detail) {
            if ($detail['in_grace'] ?? false) continue;
            if (bccomp($detail['fine_amount'], '0', 2) <= 0) continue;

            self::distributeFineForYear($db, $memberId, $detail['financial_year'], $detail['fine_amount']);
        }

        return $calc;
    }

    private static function distributeFineForYear($db, int $memberId, string $financialYear, string $totalFine): void
    {
        $rows = $db->select(
            "SELECT id, total_amount, paid_amount, fine_amount FROM subscriptions WHERE member_id = ? AND financial_year = ? AND status IN ('pending','overdue')",
            [$memberId, $financialYear]
        );
        if (empty($rows)) return;

        $yearTotal = '0.00';
        foreach ($rows as $r) {
            $yearTotal = bcadd($yearTotal, $r['total_amount'], 2);
        }

        $fineDistributed = '0.00';
        $lastIdx = count($rows) - 1;
        foreach ($rows as $idx => $r) {
            if ($idx === $lastIdx) {
                $rowFine = bcsub($totalFine, $fineDistributed, 2);
            } else {
                $rowFine = (bccomp($yearTotal, '0', 2) > 0)
                    ? bcdiv(bcmul($r['total_amount'], $totalFine, 4), $yearTotal, 2)
                    : '0.00';
            }
            $fineDistributed = bcadd($fineDistributed, $rowFine, 2);

            // Only update if fine actually changed to avoid unnecessary writes
            if (bccomp($rowFine, (string) ($r['fine_amount'] ?? '0'), 2) !== 0) {
                $db->query(
                    "UPDATE subscriptions SET fine_amount = ?, status = 'overdue', updated_at = NOW() WHERE id = ?",
                    [$rowFine, (int) $r['id']]
                );
            }
        }
    }

    public static function expireReinstatements(): array
    {
        $db = App::getInstance()->db();
        $results = ['expired' => 0];

        $windowData = \App\Modules\Rules\Services\RuleEngine::get('REINSTATEMENT_WINDOW');
        $windowMonths = (int) ($windowData['months'] ?? 12);

        $expired = $db->select(
            "SELECT id, full_name_ar FROM members
             WHERE status = 'dropped'
               AND updated_at < DATE_SUB(NOW(), INTERVAL ? MONTH)
               AND status != 'permanently_dropped'",
            [$windowMonths]
        );

        foreach ($expired as $m) {
            $db->query(
                "UPDATE members SET status = 'permanently_dropped', updated_at = NOW() WHERE id = ?",
                [(int) $m['id']]
            );
            $results['expired']++;

            EventBus::dispatch('member.permanently_dropped', [
                'member_id' => (int) $m['id'],
                'reason'    => 'انتهاء مهلة إعادة العضوية (' . $windowMonths . ' شهر) بعد إسقاط العضوية',
            ]);
        }

        return $results;
    }

    private static function currentFinancialYear(): string
    {
        $month = (int) date('n');
        $year = (int) date('Y');
        return $month >= 7 ? $year . '/' . ($year + 1) : ($year - 1) . '/' . $year;
    }
}
