<?php
declare(strict_types=1);

namespace App\Modules\Payments\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\Members\Services\MembershipPaymentGuard;

/**
 * Orchestrates payment lifecycle events (completion, void, cancellation)
 * with membership status transitions via MembershipPaymentGuard.
 *
 * This service is the ONLY place that connects payment state changes
 * to membership activation/deactivation. The EventBus listeners in
 * Cashier/bootstrap.php delegate here — they don't contain logic.
 */
final class PaymentLifecycleService
{
    /**
     * Handle a completed membership/down_payment — activate member + dependents.
     */
    public static function onMembershipPaymentCompleted(int $memberId, int $paymentId, string $paymentType, array $requestData = []): array
    {
        if ($paymentType === 'down_payment') {
            $result = self::transitionToPendingCheques($memberId, $paymentId);
            if ($result['success']) {
                self::createInstallmentPlan($memberId, $paymentId, $requestData);
            }
            return $result;
        }

        $result = MembershipPaymentGuard::activateMember($memberId, $paymentId);

        if ($result['success']) {
            MembershipPaymentGuard::activateIncludedDependents($memberId, $paymentId);
        }

        return $result;
    }

    /**
     * Transition member to pending_cheques after installment down-payment.
     * Member stays in this state until all cheques are submitted via ChequeService.
     */
    private static function transitionToPendingCheques(int $memberId, int $paymentId): array
    {
        $db = App::getInstance()->db();
        $member = $db->selectOne("SELECT * FROM members WHERE id = ? AND is_archived = 0", [$memberId]);
        if (!$member) {
            return ['success' => false, 'error' => 'العضو غير موجود'];
        }

        $validStatuses = ['potential', 'payment_pending', 'accepted', 'under_review'];
        if (!in_array($member['status'], $validStatuses, true)) {
            if ($member['status'] === 'pending_cheques') {
                return ['success' => true, 'already_pending_cheques' => true];
            }
            if ($member['status'] === 'active') {
                return ['success' => true, 'already_active' => true];
            }
            return ['success' => false, 'error' => 'حالة العضو لا تسمح بالانتقال: ' . $member['status']];
        }

        $db->update('members', [
            'status'                 => 'pending_cheques',
            'activated_by_payment_id'=> $paymentId,
            'updated_at'             => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$memberId]);

        Logger::info("PaymentLifecycleService: member #{$memberId} transitioned to pending_cheques after down-payment #{$paymentId}");
        return ['success' => true];
    }

    /**
     * Handle a completed addition_fee — activate the specific dependent.
     */
    public static function onAdditionFeeCompleted(string $entityType, int $entityId, int $memberId, int $paymentId): array
    {
        $validTables = ['spouses', 'children', 'temporary_members'];
        if (!in_array($entityType, $validTables, true)) {
            return ['success' => false, 'error' => 'نوع الملحق غير صالح'];
        }

        return MembershipPaymentGuard::activateDependent($entityType, $entityId, $memberId, $paymentId);
    }

    /**
     * Handle payment void — deactivate member/dependent if no replacement payment exists.
     * Called AFTER PaymentService::voidPayment() has already voided the record.
     */
    public static function onPaymentVoided(int $paymentId, int $memberId, string $paymentType, ?string $entityType = null, ?int $entityId = null): void
    {
        if (in_array($paymentType, ['membership_fee', 'down_payment'], true)) {
            MembershipPaymentGuard::deactivateMember($memberId, $paymentId);
            return;
        }

        if ($paymentType === 'addition_fee' && $entityType && $entityId > 0) {
            $validTables = ['spouses', 'children', 'temporary_members'];
            if (in_array($entityType, $validTables, true)) {
                MembershipPaymentGuard::deactivateDependent($entityType, $entityId, $paymentId);
            }
            return;
        }

        self::revertLifeEventOnVoid($paymentType, $entityType, $entityId);
    }

    /**
     * Handle payment request cancellation (from cashier queue).
     * If the request was already completed, its linked payment is voided first
     * by PaymentRequestService::cancelRequest(), which triggers payment.voided.
     * This handles the case where a pending/processing request is cancelled
     * before any payment was made — no membership change needed, but life-event
     * status may need reverting.
     */
    public static function onPaymentRequestCancelled(array $data): void
    {
        $paymentType = $data['payment_type'] ?? '';
        $entityType = $data['related_entity_type'] ?? null;
        $entityId = (int) ($data['related_entity_id'] ?? 0);
        $memberId = (int) ($data['member_id'] ?? 0);

        if ($paymentType === 'addition_fee' && $entityType && $entityId > 0) {
            self::archiveCancelledDependent($entityType, $entityId, $memberId);
            return;
        }

        self::revertLifeEventOnVoid($paymentType, $entityType, $entityId);
    }

    /**
     * Archive a dependent whose addition fee was cancelled.
     * The member addition is considered withdrawn — archive the record.
     */
    private static function archiveCancelledDependent(string $entityType, int $entityId, int $memberId): void
    {
        $validTables = ['spouses', 'children', 'temporary_members'];
        if (!in_array($entityType, $validTables, true)) {
            return;
        }

        $db = App::getInstance()->db();
        $entity = $db->selectOne(
            "SELECT id, status FROM `{$entityType}` WHERE id = ? AND member_id = ? AND is_archived = 0",
            [$entityId, $memberId]
        );

        if (!$entity) {
            return;
        }

        // Only archive if still pending — if active via another payment, leave it
        if (!in_array($entity['status'], ['pending_payment', 'inactive'], true)) {
            return;
        }

        $employee = App::getInstance()->currentEmployee();
        $db->update($entityType, [
            'is_archived' => 1,
            'status'      => 'inactive',
            'archived_at' => date('Y-m-d H:i:s'),
            'archived_by' => $employee ? (int) $employee->id : null,
            'updated_at'  => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$entityId]);

        Logger::info("PaymentLifecycleService: archived {$entityType} #{$entityId} after addition_fee cancelled for member #{$memberId}");
    }

    /**
     * Revert life-event entity status when its fee payment is voided/cancelled.
     */
    private static function revertLifeEventOnVoid(string $paymentType, ?string $entityType, ?int $entityId): void
    {
        if (!$entityId || $entityId <= 0) return;

        $db = App::getInstance()->db();
        $entityRevertMap = [
            'separation_fee' => 'transfer_requests',
            'divorce_fee'    => 'divorce_cases',
            'death_fee'      => 'death_cases',
            'waiver_fee'     => 'waiver_requests',
        ];

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

            Logger::info("PaymentLifecycleService: reverted {$table} #{$entityId} to 'requested' after void/cancel");
        }
    }

    /**
     * Create installment plan for down_payment.
     */
    private static function createInstallmentPlan(int $memberId, int $paymentId, array $requestData): void
    {
        $db = App::getInstance()->db();

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

        $amount = (string) $payment['amount'];
        $member = $db->selectOne("SELECT membership_value FROM members WHERE id = ?", [$memberId]);
        if (!$member) return;

        $membershipValue = $member['membership_value'] ?? '0.00';
        $remaining = bcsub($membershipValue, $amount, 2);

        if (bccomp($remaining, '0', 2) <= 0) return;

        $months = min(30, max(1, (int) ($requestData['installment_months'] ?? 30)));

        $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,
        ]);

        Logger::info("PaymentLifecycleService: installment plan created", ['plan_id' => $planId, 'member_id' => $memberId]);
    }
}
