<?php

namespace App\Services;

use App\Enums\DestinationType;
use App\Enums\RateCategory;
use App\Exceptions\InsuranceCalculationException;

class ManulifeRateService
{
    private array $rateTables = [
        'rate_a' => [
            '0-25' => [3.83, 3.89, 3.99, 4.04, 4.06, 4.15, 4.26, 4.62],
            '26-39' => [4.15, 4.21, 4.33, 4.36, 4.42, 4.60, 4.84, 5.14],
            '40-54' => [4.53, 4.65, 4.99, 5.04, 5.23, 5.57, 5.83, 6.25],
            '55-59' => [5.32, 5.40, 5.71, 5.92, 6.14, 6.75, 7.10, 7.63],
            '60-64' => [5.41, 5.79, 6.50, 7.19, 7.44, 7.61, 8.97, 9.25],
            '65-69' => [8.10, 8.15, 9.06, 9.12, 9.49, 9.85, 10.60, 10.64],
            '70-74' => [11.05, 11.10, 11.16, 12.22, 13.45, 13.91, 14.67, 16.53],
            '75-79' => [17.74, 18.23, 19.31, 20.17, 21.77, 23.60, 24.43, 28.11],
            '80-84' => [30.29, 30.54, 30.77, 33.83, 34.18, 35.40, 36.72, 40.41],
            '85+' => [41.36, 41.84, 41.90, 46.03, 46.29, 47.76, 50.45, 53.57],
        ],
    ];
    
    private array $durationBrackets = [1, 18, 31, 61, 91, 121, 151, 213];
    private array $deductibleDiscounts = [0 => 0, 500 => 10, 1000 => 15, 5000 => 30, 10000 => 35];

    public function calculatePremium(
        int $age,
        int $days,
        DestinationType $destination,
        ?RateCategory $rateCategory = null,
        bool $isSmoker = false,
        int $deductible = 0,
        bool $hasPreExistingConditions = false
    ): array {
        $this->validateInputs($age, $days, $destination);
        
        $rateCategory = $rateCategory ?? RateCategory::A;
        $dailyRate = $this->findDailyRate($age, $days, $rateCategory);
        $basePremium = $dailyRate * $days;
        
        // Apply Canada half-price rule
        $canadaHalfPrice = false;
        if ($destination === DestinationType::CANADA && !$hasPreExistingConditions) {
            $basePremium *= 0.5;
            $canadaHalfPrice = true;
        }
        
        // Apply deductible discount
        $deductibleDiscountPercent = $this->deductibleDiscounts[$deductible] ?? 0;
        $deductibleDiscount = $basePremium * ($deductibleDiscountPercent / 100);
        
        // Apply smoking surcharge (10% for age 60+)
        $smokingSurcharge = 0;
        if ($isSmoker && $age >= 60) {
            $smokingSurcharge = $basePremium * 0.10;
        }
        
        $totalPremium = $basePremium - $deductibleDiscount + $smokingSurcharge;
        
        return [
            'daily_rate' => round($dailyRate, 2),
            'base_premium' => round($basePremium, 2),
            'deductible_discount' => round($deductibleDiscount, 2),
            'smoking_surcharge' => round($smokingSurcharge, 2),
            'total_premium' => round($totalPremium, 2),
            'destination' => $destination->value,
            'rate_category' => $rateCategory->value,
            'canada_half_price_applied' => $canadaHalfPrice,
            'plan_type' => 'manulife_single_trip',
        ];
    }

    private function findDailyRate(int $age, int $days, RateCategory $rateCategory): float
    {
        $ageBracket = $this->findAgeBracket($age);
        $durationIndex = $this->findDurationIndex($days);
        
        $tableKey = 'rate_' . strtolower($rateCategory->value);
        $rateTable = $this->rateTables[$tableKey] ?? $this->rateTables['rate_a'];
        
        return $rateTable[$ageBracket][$durationIndex] ?? $rateTable[$ageBracket][0];
    }

    private function findAgeBracket(int $age): string
    {
        $brackets = [
            '0-25', '26-39', '40-54', '55-59', '60-64',
            '65-69', '70-74', '75-79', '80-84', '85+'
        ];
        
        foreach ($brackets as $bracket) {
            if ($bracket === '85+') {
                if ($age >= 85) return $bracket;
            } else {
                [$min, $max] = explode('-', $bracket);
                if ($age >= (int)$min && $age <= (int)$max) {
                    return $bracket;
                }
            }
        }
        
        throw InsuranceCalculationException::ageNotSupported($age);
    }

    private function findDurationIndex(int $days): int
    {
        $selectedIndex = 0;
        foreach ($this->durationBrackets as $index => $minDays) {
            if ($days >= $minDays) {
                $selectedIndex = $index;
            } else {
                break;
            }
        }
        return $selectedIndex;
    }

    private function validateInputs(int $age, int $days, DestinationType $destination): void
    {
        if ($age < 0 || $age > 120) {
            throw InsuranceCalculationException::ageNotSupported($age);
        }
        
        if ($days < 1 || $days > 365) {
            throw InsuranceCalculationException::durationNotSupported($days);
        }
    }
}