<?php
declare(strict_types=1);

use App\Core\Registries\MenuRegistry;
use App\Core\Registries\PermissionRegistry;
use App\Core\EventBus;

MenuRegistry::register('cashier', [
    'label_ar'   => 'الخزينة',
    'label_en'   => 'Cashier',
    'icon'       => 'credit-card',
    'route'      => '/cashier',
    'permission' => 'cashier.view_queue',
    'parent'     => null,
    'order'      => 250,
    'children'   => [
        ['label_ar' => 'طابور الدفع', 'label_en' => 'Payment Queue', 'route' => '/cashier', 'permission' => 'cashier.view_queue', 'order' => 1],
    ],
]);

PermissionRegistry::register('cashier', [
    'cashier.view_queue'      => ['ar' => 'عرض طابور الخزينة',  'en' => 'View Cashier Queue'],
    'cashier.process_payment' => ['ar' => 'معالجة طلب دفع',     'en' => 'Process Payment Request'],
    'cashier.cancel_request'  => ['ar' => 'إلغاء طلب دفع',      'en' => 'Cancel Payment Request'],
]);

// When a payment is voided, revert entity status back to pending_payment
EventBus::listen('payment.voided', function (array $data) {
    try {
        $db = \App\Core\App::getInstance()->db();
        $paymentId = (int) ($data['payment_id'] ?? 0);
        $memberId = (int) ($data['member_id'] ?? 0);
        $paymentType = $data['payment_type'] ?? '';

        if ($paymentId <= 0 || $memberId <= 0) return;

        $payment = $db->selectOne("SELECT * FROM payments WHERE id = ?", [$paymentId]);
        if (!$payment) return;

        $entityType = $payment['related_entity_type'] ?? null;
        $entityId = (int) ($payment['related_entity_id'] ?? 0);

        if ($paymentType === 'addition_fee' && $entityType && $entityId > 0) {
            $validTables = ['spouses', 'children', 'temporary_members'];
            if (in_array($entityType, $validTables, true)) {
                $db->update($entityType, [
                    'status'     => 'pending_payment',
                    'updated_at' => date('Y-m-d H:i:s'),
                ], '`id` = ? AND is_archived = 0', [$entityId]);
                \App\Core\Logger::info("Reverted {$entityType} #{$entityId} to pending_payment after payment void");
            }
        }

        if (in_array($paymentType, ['membership_fee', 'down_payment'], true)) {
            $otherPayment = $db->selectOne(
                "SELECT id FROM payments WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment') AND is_voided = 0 AND id != ? LIMIT 1",
                [$memberId, $paymentId]
            );
            if (!$otherPayment) {
                $member = \App\Modules\Members\Models\Member::find($memberId);
                if ($member && in_array($member->status, ['active', 'frozen', 'suspended'], true)) {
                    $member->update([
                        'status' => 'payment_pending',
                        'membership_number' => null,
                    ]);
                    foreach (['spouses', 'children', 'temporary_members'] as $depTable) {
                        $db->query(
                            "UPDATE `{$depTable}` SET status = 'pending_payment', join_date = NULL, updated_at = NOW() WHERE member_id = ? AND status IN ('active','frozen') AND is_archived = 0",
                            [$memberId]
                        );
                    }
                    \App\Core\Logger::info("Reverted member #{$memberId} to payment_pending after membership payment void");
                }
            }
        }
    } catch (\Throwable $e) {
        \App\Core\Logger::error("payment.voided listener failed: " . $e->getMessage(), ['data' => $data]);
    }
});

EventBus::listen('payment_request.completed', function (array $data) {
    try {
        $db = \App\Core\App::getInstance()->db();
        $paymentType = $data['payment_type'] ?? '';
        $memberId = (int) ($data['member_id'] ?? 0);
        $requestId = (int) ($data['request_id'] ?? 0);

        if ($memberId <= 0) return;

        if (in_array($paymentType, ['membership_fee', 'down_payment'], true)) {
            $member = \App\Modules\Members\Models\Member::find($memberId);
            if (!$member) return;

            if (in_array($member->status, ['potential', 'payment_pending', 'accepted', 'under_review'], true)) {
                $membershipNumber = \App\Modules\Members\Services\MemberNumberGenerator::assign($memberId);
                $member->update(['status' => 'active']);
            } else {
                $membershipNumber = $member->membership_number;
            }

            if ($paymentType === 'down_payment') {
                $request = $db->selectOne("SELECT * FROM payment_requests WHERE id = ?", [$requestId]);
                $notesData = $request && $request['notes'] ? json_decode($request['notes'], true) : [];
                $months = min(30, max(1, (int) ($notesData['installment_months'] ?? 30)));
                $amount = (string) ($data['amount'] ?? '0.00');
                $membershipValue = $member->membership_value ?? '0.00';
                $remaining = bcsub($membershipValue, $amount, 2);

                if (bccomp($remaining, '0', 2) > 0) {
                    $interestRateData = \App\Modules\Rules\Services\RuleEngine::get('INSTALLMENT_INTEREST_RATE');
                    $interestRate = $interestRateData['percentage'] ?? '22.00';
                    $totalInterest = bcdiv(bcmul($remaining, $interestRate, 4), '100', 2);
                    $totalWithInterest = bcadd($remaining, $totalInterest, 2);
                    $monthlyPayment = bcdiv($totalWithInterest, (string) $months, 2);

                    $planId = $db->insert('installment_plans', [
                        'member_id' => $memberId, 'related_entity_type' => 'members', 'related_entity_id' => $memberId,
                        'total_amount' => $membershipValue, 'down_payment' => $amount,
                        'remaining_balance' => $remaining, 'interest_rate' => $interestRate,
                        'total_interest' => $totalInterest, 'total_with_interest' => $totalWithInterest,
                        'number_of_months' => $months, 'monthly_payment' => $monthlyPayment,
                        'start_date' => date('Y-m-d'), 'status' => 'active',
                        'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
                    ]);

                    $rem = $totalWithInterest;
                    for ($i = 1; $i <= $months; $i++) {
                        $principal = bcdiv($remaining, (string) $months, 2);
                        $interest = bcdiv($totalInterest, (string) $months, 2);
                        $instAmount = bcadd($principal, $interest, 2);
                        $rem = bcsub($rem, $instAmount, 2);
                        if (bccomp($rem, '0', 2) < 0) $rem = '0.00';
                        $db->insert('installment_schedule', [
                            'installment_plan_id' => $planId, 'installment_number' => $i,
                            'due_date' => date('Y-m-d', strtotime("+{$i} months")),
                            'amount' => $instAmount, 'principal' => $principal, 'interest' => $interest,
                            'remaining_after' => $rem, 'paid_amount' => '0.00', 'status' => 'pending',
                            'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
                        ]);
                    }

                    EventBus::dispatch('installment.plan_created', [
                        'plan_id' => $planId, 'member_id' => $memberId, 'total_amount' => $totalWithInterest,
                    ]);
                }
            }

            // Activate all pending_payment dependants included in the lump sum
            foreach (['spouses', 'children', 'temporary_members'] as $depTable) {
                try {
                    $pendingDeps = $db->select(
                        "SELECT id FROM `{$depTable}` WHERE member_id = ? AND status = 'pending_payment' AND is_archived = 0",
                        [$memberId]
                    );
                    foreach ($pendingDeps as $dep) {
                        $hasSeparateRequest = $db->selectOne(
                            "SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND status IN ('pending','processing') AND is_voided = 0 LIMIT 1",
                            [$memberId, $depTable, (int) $dep['id']]
                        );
                        if (!$hasSeparateRequest) {
                            $updateData = [
                                'status'     => 'active',
                                'join_date'  => date('Y-m-d'),
                                'updated_at' => date('Y-m-d H:i:s'),
                            ];
                            $db->update($depTable, $updateData, '`id` = ?', [(int) $dep['id']]);
                        }
                    }
                } catch (\Throwable $e) {
                    \App\Core\Logger::error("Failed to activate {$depTable} for member {$memberId}: " . $e->getMessage());
                }
            }

            EventBus::dispatch('member.activated', ['member_id' => $memberId, 'membership_number' => $membershipNumber]);
            \App\Core\Logger::info("Member activated via cashier", ['member_id' => $memberId, 'membership_number' => $membershipNumber]);
        }

        $entityType = $data['related_entity_type'] ?? null;
        $entityId = (int) ($data['related_entity_id'] ?? 0);
        $paymentId = (int) ($data['payment_id'] ?? 0);

        if ($entityType && $entityId > 0) {
            $tableMap = [
                'divorce_fee'    => ['table' => 'divorce_cases',    'event' => 'divorce.fee_paid',  'key' => 'case_id'],
                'death_fee'      => ['table' => 'death_cases',      'event' => 'death.fee_paid',    'key' => 'case_id'],
                'waiver_fee'     => ['table' => 'waiver_requests',  'event' => 'waiver.fee_paid',   'key' => 'waiver_id'],
                'separation_fee' => ['table' => 'transfer_requests','event' => 'transfer.fee_paid', 'key' => 'transfer_id'],
            ];

            if (isset($tableMap[$paymentType])) {
                $cfg = $tableMap[$paymentType];
                $db->update($cfg['table'], [
                    'status'     => 'fee_paid',
                    'updated_at' => date('Y-m-d H:i:s'),
                ], '`id` = ?', [$entityId]);

                EventBus::dispatch($cfg['event'], [$cfg['key'] => $entityId, 'payment_id' => $paymentId]);
                \App\Core\Logger::info("Life-event fee paid via cashier", ['type' => $paymentType, 'entity_id' => $entityId]);
            }

            $receiptNumber = $data['receipt_number'] ?? '';

            if ($paymentType === 'addition_fee' && $entityType === 'spouses') {
                $db->update('spouses', [
                    'status'             => 'active',
                    'join_date'          => date('Y-m-d'),
                    'fee_receipt_number' => $receiptNumber ?: null,
                    'updated_at'         => date('Y-m-d H:i:s'),
                ], '`id` = ?', [$entityId]);
                EventBus::dispatch('spouse.fee_paid', ['spouse_id' => $entityId, 'member_id' => $memberId, 'payment_id' => $paymentId]);
                \App\Core\Logger::info("Spouse activated via cashier", ['spouse_id' => $entityId]);
            }

            if ($paymentType === 'addition_fee' && $entityType === 'children') {
                $db->update('children', [
                    'status'             => 'active',
                    'join_date'          => date('Y-m-d'),
                    'fee_receipt_number' => $receiptNumber ?: null,
                    'updated_at'         => date('Y-m-d H:i:s'),
                ], '`id` = ?', [$entityId]);
                EventBus::dispatch('child.fee_paid', ['child_id' => $entityId, 'member_id' => $memberId, 'payment_id' => $paymentId]);
                \App\Core\Logger::info("Child activated via cashier", ['child_id' => $entityId]);
            }

            if ($paymentType === 'seasonal_fee' && $entityType === 'seasonal_memberships') {
                $db->update('seasonal_memberships', [
                    'status'     => 'active',
                    'updated_at' => date('Y-m-d H:i:s'),
                ], '`id` = ?', [$entityId]);
                EventBus::dispatch('seasonal.fee_paid', ['seasonal_id' => $entityId, 'member_id' => $memberId, 'payment_id' => $paymentId]);
                \App\Core\Logger::info("Seasonal membership activated via cashier", ['seasonal_id' => $entityId]);
            }

            if ($paymentType === 'addition_fee' && $entityType === 'temporary_members') {
                $db->update('temporary_members', [
                    'status'             => 'active',
                    'join_date'          => date('Y-m-d'),
                    'fee_receipt_number' => $receiptNumber ?: null,
                    'updated_at'         => date('Y-m-d H:i:s'),
                ], '`id` = ?', [$entityId]);
                EventBus::dispatch('temporary.fee_paid', ['temp_id' => $entityId, 'member_id' => $memberId, 'payment_id' => $paymentId]);
                \App\Core\Logger::info("Temporary member activated via cashier", ['temp_id' => $entityId]);
            }
        }
    } catch (\Throwable $e) {
        \App\Core\Logger::error("payment_request.completed listener failed: " . $e->getMessage(), ['data' => $data]);
    }
});
