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

        // Resolve installment terms from offer snapshot (if any) or defaults
        $offerSnapshot = null;
        $boardOfferId = null;
        $graceMonths = 0;
        $postGraceRate = null;

        if (!empty($requestData['offer_snapshot_json'])) {
            $offerSnapshot = \App\Modules\Members\Services\BoardOfferService::restoreFromSnapshot($requestData['offer_snapshot_json']);
            $boardOfferId = (int) ($requestData['board_offer_id'] ?? $offerSnapshot['id'] ?? 0) ?: null;
        }

        $offerOverrides = [];
        if ($offerSnapshot) {
            if ($offerSnapshot['inst_months'] !== null) $offerOverrides['max_months'] = (int) $offerSnapshot['inst_months'];
            if ($offerSnapshot['inst_interest_rate'] !== null) $offerOverrides['interest_rate'] = (string) $offerSnapshot['inst_interest_rate'];
            if ($offerSnapshot['inst_down_payment_pct'] !== null) $offerOverrides['min_down_pct'] = (string) $offerSnapshot['inst_down_payment_pct'];
            if ($offerSnapshot['inst_grace_type'] !== null) {
                $offerOverrides['grace_type'] = $offerSnapshot['inst_grace_type'];
                $offerOverrides['grace_months'] = (int) $offerSnapshot['inst_grace_months'];
                $graceMonths = (int) $offerSnapshot['inst_grace_months'];
                if ($offerSnapshot['inst_post_grace_rate'] !== null) {
                    $offerOverrides['post_grace_rate'] = (string) $offerSnapshot['inst_post_grace_rate'];
                    $postGraceRate = (string) $offerSnapshot['inst_post_grace_rate'];
                }
            }
        }

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

        $calc = \App\Modules\Installments\Services\InstallmentCalculator::calculate(
            $membershipValue, $amount, $months, date('Y-m-d'), $offerOverrides
        );

        if (!($calc['success'] ?? false)) {
            Logger::error("PaymentLifecycleService: installment calc failed", ['errors' => $calc['errors'] ?? [], 'member_id' => $memberId]);
            return;
        }

        $planId = $db->insert('installment_plans', [
            'member_id'                  => $memberId,
            'related_entity_type'        => 'members',
            'related_entity_id'          => $memberId,
            'total_amount'               => $membershipValue,
            'down_payment'               => $amount,
            'remaining_balance'          => $calc['remaining_balance'],
            'interest_rate'              => $calc['interest_rate'],
            'total_interest'             => $calc['total_interest'],
            'total_with_interest'        => $calc['total_with_interest'],
            'number_of_months'           => $months,
            'monthly_payment'            => $calc['monthly_payment'],
            'start_date'                 => date('Y-m-d'),
            'status'                     => 'active',
            'board_offer_id'             => $boardOfferId,
            'grace_months'               => $graceMonths,
            'post_grace_interest_rate'   => $postGraceRate,
            'created_at'                 => date('Y-m-d H:i:s'),
            'updated_at'                 => date('Y-m-d H:i:s'),
        ]);

        foreach ($calc['schedule'] as $row) {
            $db->insert('installment_schedule', [
                'installment_plan_id' => $planId,
                'installment_number'  => $row['number'],
                'due_date'            => $row['due_date'],
                'amount'              => $row['amount'],
                'principal'           => $row['principal'],
                'interest'            => $row['interest'],
                'remaining_after'     => $row['remaining_after'],
                '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' => $calc['total_with_interest'],
        ]);

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