<?php
declare(strict_types=1);

namespace App\Modules\Pricing\Services;

use App\Core\App;
use App\Core\Logger;
use App\Modules\Rules\Services\RuleEngine;

final class PricingEngine
{
    public static function getMembershipPrice(int $branchId, string $qualificationCode, ?string $date = null): array
    {
        $date = $date ?? date('Y-m-d');
        $db = App::getInstance()->db();

        $qual = $db->selectOne("SELECT id FROM qualifications WHERE code = ?", [$qualificationCode]);
        if (!$qual) {
            return ['price' => '0.00', 'currency' => 'EGP', 'config_id' => null, 'error' => 'Qualification not found'];
        }

        $config = $db->selectOne(
            "SELECT * FROM pricing_configs WHERE branch_id = ? AND qualification_id = ? AND membership_type = 'working' AND is_active = 1 AND effective_from <= ? AND (effective_to IS NULL OR effective_to >= ?) ORDER BY effective_from DESC LIMIT 1",
            [$branchId, $qual['id'], $date, $date]
        );

        if (!$config) {
            return ['price' => '0.00', 'currency' => 'EGP', 'config_id' => null, 'error' => 'No pricing config found'];
        }

        return [
            'price'     => $config['price'],
            'currency'  => $config['currency'],
            'config_id' => (int) $config['id'],
            'error'     => null,
        ];
    }

    public static function calculateChildFee(string $membershipValue, int $childAge, int $childOrder): array
    {
        $maxIncluded = (int) RuleEngine::requireValue('INITIAL_FREE_CHILDREN_COUNT', 'value');
        $maxIncludedAge = (int) RuleEngine::requireValue('CHILD_INCLUDED_MAX_AGE', 'value');
        $maxChildAge = (int) (RuleEngine::getValue('CHILD_MAX_AGE', 'value') ?? 25);

        if ($childAge < $maxIncludedAge && $childOrder <= $maxIncluded) {
            return ['fee' => '0.00', 'rule_applied' => 'included', 'percentage' => '0.00', 'classification' => 'included'];
        }

        if ($childAge < $maxIncludedAge && $childOrder > $maxIncluded) {
            $data = RuleEngine::require('CHILD_4TH_UNDER_18_FEE');
            $pct = $data['percentage'];
            $fee = bcmul($membershipValue, bcdiv($pct, '100', 4), 2);
            return ['fee' => $fee, 'rule_applied' => 'CHILD_4TH_UNDER_18_FEE', 'percentage' => $pct, 'classification' => 'dependent_with_fee'];
        }

        $ruleMap = [18 => 'CHILD_FEE_AGE_18', 19 => 'CHILD_FEE_AGE_19', 20 => 'CHILD_FEE_AGE_20'];

        if (isset($ruleMap[$childAge])) {
            $data = RuleEngine::require($ruleMap[$childAge]);
            $pct = $data['percentage'];
            $fee = bcmul($membershipValue, bcdiv($pct, '100', 4), 2);
            return ['fee' => $fee, 'rule_applied' => $ruleMap[$childAge], 'percentage' => $pct, 'classification' => $data['type'] ?? 'regular'];
        }

        if ($childAge >= 21 && $childAge < $maxChildAge) {
            $data = RuleEngine::require('CHILD_FEE_AGE_21');
            $pct = $data['percentage'];
            $fee = bcmul($membershipValue, bcdiv($pct, '100', 4), 2);
            return ['fee' => $fee, 'rule_applied' => 'CHILD_FEE_AGE_21', 'percentage' => $pct, 'classification' => 'temporary'];
        }

        return ['fee' => '0.00', 'rule_applied' => 'CHILD_AUTO_DELETE_AGE', 'percentage' => '0.00', 'classification' => 'not_accepted', 'error' => 'سن الابن/الابنة يتجاوز الحد المسموح (' . $maxChildAge . ' سنة)'];
    }

    public static function calculateSpouseFee(string $membershipValue, int $spouseOrder, string $nationality, string $marriageDate, string $membershipAcquisitionDate, string $memberType): array
    {
        if ($spouseOrder === 1) {
            return ['percentage_fee' => '0.00', 'annual_fee' => '0.00', 'total' => '0.00', 'years_count' => 0, 'rule_applied' => 'included'];
        }

        if (strtolower($nationality) !== 'مصري' && strtolower($nationality) !== 'egyptian' && strtolower($nationality) !== 'egy') {
            $data = RuleEngine::require('SPOUSE_FOREIGN_FEE');
            $pct = $data['percentage'];
            $fee = bcmul($membershipValue, bcdiv($pct, '100', 4), 2);
            return ['percentage_fee' => $fee, 'annual_fee' => '0.00', 'total' => $fee, 'years_count' => 0, 'rule_applied' => 'SPOUSE_FOREIGN_FEE'];
        }

        if ($memberType === 'acquired') {
            $data = RuleEngine::require('SPOUSE_ACQUIRED_MEMBER_FEE');
            $pct = $data['percentage'];
            $fee = bcmul($membershipValue, bcdiv($pct, '100', 4), 2);
            return ['percentage_fee' => $fee, 'annual_fee' => '0.00', 'total' => $fee, 'years_count' => 0, 'rule_applied' => 'SPOUSE_ACQUIRED_MEMBER_FEE'];
        }

        $ruleMap = [2 => 'SPOUSE_2ND_FEE', 3 => 'SPOUSE_3RD_FEE', 4 => 'SPOUSE_4TH_FEE'];
        $ruleCode = $ruleMap[$spouseOrder] ?? 'SPOUSE_4TH_FEE';
        $data = RuleEngine::require($ruleCode);

        $pct = $data['percentage'];
        $annualFlat = $data['annual_flat'];

        $percentageFee = bcmul($membershipValue, bcdiv($pct, '100', 4), 2);

        // Annual fine only applies if marriage date is BEFORE membership date
        $yearsCount = 0;
        $marriageTs = strtotime($marriageDate);
        $memberTs = strtotime($membershipAcquisitionDate);
        if ($marriageTs && $memberTs && $marriageTs < $memberTs) {
            $start = new \DateTime($marriageDate);
            $end = new \DateTime($membershipAcquisitionDate);
            $diff = $end->diff($start);
            $yearsCount = $diff->y;
            if ($diff->m > 0 || $diff->d > 0) {
                $yearsCount++;
            }
            $yearsCount = max(1, $yearsCount);
        }

        $annualFee = bcmul((string) $yearsCount, $annualFlat, 2);
        $total = bcadd($percentageFee, $annualFee, 2);

        return [
            'percentage_fee' => $percentageFee,
            'annual_fee'     => $annualFee,
            'total'          => $total,
            'years_count'    => $yearsCount,
            'rule_applied'   => $ruleCode,
        ];
    }

    public static function calculateSeparationFee(string $newMembershipValue, int $yearsSinceAcquisition): array
    {
        $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[$yearsSinceAcquisition] ?? 'SEPARATION_FEE_YEAR_6_PLUS';
        $data = RuleEngine::require($ruleCode);
        $pct = $data['percentage'];

        $fee = bcmul($newMembershipValue, bcdiv($pct, '100', 4), 2);

        return [
            'fee'          => $fee,
            'percentage'   => $pct,
            'rule_applied' => $ruleCode,
            'years'        => $yearsSinceAcquisition,
        ];
    }

    public static function calculateInstallmentPlan(string $totalAmount, string $downPayment, int $months): array
    {
        $rateData = RuleEngine::require('INSTALLMENT_INTEREST_RATE');
        $annualRate = $rateData['percentage'];

        $remaining = bcsub($totalAmount, $downPayment, 2);

        // Flat simple interest: remaining × (annual_rate/100) × (months/12)
        $totalInterest = bcmul(
            bcmul($remaining, bcdiv($annualRate, '100', 10), 10),
            bcdiv((string) $months, '12', 10),
            2
        );
        $totalWithInterest = bcadd($remaining, $totalInterest, 2);
        $monthlyPayment    = bcdiv($totalWithInterest, (string) $months, 2);
        $monthlyPrincipal  = bcdiv($remaining, (string) $months, 2);
        $monthlyInterest   = bcdiv($totalInterest, (string) $months, 2);

        $schedule = [];
        $balance  = $remaining;
        $startDate = new \DateTime();
        $startDate->modify('+1 month');

        for ($i = 1; $i <= $months; $i++) {
            $isLast    = ($i === $months);
            $principal = $isLast ? $balance : $monthlyPrincipal;
            $interest  = $isLast
                ? bcsub($totalWithInterest, bcadd($remaining, bcsub($totalInterest, $monthlyInterest, 2), 2), 2)
                : $monthlyInterest;
            if (bccomp($interest, '0', 2) < 0) $interest = '0.00';
            $amount = bcadd($principal, $interest, 2);

            $balance = bcsub($balance, $principal, 2);
            if (bccomp($balance, '0', 2) < 0) $balance = '0.00';

            $dueDate = clone $startDate;
            $dueDate->modify('+' . ($i - 1) . ' months');

            $schedule[] = [
                'number'    => $i,
                'due_date'  => $dueDate->format('Y-m-d'),
                'amount'    => $amount,
                'principal' => $principal,
                'interest'  => $interest,
                'remaining' => $balance,
            ];
        }

        return [
            'total_amount'        => $totalAmount,
            'down_payment'        => $downPayment,
            'remaining'           => $remaining,
            'annual_rate'         => $annualRate,
            'total_interest'      => $totalInterest,
            'total_with_interest' => $totalWithInterest,
            'monthly_payment'     => $monthlyPayment,
            'months'              => $months,
            'schedule'            => $schedule,
        ];
    }

    public static function applyRegulatoryDiscount(string $membershipFee, array $context): array
    {
        $best = RegulatoryDiscountService::getBestDiscount(array_merge($context, ['membership_fee' => $membershipFee]));

        if (!$best) {
            return [
                'original_amount'     => $membershipFee,
                'discount_amount'     => '0.00',
                'final_amount'        => $membershipFee,
                'discount_percentage' => '0.00',
                'rule_applied'        => null,
                'allows_installment'  => false,
            ];
        }

        return [
            'original_amount'         => $best['original_amount'],
            'discount_amount'         => $best['discount_amount'],
            'final_amount'            => $best['final_amount'],
            'discount_percentage'     => $best['discount_percentage'],
            'rule_applied'            => $best['name_ar'],
            'article_number'          => $best['article_number'],
            'rule_id'                 => $best['rule_id'],
            'allows_installment'      => $best['allows_installment'],
            'installment_max_years'   => $best['installment_max_years'],
            'installment_interest_rate' => $best['installment_interest_rate'],
        ];
    }

    public static function getServiceFee(string $serviceCode, ?int $branchId = null): ?array
    {
        $db = App::getInstance()->db();
        $service = null;
        if ($branchId) {
            $service = $db->selectOne(
                "SELECT * FROM service_catalog WHERE service_code = ? AND branch_id = ? AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE())",
                [$serviceCode, $branchId]
            );
        }
        if (!$service) {
            $service = $db->selectOne(
                "SELECT * FROM service_catalog WHERE service_code = ? AND branch_id IS NULL AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE())",
                [$serviceCode]
            );
        }
        return $service;
    }

    public static function getFormFee(string $formType): string
    {
        $codeMap = [
            'NEW_MEMBERSHIP'      => 'SVC_NEW_FORM',
            'TRANSFER_SEPARATION' => 'SVC_TRANSFER_FORM',
            'ADDITION'            => 'SVC_ADDITION_FORM',
        ];
        $code = $codeMap[$formType] ?? $formType;
        $service = self::getServiceFee($code);
        return $service ? ($service['base_amount'] ?? '0.00') : '0.00';
    }
}