<?php
declare(strict_types=1);

namespace App\Modules\Transfers\Services;

use App\Core\App;
use App\Modules\Rules\Services\RuleEngine;
use App\Modules\Pricing\Services\PricingEngine;
use App\Modules\ServiceCatalog\Models\ServicePrice;

final class SeparationFeeCalculator
{
    /**
     * Calculate separation/transfer fee based on years since acquisition.
     */
    public static function calculate(int $sourceMemberId, ?int $childId = null, ?string $qualificationCode = null): array
    {
        $db = App::getInstance()->db();
        $member = $db->selectOne("SELECT * FROM members WHERE id = ?", [$sourceMemberId]);
        if (!$member) {
            return ['error' => 'العضو غير موجود', 'success' => false];
        }

        // Determine qualification code
        if ($qualificationCode === null && !empty($member['qualification_id'])) {
            $qual = $db->selectOne("SELECT code FROM qualifications WHERE id = ?", [(int) $member['qualification_id']]);
            $qualificationCode = $qual['code'] ?? null;
        }

        // Get new membership value at current date from pricing_configs
        $branchId = (int) ($member['branch_id'] ?? 1);
        $mType = $member['membership_type'] ?? 'working';
        $newMembershipValue = '0.00';

        if ($qualificationCode) {
            $priceInfo = PricingEngine::getMembershipPrice($branchId, $qualificationCode);
            $newMembershipValue = $priceInfo['price'] ?? '0.00';
        }

        // Fallback: query pricing_configs directly (handles NULL qualification or membership_type mismatch)
        if (bccomp($newMembershipValue, '0.01', 2) < 0) {
            $qualId = !empty($member['qualification_id']) ? (int) $member['qualification_id'] : null;
            if ($qualId) {
                $pricing = $db->selectOne(
                    "SELECT price FROM pricing_configs WHERE branch_id = ? AND qualification_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY effective_from DESC LIMIT 1",
                    [$branchId, $qualId, $mType]
                );
            } else {
                $pricing = $db->selectOne(
                    "SELECT price FROM pricing_configs WHERE branch_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY price ASC LIMIT 1",
                    [$branchId, $mType]
                );
            }
            if ($pricing && bccomp($pricing['price'], '0.01', 2) >= 0) {
                $newMembershipValue = $pricing['price'];
            }
        }

        // Calculate years since acquisition
        $acquisitionDate = $member['membership_origin_date'] ?? $member['form_date'] ?? $member['created_at'] ?? date('Y-m-d');
        $yearsSince = self::calculateYearsSince($acquisitionDate);

        // Get fee percentage based on years
        $feePercentage = self::getFeePercentageByYear($yearsSince);

        // Calculate separation fee
        $separationFee = bcdiv(bcmul($newMembershipValue, (string) $feePercentage, 4), '100', 2);

        // Form fee
        $formFeeData = RuleEngine::get('FORM_TRANSFER_FEE');
        $formFee = $formFeeData['amount'] ?? '570.00';

        // Annual subscription: skip if member was activated after July 1 of current FY
        $activatedAt = $member['activated_at'] ?? $member['created_at'] ?? date('Y-m-d');
        $annualSubCovered = self::isCurrentYearSubscriptionCovered($activatedAt);

        $memberSub = ServicePrice::getPrice('SVC_ANNUAL_MEMBER', '492.00');
        $spouseSub = ServicePrice::getPrice('SVC_ANNUAL_SPOUSE', '492.00');
        $childSub  = ServicePrice::getPrice('SVC_ANNUAL_CHILD', '222.00');
        $tempSub   = ServicePrice::getPrice('SVC_ANNUAL_TEMP', '222.00');
        $devFeeData = RuleEngine::get('DEVELOPMENT_FEE');
        $devFee = $devFeeData['amount'] ?? '35.00';

        $spouseCount = (int) ($db->selectOne(
            "SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0 AND status = 'active'",
            [$sourceMemberId]
        )['cnt'] ?? 0);
        $childCount = (int) ($db->selectOne(
            "SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0 AND status = 'active'",
            [$sourceMemberId]
        )['cnt'] ?? 0);
        $tempCount = (int) ($db->selectOne(
            "SELECT COUNT(*) as cnt FROM temporary_members WHERE member_id = ? AND is_archived = 0 AND status = 'active'",
            [$sourceMemberId]
        )['cnt'] ?? 0);

        $familyTotal = $memberSub;
        $familyTotal = bcadd($familyTotal, bcmul($spouseSub, (string) $spouseCount, 2), 2);
        $familyTotal = bcadd($familyTotal, bcmul($childSub, (string) $childCount, 2), 2);
        $familyTotal = bcadd($familyTotal, bcmul($tempSub, (string) $tempCount, 2), 2);
        $annualSubscriptionFee = $annualSubCovered ? '0.00' : bcadd($familyTotal, $devFee, 2);

        $totalFee = bcadd(bcadd($separationFee, $formFee, 2), $annualSubscriptionFee, 2);

        return [
            'success'                   => true,
            'source_member_id'          => $sourceMemberId,
            'child_id'                  => $childId,
            'qualification_code'        => $qualificationCode,
            'new_membership_value'      => $newMembershipValue,
            'years_since_acquisition'   => $yearsSince,
            'fee_percentage'            => $feePercentage,
            'separation_fee'            => $separationFee,
            'form_fee'                  => $formFee,
            'annual_subscription_fee'   => $annualSubscriptionFee,
            'annual_sub_covered'        => $annualSubCovered,
            'total_fee'                 => $totalFee,
            'acquisition_date'          => $acquisitionDate,
        ];
    }

    /**
     * Returns true if the member's activation date falls on or after July 1 of the
     * current fiscal year — meaning the current-year subscription is already covered
     * by the membership fee and must NOT be charged again.
     *
     * Fiscal year: July 1 → June 30.
     */
    public static function isCurrentYearSubscriptionCovered(string $activatedAt): bool
    {
        $activated = new \DateTime(substr($activatedAt, 0, 10));
        $now = new \DateTime();
        $currentYear = (int) $now->format('Y');
        $currentMonth = (int) $now->format('n');

        $fyStart = $currentMonth >= 7
            ? new \DateTime("{$currentYear}-07-01")
            : new \DateTime(($currentYear - 1) . '-07-01');

        return $activated >= $fyStart;
    }

    public static function calculateYearsSince(string $date): int
    {
        $then = new \DateTime(substr($date, 0, 10));
        $now = new \DateTime();
        $diff = $now->diff($then);
        return max(1, $diff->y + ($diff->m > 0 || $diff->d > 0 ? 1 : 0)); // partial year rounds up
    }

    /**
     * فصل أبناء specific: floors elapsed years (never rounds up partial year).
     * Returns elapsed complete years between $acquisitionDate and $effectiveDate.
     * Minimum 0 (not 1) — the percentage table covers year 1+.
     */
    public static function calculateYearsFloor(string $acquisitionDate, string $effectiveDate): int
    {
        $from = new \DateTime(substr($acquisitionDate, 0, 10));
        $to   = new \DateTime(substr($effectiveDate, 0, 10));
        $diff = $to->diff($from);
        return max(1, $diff->y); // floor: only completed years; min 1 for fee-table lookup
    }

    public static function buildYearsBreakdown(string $acquisitionDate, string $effectiveDate, int $yearsSince, string $feePercentage): array
    {
        $from = new \DateTime(substr($acquisitionDate, 0, 10));
        $to   = new \DateTime(substr($effectiveDate, 0, 10));
        $diff = $to->diff($from);

        $years = [];
        $cursor = clone $from;
        for ($i = 1; $i <= $diff->y; $i++) {
            $yearStart = $cursor->format('Y-m-d');
            $cursor->modify('+1 year');
            $years[] = ['year' => $i, 'from' => $yearStart, 'to' => $cursor->format('Y-m-d')];
        }

        return [
            'acquisition_date'        => substr($acquisitionDate, 0, 10),
            'effective_transfer_date' => substr($effectiveDate, 0, 10),
            'diff_years'              => $diff->y,
            'diff_months'             => $diff->m,
            'diff_days'               => $diff->d,
            'applied_years'           => $yearsSince,
            'fee_percentage'          => $feePercentage,
            'year_ranges'             => $years,
        ];
    }

    /**
     * Calculate the effective transfer date for فصل أبناء:
     *   - If employed: effective = min(max(work_date, graduation_date), date_turned_25)
     *     Both work AND graduation must be completed; the later of the two is when
     *     both conditions are met. Then compare with date_turned_25 (take earlier).
     *   - If not employed: effective = date_turned_25
     *
     * @param string      $dobString        Child's date_of_birth (YYYY-MM-DD)
     * @param bool        $isEmployed
     * @param string|null $workDate         YYYY-MM-DD, required when employed
     * @param string|null $date25Override   User-supplied override for date_turned_25
     * @param string|null $graduationDate   YYYY-MM-DD, required when employed
     * @return array{date_turned_25: string, effective_transfer_date: string}
     */
    public static function computeChildSeparationDates(
        string $dobString,
        bool $isEmployed,
        ?string $workDate,
        ?string $date25Override = null,
        ?string $graduationDate = null
    ): array {
        $dob = new \DateTime(substr($dobString, 0, 10));
        $dob->modify('+25 years');
        $dateTurned25 = $date25Override ?? $dob->format('Y-m-d');

        if ($isEmployed && $workDate && $graduationDate) {
            $conditionMetDate = max($workDate, $graduationDate);
            $effective = min($conditionMetDate, $dateTurned25);
        } elseif ($isEmployed && $workDate) {
            $effective = min($workDate, $dateTurned25);
        } else {
            $effective = $dateTurned25;
        }

        return [
            'date_turned_25'          => $dateTurned25,
            'effective_transfer_date' => $effective,
        ];
    }

    /**
     * Full فصل أبناء fee calculation.
     * Uses floor years (not ceil) and effective_transfer_date as the end date.
     */
    public static function calculateForChildSeparation(
        int $sourceMemberId,
        int $childId,
        bool $isEmployed,
        ?string $workDate,
        string $effectiveTransferDate,
        ?string $qualificationCode = null,
        int $depSpouses = 0,
        int $depChildren = 0,
        int $depTemps = 0,
        int $foreignSpouseCount = 0
    ): array {
        $db = App::getInstance()->db();
        $member = $db->selectOne("SELECT * FROM members WHERE id = ?", [$sourceMemberId]);
        if (!$member) {
            return ['error' => 'العضو غير موجود', 'success' => false];
        }

        // Determine qualification code
        if ($qualificationCode === null && !empty($member['qualification_id'])) {
            $qual = $db->selectOne("SELECT code FROM qualifications WHERE id = ?", [(int) $member['qualification_id']]);
            $qualificationCode = $qual['code'] ?? null;
        }

        // Current membership subscription price (same lookup as calculate())
        $branchId = (int) ($member['branch_id'] ?? 1);
        $mType = $member['membership_type'] ?? 'working';
        $subscriptionPrice = '0.00';

        if ($qualificationCode) {
            $priceInfo = PricingEngine::getMembershipPrice($branchId, $qualificationCode);
            $subscriptionPrice = $priceInfo['price'] ?? '0.00';
        }

        if (bccomp($subscriptionPrice, '0.01', 2) < 0) {
            $qualId = !empty($member['qualification_id']) ? (int) $member['qualification_id'] : null;
            if ($qualId) {
                $pricing = $db->selectOne(
                    "SELECT price FROM pricing_configs WHERE branch_id = ? AND qualification_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY effective_from DESC LIMIT 1",
                    [$branchId, $qualId, $mType]
                );
            } else {
                $pricing = $db->selectOne(
                    "SELECT price FROM pricing_configs WHERE branch_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY price ASC LIMIT 1",
                    [$branchId, $mType]
                );
            }
            if ($pricing && bccomp($pricing['price'], '0.01', 2) >= 0) {
                $subscriptionPrice = $pricing['price'];
            }
        }

        // Elapsed years: floor from father's membership creation to effective_transfer_date
        $acquisitionDate = $member['membership_origin_date'] ?? $member['form_date'] ?? $member['created_at'] ?? date('Y-m-d');
        $yearsSince = self::calculateYearsFloor($acquisitionDate, $effectiveTransferDate);

        $feePercentage = self::getFeePercentageByYear($yearsSince);

        // Build calculation breakdown for transparency
        $calcBreakdown = self::buildYearsBreakdown($acquisitionDate, $effectiveTransferDate, $yearsSince, $feePercentage);

        // Separation fee = percentage × current subscription price
        $separationFee = bcdiv(bcmul($subscriptionPrice, (string) $feePercentage, 4), '100', 2);

        // Form fee + annual subscription (skip if member activated after July 1 of current FY)
        $formFeeData = RuleEngine::get('FORM_TRANSFER_FEE');
        $formFee = $formFeeData['amount'] ?? '570.00';

        $activatedAt = $member['activated_at'] ?? $member['created_at'] ?? date('Y-m-d');
        $annualSubCovered = self::isCurrentYearSubscriptionCovered($activatedAt);

        $memberSub = ServicePrice::getPrice('SVC_ANNUAL_MEMBER', '492.00');
        $spouseSub = ServicePrice::getPrice('SVC_ANNUAL_SPOUSE', '492.00');
        $childSub  = ServicePrice::getPrice('SVC_ANNUAL_CHILD', '222.00');
        $tempSub   = ServicePrice::getPrice('SVC_ANNUAL_TEMP', '222.00');
        $devFeeData = RuleEngine::get('DEVELOPMENT_FEE');
        $devFee = $devFeeData['amount'] ?? '35.00';

        $familyTotal = $memberSub;
        $familyTotal = bcadd($familyTotal, bcmul($spouseSub, (string) $depSpouses, 2), 2);
        $familyTotal = bcadd($familyTotal, bcmul($childSub, (string) $depChildren, 2), 2);
        $familyTotal = bcadd($familyTotal, bcmul($tempSub, (string) $depTemps, 2), 2);
        $annualSubscriptionFee = $annualSubCovered ? '0.00' : bcadd($familyTotal, $devFee, 2);

        // Foreign spouse surcharge: 15% of membership value per foreign spouse
        $foreignSpouseFee = '0.00';
        if ($foreignSpouseCount > 0) {
            $perForeignSpouse = bcdiv(bcmul($subscriptionPrice, '15', 4), '100', 2);
            $foreignSpouseFee = bcmul($perForeignSpouse, (string) $foreignSpouseCount, 2);
        }

        $totalFee = bcadd(bcadd(bcadd($separationFee, $formFee, 2), $annualSubscriptionFee, 2), $foreignSpouseFee, 2);

        return [
            'success'                 => true,
            'source_member_id'        => $sourceMemberId,
            'child_id'                => $childId,
            'qualification_code'      => $qualificationCode,
            'subscription_price'      => $subscriptionPrice,
            'years_since_acquisition' => $yearsSince,
            'fee_percentage'          => $feePercentage,
            'separation_fee'          => $separationFee,
            'form_fee'                => $formFee,
            'annual_subscription_fee' => $annualSubscriptionFee,
            'annual_sub_covered'      => $annualSubCovered,
            'foreign_spouse_fee'      => $foreignSpouseFee,
            'foreign_spouse_count'    => $foreignSpouseCount,
            'total_fee'               => $totalFee,
            'acquisition_date'        => $acquisitionDate,
            'effective_transfer_date' => $effectiveTransferDate,
            'is_employed'             => $isEmployed,
            'work_date'               => $workDate,
            'dep_spouses'             => $depSpouses,
            'dep_children'            => $depChildren,
            'dep_temps'               => $depTemps,
            'calc_breakdown'          => $calcBreakdown,
        ];
    }

    public static function calculateAcquiredMemberChildFee(int $childAge, string $membershipValue): array
    {
        if ($childAge < 12) {
            $ruleCode = 'DIVORCE_CHILD_UNDER_12';
        } elseif ($childAge < 16) {
            $ruleCode = 'DIVORCE_CHILD_12_TO_16';
        } elseif ($childAge < 18) {
            $ruleCode = 'DIVORCE_CHILD_16_TO_18';
        } else {
            $ruleCode = 'DIVORCE_CHILD_OVER_18';
        }

        $data = RuleEngine::get($ruleCode);
        $percentage = $data['percentage'] ?? '30.00';
        $fee = bcdiv(bcmul($membershipValue, $percentage, 4), '100', 2);

        return [
            'age' => $childAge,
            'rule_code' => $ruleCode,
            'percentage' => $percentage,
            'fee' => $fee,
            'membership_value' => $membershipValue,
        ];
    }

    public static function getFeePercentageByYear(int $year): string
    {
        $ruleMap = [
            1 => 'SEPARATION_FEE_YEAR_1',
            2 => 'SEPARATION_FEE_YEAR_2',
            3 => 'SEPARATION_FEE_YEAR_3',
            4 => 'SEPARATION_FEE_YEAR_4',
            5 => 'SEPARATION_FEE_YEAR_5',
        ];

        $ruleCode = $ruleMap[min($year, 5)] ?? 'SEPARATION_FEE_YEAR_6_PLUS';
        if ($year > 5) {
            $ruleCode = 'SEPARATION_FEE_YEAR_6_PLUS';
        }

        $data = RuleEngine::require($ruleCode);
        return $data['percentage'];
    }

    /**
     * Calculate surcharge when new owner has more companions than the original member.
     * Uses the EXACT same tiered fee logic as adding a brand-new spouse/child:
     * - Extra spouses are charged per their ordinal position (2nd/3rd/4th wife rules)
     * - Extra children are charged per their ordinal position via PricingEngine
     */
    public static function calculateCompanionSurcharge(
        int $sourceMemberId,
        int $targetSpousesCount,
        int $targetChildrenCount,
        array $targetChildrenAges = []
    ): array {
        $db = App::getInstance()->db();
        $member = $db->selectOne("SELECT * FROM members WHERE id = ?", [$sourceMemberId]);
        if (!$member) {
            return ['surcharge' => '0.00', 'breakdown' => [], 'source_count' => 0, 'target_count' => 0];
        }

        $sourceSpouses = (int) $db->selectOne(
            "SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0 AND status = 'active'",
            [$sourceMemberId]
        )['cnt'];

        $sourceChildren = (int) $db->selectOne(
            "SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0 AND status = 'active'",
            [$sourceMemberId]
        )['cnt'];

        $sourceTotal = $sourceSpouses + $sourceChildren;
        $targetTotal = $targetSpousesCount + $targetChildrenCount;
        $breakdown = [];
        $surcharge = '0.00';

        // Use current pricing (same logic as calculate())
        $branchId = (int) ($member['branch_id'] ?? 1);
        $mType = $member['membership_type'] ?? 'working';
        $membershipValue = $member['membership_value'] ?? '0.00';
        $qualId = !empty($member['qualification_id']) ? (int) $member['qualification_id'] : null;
        if ($qualId) {
            $currentPricing = $db->selectOne(
                "SELECT price FROM pricing_configs WHERE branch_id = ? AND qualification_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY effective_from DESC LIMIT 1",
                [$branchId, $qualId, $mType]
            );
        } else {
            $currentPricing = $db->selectOne(
                "SELECT price FROM pricing_configs WHERE branch_id = ? AND membership_type = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY price ASC LIMIT 1",
                [$branchId, $mType]
            );
        }
        if ($currentPricing && bccomp($currentPricing['price'], '0.01', 2) >= 0) {
            $membershipValue = $currentPricing['price'];
        }

        // Extra spouses: use tiered rules per ordinal position
        $extraSpouses = max(0, $targetSpousesCount - $sourceSpouses);
        if ($extraSpouses > 0) {
            for ($i = 0; $i < $extraSpouses; $i++) {
                $spouseOrder = $sourceSpouses + $i + 1;
                $feeForOrder = self::getSpouseFeeByOrder($spouseOrder, $membershipValue);
                $surcharge = bcadd($surcharge, $feeForOrder['fee'], 2);
                $breakdown[] = [
                    'type'       => 'extra_spouse',
                    'order'      => $spouseOrder,
                    'label'      => $feeForOrder['label'],
                    'percentage' => $feeForOrder['percentage'],
                    'fee'        => $feeForOrder['fee'],
                ];
            }
        }

        // Extra children: use tiered rules per ordinal position and age
        $extraChildren = max(0, $targetChildrenCount - $sourceChildren);
        if ($extraChildren > 0) {
            for ($i = 0; $i < $extraChildren; $i++) {
                $childOrder = $sourceChildren + $i + 1;
                $childAge = $targetChildrenAges[$i] ?? 10;
                $feeForOrder = self::getChildFeeByOrder($childOrder, $childAge, $membershipValue);
                $surcharge = bcadd($surcharge, $feeForOrder['fee'], 2);
                $breakdown[] = [
                    'type'       => 'extra_child',
                    'order'      => $childOrder,
                    'age'        => $childAge,
                    'label'      => $feeForOrder['label'],
                    'percentage' => $feeForOrder['percentage'],
                    'fee'        => $feeForOrder['fee'],
                ];
            }
        }

        return [
            'surcharge'      => $surcharge,
            'breakdown'      => $breakdown,
            'source_count'   => $sourceTotal,
            'target_count'   => $targetTotal,
            'extra_spouses'  => $extraSpouses,
            'extra_children' => $extraChildren,
        ];
    }

    /**
     * Get the addition fee for a spouse at a specific ordinal position.
     * Uses the same rule codes as SpouseFeeCalculator.
     */
    private static function getSpouseFeeByOrder(int $order, string $membershipValue): array
    {
        switch (true) {
            case ($order <= 1):
                $data = RuleEngine::require('SPOUSE_BASE_MEMBER_FEE');
                $pct = $data['percentage'];
                $label = 'الزوجة الأولى — ' . $pct . '%';
                break;
            case ($order === 2):
                $data = RuleEngine::require('SPOUSE_2ND_FEE');
                $pct = $data['percentage'];
                $label = 'الزوجة الثانية — ' . $pct . '%';
                break;
            case ($order === 3):
                $data = RuleEngine::require('SPOUSE_3RD_FEE');
                $pct = $data['percentage'];
                $label = 'الزوجة الثالثة — ' . $pct . '%';
                break;
            default:
                $data = RuleEngine::require('SPOUSE_4TH_FEE');
                $pct = $data['percentage'];
                $label = 'الزوجة الرابعة+ — ' . $pct . '%';
                break;
        }

        $fee = bcdiv(bcmul($membershipValue, $pct, 4), '100', 2);
        return ['fee' => $fee, 'percentage' => $pct, 'label' => $label];
    }

    /**
     * Get the addition fee for a child at a specific ordinal position and age.
     * Uses PricingEngine::calculateChildFee (same logic as ChildFeeCalculator).
     */
    private static function getChildFeeByOrder(int $order, int $age, string $membershipValue): array
    {
        $feeResult = PricingEngine::calculateChildFee($membershipValue, $age, $order);
        $fee = $feeResult['fee'] ?? '0.00';
        $pct = $feeResult['percentage'] ?? '0.00';
        $label = 'الابن/الابنة رقم ' . $order . ' (سن ' . $age . ') — ' . $pct . '%';

        return ['fee' => $fee, 'percentage' => $pct, 'label' => $label];
    }
}