<?php
declare(strict_types=1);

namespace App\Modules\Members\Services;

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

/**
 * Single source of truth for membership activation/deactivation.
 *
 * INVARIANT: A member is "active" if and only if they have at least one
 * non-voided payment of type membership_fee or down_payment.
 * A dependent is "active" if and only if their parent member is active AND
 * (they were included in the membership payment OR they have their own
 * non-voided addition_fee payment).
 *
 * No other code path should set status = 'active' on members or dependents.
 * This service is the ONLY authority for these transitions.
 */
final class MembershipPaymentGuard
{
    /**
     * Activate a member after confirmed payment.
     * Called ONLY by the payment completion flow.
     */
    public static function activateMember(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', 'pending_cheques'];
        if (!in_array($member['status'], $validStatuses, true)) {
            if ($member['status'] === 'active') {
                return ['success' => true, 'already_active' => true];
            }
            return ['success' => false, 'error' => 'حالة العضو لا تسمح بالتفعيل: ' . $member['status']];
        }

        $membershipNumber = $member['membership_number'];
        if (!$membershipNumber) {
            $membershipNumber = MemberNumberGenerator::assign($memberId);
        }

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

        Logger::info("MembershipPaymentGuard: member #{$memberId} activated by payment #{$paymentId}");
        return ['success' => true, 'membership_number' => $membershipNumber];
    }

    /**
     * Deactivate a member when their payment is voided/cancelled.
     * Verifies no other valid payment exists before revoking.
     */
    public static function deactivateMember(int $memberId, int $voidedPaymentId): array
    {
        $db = App::getInstance()->db();
        $member = $db->selectOne("SELECT * FROM members WHERE id = ? AND is_archived = 0", [$memberId]);
        if (!$member) {
            return ['success' => false, 'error' => 'العضو غير موجود'];
        }

        if (!in_array($member['status'], ['active', 'frozen', 'suspended'], true)) {
            return ['success' => true, 'already_inactive' => true];
        }

        $otherValidPayment = $db->selectOne(
            "SELECT id FROM payments WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment','foreign_membership_fee','sports_membership_fee','seasonal_fee') AND is_voided = 0 AND id != ? LIMIT 1",
            [$memberId, $voidedPaymentId]
        );

        if ($otherValidPayment) {
            return ['success' => true, 'still_covered' => true];
        }

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

        self::deactivateAllDependents($memberId);

        Logger::info("MembershipPaymentGuard: member #{$memberId} deactivated — payment #{$voidedPaymentId} voided, no replacement found");
        return ['success' => true, 'deactivated' => true];
    }

    /**
     * Activate a dependent (spouse/child/temp) after their fee is paid.
     */
    public static function activateDependent(string $table, int $entityId, int $memberId, int $paymentId): array
    {
        $db = App::getInstance()->db();
        $validTables = ['spouses', 'children', 'temporary_members'];
        if (!in_array($table, $validTables, true)) {
            return ['success' => false, 'error' => 'نوع الملحق غير صالح'];
        }

        $member = $db->selectOne("SELECT status FROM members WHERE id = ? AND is_archived = 0", [$memberId]);
        if (!$member || $member['status'] !== 'active') {
            return ['success' => false, 'error' => 'لا يمكن تفعيل ملحق لعضو غير نشط'];
        }

        $entity = $db->selectOne("SELECT id, status FROM `{$table}` WHERE id = ? AND member_id = ? AND is_archived = 0", [$entityId, $memberId]);
        if (!$entity) {
            return ['success' => false, 'error' => 'الملحق غير موجود'];
        }

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

        Logger::info("MembershipPaymentGuard: {$table} #{$entityId} activated by payment #{$paymentId}");
        return ['success' => true];
    }

    /**
     * Deactivate a specific dependent when their addition_fee is voided.
     */
    public static function deactivateDependent(string $table, int $entityId, int $voidedPaymentId): array
    {
        $db = App::getInstance()->db();
        $validTables = ['spouses', 'children', 'temporary_members'];
        if (!in_array($table, $validTables, true)) {
            return ['success' => false, 'error' => 'نوع الملحق غير صالح'];
        }

        $entity = $db->selectOne("SELECT id, member_id FROM `{$table}` WHERE id = ? AND is_archived = 0", [$entityId]);
        if (!$entity) {
            return ['success' => true, 'not_found' => true];
        }

        $otherValid = $db->selectOne(
            "SELECT id FROM payments WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND is_voided = 0 AND id != ? LIMIT 1",
            [(int) $entity['member_id'], $table, $entityId, $voidedPaymentId]
        );

        if ($otherValid) {
            return ['success' => true, 'still_covered' => true];
        }

        $wasIncludedInMembership = self::wasIncludedInMembershipPayment($table, $entityId, (int) $entity['member_id']);
        if ($wasIncludedInMembership) {
            return ['success' => true, 'included_in_membership' => true];
        }

        $db->update($table, [
            'status'                 => 'pending_payment',
            'join_date'              => null,
            'activated_by_payment_id'=> null,
            'updated_at'             => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$entityId]);

        Logger::info("MembershipPaymentGuard: {$table} #{$entityId} deactivated — payment #{$voidedPaymentId} voided");
        return ['success' => true, 'deactivated' => true];
    }

    /**
     * Activate all pending dependents included in the membership payment (lump sum).
     * These don't have their own addition_fee — they're included for free.
     */
    public static function activateIncludedDependents(int $memberId, int $paymentId): void
    {
        $db = App::getInstance()->db();

        foreach (['spouses', 'children', 'temporary_members'] as $table) {
            $pending = $db->select(
                "SELECT id FROM `{$table}` WHERE member_id = ? AND status = 'pending_payment' AND is_archived = 0",
                [$memberId]
            );
            foreach ($pending 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, $table, (int) $dep['id']]
                );
                if (!$hasSeparateRequest) {
                    $db->update($table, [
                        'status'                 => 'active',
                        'join_date'              => date('Y-m-d'),
                        'activated_by_payment_id'=> $paymentId,
                        'updated_at'             => date('Y-m-d H:i:s'),
                    ], '`id` = ?', [(int) $dep['id']]);
                }
            }
        }
    }

    /**
     * Deactivate ALL dependents for a member (used when member itself is deactivated).
     */
    private static function deactivateAllDependents(int $memberId): void
    {
        $db = App::getInstance()->db();
        foreach (['spouses', 'children', 'temporary_members'] as $table) {
            $db->query(
                "UPDATE `{$table}` SET status = 'pending_payment', join_date = NULL, activated_by_payment_id = NULL, updated_at = NOW() WHERE member_id = ? AND status IN ('active','frozen') AND is_archived = 0",
                [$memberId]
            );
        }
    }

    /**
     * Check if a dependent was activated as part of the membership fee (no separate addition_fee).
     */
    private static function wasIncludedInMembershipPayment(string $table, int $entityId, int $memberId): bool
    {
        $db = App::getInstance()->db();
        $entity = $db->selectOne("SELECT activated_by_payment_id FROM `{$table}` WHERE id = ?", [$entityId]);
        if (!$entity || !$entity['activated_by_payment_id']) {
            return false;
        }

        $payment = $db->selectOne(
            "SELECT payment_type FROM payments WHERE id = ? AND is_voided = 0",
            [(int) $entity['activated_by_payment_id']]
        );
        return $payment && in_array($payment['payment_type'], ['membership_fee', 'down_payment', 'foreign_membership_fee', 'sports_membership_fee', 'seasonal_fee'], true);
    }

    /**
     * Re-evaluate a member's status based on current payment state.
     * Used as a safety net — should rarely change anything if the system is working correctly.
     *
     * Recognizes multiple activation sources:
     * - membership_fee / down_payment (standard path)
     * - death_fee (death transfer — wife inherits membership)
     * - separation_fee (membership transfer)
     */
    public static function reconcile(int $memberId): array
    {
        $db = App::getInstance()->db();
        $member = $db->selectOne("SELECT * FROM members WHERE id = ? AND is_archived = 0", [$memberId]);
        if (!$member) {
            return ['success' => false, 'error' => 'العضو غير موجود'];
        }

        if (($member['membership_type'] ?? 'working') === 'honorary') {
            return ['success' => true, 'changes' => []];
        }

        $hasValidPayment = $db->selectOne(
            "SELECT id FROM payments WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment','foreign_membership_fee','sports_membership_fee','seasonal_fee') AND is_voided = 0 LIMIT 1",
            [$memberId]
        );

        // Also check if this member was activated via death transfer or membership transfer
        if (!$hasValidPayment) {
            $hasValidPayment = $db->selectOne(
                "SELECT p.id FROM payments p
                 JOIN death_cases dc ON dc.member_id = p.member_id AND p.related_entity_type = 'death_cases' AND p.related_entity_id = dc.id
                 WHERE dc.transferred_to_member_id = ? AND p.payment_type = 'death_fee' AND p.is_voided = 0 LIMIT 1",
                [$memberId]
            );
        }

        // Check if activated_by_payment_id references a valid non-voided payment
        if (!$hasValidPayment && $member['activated_by_payment_id']) {
            $hasValidPayment = $db->selectOne(
                "SELECT id FROM payments WHERE id = ? AND is_voided = 0 LIMIT 1",
                [(int) $member['activated_by_payment_id']]
            );
        }

        $changes = [];

        if ($hasValidPayment && $member['status'] === 'payment_pending') {
            $db->update('members', [
                'status'                 => 'active',
                'activated_by_payment_id'=> (int) $hasValidPayment['id'],
                'activated_at'           => date('Y-m-d H:i:s'),
                'updated_at'             => date('Y-m-d H:i:s'),
            ], '`id` = ?', [$memberId]);
            if (!$member['membership_number']) {
                MemberNumberGenerator::assign($memberId);
            }
            $changes[] = 'activated (had valid payment but was stuck at payment_pending)';
        }

        // pending_cheques is valid: member paid down_payment but hasn't submitted cheques yet
        // Do NOT auto-activate — cheques must be submitted first via ChequeService
        if (!$hasValidPayment && $member['status'] === 'pending_cheques') {
            $db->update('members', [
                'status'     => 'payment_pending',
                'updated_at' => date('Y-m-d H:i:s'),
            ], '`id` = ?', [$memberId]);
            $changes[] = 'reverted to payment_pending (was pending_cheques but no valid down_payment found)';
        }

        if (!$hasValidPayment && $member['status'] === 'active') {
            $db->update('members', [
                'status'                 => 'payment_pending',
                'membership_number'      => null,
                'activated_by_payment_id'=> null,
                'activated_at'           => null,
                'updated_at'             => date('Y-m-d H:i:s'),
            ], '`id` = ?', [$memberId]);
            self::deactivateAllDependents($memberId);
            $changes[] = 'deactivated (was active but no valid payment found)';
        }

        if (!empty($changes)) {
            Logger::info("MembershipPaymentGuard::reconcile member #{$memberId}", ['changes' => $changes]);
        }

        return ['success' => true, 'changes' => $changes];
    }
}
