<?php

namespace App\Services;

use App\Enums\DestinationType;
use App\Enums\RateCategory;
use App\Exceptions\InsuranceCalculationException;
use App\Models\Person;
use Illuminate\Support\Carbon;

class InsuranceRateCalculator
{
    /**
     * @var JfRateService
     */
    private $jfRateService;

    /**
     * @var ManulifeRateService
     */
    private $manulifeRateService;

    /**
     * Deductible discount percentages
     */
    private array $deductibleDiscounts = [
        0 => 0,
        500 => 10,
        1000 => 15,
        5000 => 30,
        10000 => 35,
    ];

    /**
     * Constructor
     */
    public function __construct(
        JfRateService $jfRateService,
        ManulifeRateService $manulifeRateService
    ) {
        $this->jfRateService = $jfRateService;
        $this->manulifeRateService = $manulifeRateService;
    }

    /**
     * Calculate premium for a specific company and plan
     */
    public function calculatePremium(
        string $companySlug,
        string $planType,
        array $peopleData,
        array $tripData,
        array $additionalParams = []
    ): array {
        $this->validateInput($peopleData, $tripData);
        
        // Convert string values to proper types
        $destination = DestinationType::tryFrom($tripData['destination']) 
            ?? DestinationType::WORLDWIDE_INCLUDING_USA;
            
        $rateCategory = isset($additionalParams['rate_category']) 
            ? RateCategory::tryFrom($additionalParams['rate_category']) 
            : null;
        
        $deductible = $additionalParams['deductible'] ?? 0;
        $insuranceCover = $tripData['insurance_cover'] ?? 100000;
        
        // Calculate based on company
        switch (strtolower($companySlug)) {
            case 'jf':
                return $this->calculateJfPremium($peopleData, $tripData, $destination, $deductible);
                
            case 'manulife':
                return $this->calculateManulifePremium($peopleData, $tripData, $destination, $rateCategory, $deductible, $additionalParams);
                
            default:
                return $this->calculateGenericPremium($peopleData, $tripData, $destination, $deductible, $insuranceCover);
        }
    }

    /**
     * Calculate JF insurance premium
     */
    private function calculateJfPremium(
        array $peopleData,
        array $tripData,
        DestinationType $destination,
        int $deductible
    ): array {
        $duration = $this->calculateDuration($tripData['started_at'], $tripData['ended_at']);
        $totalPremium = 0;
        $dailyRate = 0;
        $individualPremiums = [];
        
        foreach ($peopleData as $person) {
            $age = $this->calculateAge($person['birthday']);
            $personPremium = $this->jfRateService->calculatePremium($age, $duration, $destination);
            
            $individualPremiums[] = [
                'age' => $age,
                'premium' => $personPremium['total_premium'],
                'daily_rate' => $personPremium['daily_rate'],
            ];
            
            $totalPremium += $personPremium['total_premium'];
            $dailyRate = max($dailyRate, $personPremium['daily_rate']);
        }
        
        // Apply deductible discount
        $deductibleDiscountPercent = $this->getDeductibleDiscountPercent($deductible);
        $deductibleDiscount = $totalPremium * ($deductibleDiscountPercent / 100);
        $finalPremium = $totalPremium - $deductibleDiscount;
        
        return [
            'company' => 'JF',
            'plan_type' => 'jf_top',
            'destination' => $destination->value,
            'includes_usa' => $destination === DestinationType::WORLDWIDE_INCLUDING_USA,
            'total_people' => count($peopleData),
            'trip_duration' => $duration,
            'daily_rate' => round($dailyRate, 2),
            'base_premium' => round($totalPremium, 2),
            'total_premium' => round($totalPremium, 2),
            'deductible_amount' => $deductible,
            'deductible_discount_percent' => $deductibleDiscountPercent,
            'deductible_discount_amount' => round($deductibleDiscount, 2),
            'final_premium' => round($finalPremium, 2),
            'individual_premiums' => $individualPremiums,
            'calculation_details' => [
                'method' => 'jf_daily_rate',
                'rate_table' => $destination === DestinationType::WORLDWIDE_INCLUDING_USA ? 'usa' : 'non_usa',
                'notes' => 'No medical questionnaire required for JF',
            ],
        ];
    }

    /**
     * Calculate Manulife insurance premium
     */
    private function calculateManulifePremium(
        array $peopleData,
        array $tripData,
        DestinationType $destination,
        ?RateCategory $rateCategory,
        int $deductible,
        array $additionalParams
    ): array {
        $duration = $this->calculateDuration($tripData['started_at'], $tripData['ended_at']);
        $totalPremium = 0;
        $dailyRate = 0;
        $individualPremiums = [];
        $hasSmokingSurcharge = false;
        $canadaHalfPriceApplied = false;
        
        foreach ($peopleData as $index => $person) {
            $age = $this->calculateAge($person['birthday']);
            $isSmoker = $additionalParams['is_smokers'][$index] ?? false;
            $hasPreExistingConditions = $additionalParams['has_pre_existing_conditions'][$index] ?? false;
            
            // Determine rate category for each person if not provided
            $personRateCategory = $rateCategory;
            if (!$personRateCategory) {
                $personRateCategory = $this->determineRateCategory($age, $additionalParams);
            }
            
            $personPremium = $this->manulifeRateService->calculatePremium(
                $age,
                $duration,
                $destination,
                $personRateCategory,
                $isSmoker,
                $deductible,
                $hasPreExistingConditions
            );
            
            $individualPremiums[] = [
                'age' => $age,
                'premium' => $personPremium['total_premium'],
                'daily_rate' => $personPremium['daily_rate'],
                'rate_category' => $personRateCategory->value,
                'is_smoker' => $isSmoker,
                'has_pre_existing_conditions' => $hasPreExistingConditions,
                'smoking_surcharge' => $personPremium['smoking_surcharge'],
                'base_premium' => $personPremium['base_premium'],
            ];
            
            $totalPremium += $personPremium['total_premium'];
            $dailyRate = max($dailyRate, $personPremium['daily_rate']);
            
            if ($personPremium['smoking_surcharge'] > 0) {
                $hasSmokingSurcharge = true;
            }
            
            if ($personPremium['canada_half_price_applied']) {
                $canadaHalfPriceApplied = true;
            }
        }
        
        // Apply deductible discount
        $deductibleDiscountPercent = $this->getDeductibleDiscountPercent($deductible);
        $deductibleDiscount = $totalPremium * ($deductibleDiscountPercent / 100);
        $finalPremium = $totalPremium - $deductibleDiscount;
        
        return [
            'company' => 'Manulife',
            'plan_type' => 'manulife_single_trip',
            'destination' => $destination->value,
            'includes_usa' => $destination === DestinationType::WORLDWIDE_INCLUDING_USA,
            'total_people' => count($peopleData),
            'trip_duration' => $duration,
            'daily_rate' => round($dailyRate, 2),
            'base_premium' => round($totalPremium, 2),
            'total_premium' => round($totalPremium, 2),
            'deductible_amount' => $deductible,
            'deductible_discount_percent' => $deductibleDiscountPercent,
            'deductible_discount_amount' => round($deductibleDiscount, 2),
            'final_premium' => round($finalPremium, 2),
            'rate_category' => $rateCategory?->value ?? 'A',
            'has_smoking_surcharge' => $hasSmokingSurcharge,
            'canada_half_price_applied' => $canadaHalfPriceApplied,
            'individual_premiums' => $individualPremiums,
            'calculation_details' => [
                'method' => 'manulife_daily_rate',
                'requires_questionnaire' => $this->requiresQuestionnaire($peopleData),
                'notes' => $this->getManulifeNotes($rateCategory, $hasSmokingSurcharge, $canadaHalfPriceApplied),
            ],
        ];
    }

    /**
     * Calculate generic insurance premium
     */
    private function calculateGenericPremium(
        array $peopleData,
        array $tripData,
        DestinationType $destination,
        int $deductible,
        int $insuranceCover
    ): array {
        // Generic calculation for other companies
        $duration = $this->calculateDuration($tripData['started_at'], $tripData['ended_at']);
        
        // Simple calculation based on age brackets and destination
        $ageBrackets = $this->getAgeBrackets();
        $baseDailyRate = $this->getBaseDailyRate($destination, $insuranceCover);
        
        $totalPremium = 0;
        $dailyRate = 0;
        $individualPremiums = [];
        
        foreach ($peopleData as $person) {
            $age = $this->calculateAge($person['birthday']);
            $ageMultiplier = $this->getAgeMultiplier($age, $ageBrackets);
            $personDailyRate = $baseDailyRate * $ageMultiplier;
            $personPremium = $personDailyRate * $duration;
            
            $individualPremiums[] = [
                'age' => $age,
                'premium' => $personPremium,
                'daily_rate' => $personDailyRate,
                'age_multiplier' => $ageMultiplier,
            ];
            
            $totalPremium += $personPremium;
            $dailyRate = max($dailyRate, $personDailyRate);
        }
        
        // Apply deductible discount
        $deductibleDiscountPercent = $this->getDeductibleDiscountPercent($deductible);
        $deductibleDiscount = $totalPremium * ($deductibleDiscountPercent / 100);
        $finalPremium = $totalPremium - $deductibleDiscount;
        
        return [
            'company' => 'Generic',
            'plan_type' => 'generic_travel',
            'destination' => $destination->value,
            'includes_usa' => $destination === DestinationType::WORLDWIDE_INCLUDING_USA,
            'total_people' => count($peopleData),
            'trip_duration' => $duration,
            'daily_rate' => round($dailyRate, 2),
            'base_premium' => round($totalPremium, 2),
            'total_premium' => round($totalPremium, 2),
            'deductible_amount' => $deductible,
            'deductible_discount_percent' => $deductibleDiscountPercent,
            'deductible_discount_amount' => round($deductibleDiscount, 2),
            'final_premium' => round($finalPremium, 2),
            'individual_premiums' => $individualPremiums,
            'calculation_details' => [
                'method' => 'generic_age_based',
                'base_daily_rate' => $baseDailyRate,
                'insurance_cover' => $insuranceCover,
            ],
        ];
    }

    /**
     * Determine rate category based on age and parameters
     */
    private function determineRateCategory(int $age, array $parameters): RateCategory
    {
        // Default to Category A for under 60
        if ($age < 60) {
            return RateCategory::A;
        }
        
        // Check questionnaire answers if provided
        $questionnaireAnswers = $parameters['questionnaire_answers'] ?? [];
        
        if (!empty($questionnaireAnswers)) {
            // Check for Category C triggers
            $cTriggers = [
                'has_heart_condition',
                'has_aortic_aneurysm',
                'has_cirrhosis',
                'has_parkinsons',
                'has_alzheimers',
                'high_blood_pressure_meds',
            ];
            
            foreach ($cTriggers as $trigger) {
                if (!empty($questionnaireAnswers[$trigger]) && $questionnaireAnswers[$trigger] === 'yes') {
                    return RateCategory::C;
                }
            }
            
            // Check for Category B triggers
            $bTriggers = [
                'gastrointestinal_bleeding',
                'chronic_bowel_disorder',
                'kidney_disorder',
                'liver_disorder',
                'pancreatitis',
                'gallbladder_disorder',
                'blood_disorder',
                'recent_falls',
                'frequent_er_visits',
            ];
            
            foreach ($bTriggers as $trigger) {
                if (!empty($questionnaireAnswers[$trigger]) && $questionnaireAnswers[$trigger] === 'yes') {
                    return RateCategory::B;
                }
            }
        }
        
        return RateCategory::A;
    }

    /**
     * Check if questionnaire is required
     */
    private function requiresQuestionnaire(array $peopleData): bool
    {
        foreach ($peopleData as $person) {
            $age = $this->calculateAge($person['birthday']);
            if ($age >= 60) {
                return true;
            }
        }
        return false;
    }

    /**
     * Calculate trip duration in days
     */
    private function calculateDuration(string $startDate, string $endDate): int
    {
        $start = Carbon::parse($startDate);
        $end = Carbon::parse($endDate);
        
        if ($end->lessThan($start)) {
            throw InsuranceCalculationException::invalidDateRange($startDate, $endDate);
        }
        
        return $end->diffInDays($start) + 1; // Including both days
    }

    /**
     * Calculate age from birthday
     */
    private function calculateAge(string $birthday): int
    {
        $birthDate = Carbon::parse($birthday);
        $age = now()->diffInYears($birthDate);
        
        if ($age < 0 || $age > 120) {
            throw InsuranceCalculationException::ageNotSupported($age);
        }
        
        return $age;
    }

    /**
     * Get deductible discount percentage
     */
    private function getDeductibleDiscountPercent(int $deductible): int
    {
        return $this->deductibleDiscounts[$deductible] ?? 0;
    }

    /**
     * Get age brackets for generic calculation
     */
    private function getAgeBrackets(): array
    {
        return [
            ['min' => 0, 'max' => 17, 'multiplier' => 0.8],
            ['min' => 18, 'max' => 30, 'multiplier' => 1.0],
            ['min' => 31, 'max' => 50, 'multiplier' => 1.2],
            ['min' => 51, 'max' => 60, 'multiplier' => 1.5],
            ['min' => 61, 'max' => 70, 'multiplier' => 2.0],
            ['min' => 71, 'max' => 80, 'multiplier' => 3.0],
            ['min' => 81, 'max' => 90, 'multiplier' => 4.0],
            ['min' => 91, 'max' => 120, 'multiplier' => 5.0],
        ];
    }

    /**
     * Get base daily rate based on destination and coverage
     */
    private function getBaseDailyRate(DestinationType $destination, int $insuranceCover): float
    {
        $baseRates = [
            DestinationType::CANADA->value => 3.0,
            DestinationType::WORLDWIDE_EXCLUDING_USA->value => 5.0,
            DestinationType::WORLDWIDE_INCLUDING_USA->value => 8.0,
        ];
        
        $baseRate = $baseRates[$destination->value] ?? 5.0;
        
        // Adjust based on insurance coverage (per $10,000)
        $coverageMultiplier = 1 + (($insuranceCover - 100000) / 100000) * 0.1;
        
        return $baseRate * $coverageMultiplier;
    }

    /**
     * Get age multiplier for generic calculation
     */
    private function getAgeMultiplier(int $age, array $ageBrackets): float
    {
        foreach ($ageBrackets as $bracket) {
            if ($age >= $bracket['min'] && $age <= $bracket['max']) {
                return $bracket['multiplier'];
            }
        }
        
        // Default multiplier for age > 120
        return 5.0;
    }

    /**
     * Get Manulife calculation notes
     */
    private function getManulifeNotes(?RateCategory $rateCategory, bool $hasSmokingSurcharge, bool $canadaHalfPriceApplied): string
    {
        $notes = [];
        
        if ($rateCategory) {
            $notes[] = "Rate Category {$rateCategory->value} applied";
        }
        
        if ($hasSmokingSurcharge) {
            $notes[] = "10% smoking surcharge applied";
        }
        
        if ($canadaHalfPriceApplied) {
            $notes[] = "Canada half-price rule applied";
        }
        
        return implode('. ', $notes);
    }

    /**
     * Validate input data
     */
    private function validateInput(array $peopleData, array $tripData): void
    {
        if (empty($peopleData)) {
            throw InsuranceCalculationException::noPeopleProvided();
        }
        
        if (count($peopleData) > 10) {
            throw InsuranceCalculationException::tooManyPeople(count($peopleData));
        }
        
        if (empty($tripData['started_at']) || empty($tripData['ended_at'])) {
            throw InsuranceCalculationException::missingTripDates();
        }
        
        if (empty($tripData['destination'])) {
            throw InsuranceCalculationException::missingDestination();
        }
        
        // Validate each person
        foreach ($peopleData as $index => $person) {
            if (empty($person['birthday'])) {
                throw InsuranceCalculationException::missingBirthday($index + 1);
            }
            
            $age = $this->calculateAge($person['birthday']);
            if ($age > 120) {
                throw InsuranceCalculationException::ageNotSupported($age);
            }
        }
    }

    /**
     * Compare quotes from multiple companies
     */
    public function compareQuotes(
        array $peopleData,
        array $tripData,
        array $companySlugs = ['jf', 'manulife'],
        array $additionalParams = []
    ): array {
        $quotes = [];
        
        foreach ($companySlugs as $companySlug) {
            try {
                $quote = $this->calculatePremium(
                    $companySlug,
                    'single_trip', // Default plan type
                    $peopleData,
                    $tripData,
                    $additionalParams
                );
                
                $quotes[] = $quote;
                
            } catch (InsuranceCalculationException $e) {
                // Log but continue with other companies
                \Log::warning("Failed to calculate quote for {$companySlug}: " . $e->getMessage());
                continue;
            }
        }
        
        // Sort by final premium (cheapest first)
        usort($quotes, function ($a, $b) {
            return $a['final_premium'] <=> $b['final_premium'];
        });
        
        return [
            'quotes' => $quotes,
            'best_quote' => $quotes[0] ?? null,
            'total_companies_compared' => count($companySlugs),
            'successful_calculations' => count($quotes),
        ];
    }

    /**
     * Calculate premium for a single person
     */
    public function calculateSinglePersonPremium(
        string $companySlug,
        int $age,
        int $duration,
        string $destination,
        array $additionalParams = []
    ): array {
        $peopleData = [['birthday' => now()->subYears($age)->format('Y-m-d')]];
        $tripData = [
            'started_at' => now()->format('Y-m-d'),
            'ended_at' => now()->addDays($duration - 1)->format('Y-m-d'),
            'destination' => $destination,
            'insurance_cover' => $additionalParams['insurance_cover'] ?? 100000,
        ];
        
        $result = $this->calculatePremium(
            $companySlug,
            'single_trip',
            $peopleData,
            $tripData,
            $additionalParams
        );
        
        // Extract single person details
        return [
            'company' => $result['company'],
            'plan_type' => $result['plan_type'],
            'age' => $age,
            'duration' => $duration,
            'destination' => $destination,
            'daily_rate' => $result['daily_rate'],
            'total_premium' => $result['final_premium'],
            'calculation_details' => $result['calculation_details'],
        ];
    }
}