<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
 * Class Plan
 * @package App\Models
 *
 * @property int $id
 * @property int $company_id
 * @property string $slug
 * @property int $sort
 * @property bool $is_active
 * @property bool $requires_questionnaire
 * @property string|null $questionnaire_type
 * @property array|null $rate_tables
 * @property bool $supports_outbound
 * @property bool $supports_inbound
 *
 * @property Company $company
 * @property Collection<Price> $prices
 * @property Collection<PlanTranslation> $translations
 */
class Plan extends Model
{
    /**
     * Questionnaire types for different insurance companies
     */
    const QUESTIONNAIRE_TYPE_MANULIFE_60_PLUS = 'manulife_60_plus';
    const QUESTIONNAIRE_TYPE_MANULIFE_MEDICAL = 'manulife_medical';
    const QUESTIONNAIRE_TYPE_OTHER = 'other';

    /**
     * Rate table types
     */
    const RATE_TABLE_JF_NON_USA = 'jf_non_usa';
    const RATE_TABLE_JF_USA = 'jf_usa';
    const RATE_TABLE_MANULIFE_A = 'manulife_a';
    const RATE_TABLE_MANULIFE_B = 'manulife_b';
    const RATE_TABLE_MANULIFE_C = 'manulife_c';

    public $timestamps = false;
    protected $table = 'plans';
    protected $fillable = [
        'id',
        'company_id',
        'slug',
        'sort',
        'is_active',
        'requires_questionnaire',
        'questionnaire_type',
        'rate_tables',
        'supports_outbound',
        'supports_inbound',
    ];

    /**
     * Boot the model and register model events.
     * @return void
     */
    protected static function boot(): void
    {
        parent::boot();

        // Set default values before creating
        static::creating(function (Plan $plan) {
            // Default to supporting inbound only
            if (!isset($plan->supports_outbound)) {
                $plan->supports_outbound = false;
            }
            
            if (!isset($plan->supports_inbound)) {
                $plan->supports_inbound = true;
            }
            
            // Default questionnaire requirement
            if (!isset($plan->requires_questionnaire)) {
                $plan->requires_questionnaire = false;
            }
            
            // Set default sort order if not provided
            if (empty($plan->sort)) {
                $plan->sort = 100;
            }
        });
    }

    /**
     * Get questionnaire type options
     * @return array
     */
    public static function getQuestionnaireTypeOptions(): array
    {
        return [
            self::QUESTIONNAIRE_TYPE_MANULIFE_60_PLUS => 'Manulife 60+ Questionnaire',
            self::QUESTIONNAIRE_TYPE_MANULIFE_MEDICAL => 'Manulife Medical Questionnaire',
            self::QUESTIONNAIRE_TYPE_OTHER => 'Other Questionnaire',
        ];
    }

    /**
     * Get rate table type options
     * @return array
     */
    public static function getRateTableTypeOptions(): array
    {
        return [
            self::RATE_TABLE_JF_NON_USA => 'JF Non-USA Rates',
            self::RATE_TABLE_JF_USA => 'JF USA Rates',
            self::RATE_TABLE_MANULIFE_A => 'Manulife Rate Category A',
            self::RATE_TABLE_MANULIFE_B => 'Manulife Rate Category B',
            self::RATE_TABLE_MANULIFE_C => 'Manulife Rate Category C',
        ];
    }

    /**
     * Get questionnaire type label
     * @return string|null
     */
    public function getQuestionnaireTypeLabel(): ?string
    {
        $options = self::getQuestionnaireTypeOptions();
        return $options[$this->questionnaire_type] ?? null;
    }

    /**
     * Check if this plan supports a specific insurance type
     * @param bool $isEntry Whether it's entry insurance (true) or exit insurance (false)
     * @return bool
     */
    public function supportsInsuranceType(bool $isEntry): bool
    {
        return $isEntry ? $this->supports_inbound : $this->supports_outbound;
    }

    /**
     * Check if this plan requires questionnaire for given ages
     * @param array $ages Array of ages to check
     * @return bool
     */
    public function requiresQuestionnaireForAges(array $ages): bool
    {
        if (!$this->requires_questionnaire) {
            return false;
        }

        // Check if any person is 60+ (for Manulife questionnaire)
        foreach ($ages as $age) {
            if ($age >= 60) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get the appropriate rate table based on parameters
     * @param string $rateTableType Type of rate table needed
     * @param array $parameters Additional parameters (age, days, destination, etc.)
     * @return array|null
     */
    public function getRateTable(string $rateTableType, array $parameters = []): ?array
    {
        if (empty($this->rate_tables)) {
            return null;
        }

        // Check if we have the requested rate table type
        if (isset($this->rate_tables[$rateTableType])) {
            $table = $this->rate_tables[$rateTableType];

            // Apply any parameter-based filtering if needed
            return $this->filterRateTableByParameters($table, $parameters);
        }

        return null;
    }

    /**
     * Filter rate table by parameters
     * @param array $rateTable The rate table to filter
     * @param array $parameters Filter parameters
     * @return array
     */
    private function filterRateTableByParameters(array $rateTable, array $parameters): array
    {
        // Implement parameter-based filtering logic here
        // This could filter by age range, duration, destination, etc.
        
        // For now, return the full table
        // In production, this would implement the actual filtering logic
        return $rateTable;
    }

    /**
     * Calculate premium using rate tables
     * @param int $age Age of insured person
     * @param int $days Number of days
     * @param array $additionalParams Additional parameters
     * @return float|null
     */
    public function calculatePremiumFromRateTables(int $age, int $days, array $additionalParams = []): ?float
    {
        if (empty($this->rate_tables)) {
            return null;
        }

        // Determine which rate table to use based on parameters
        $rateTableType = $this->determineRateTableType($additionalParams);
        
        if (!$rateTableType) {
            return null;
        }

        $rateTable = $this->getRateTable($rateTableType, array_merge(['age' => $age, 'days' => $days], $additionalParams));
        
        if (!$rateTable) {
            return null;
        }

        // Find the appropriate rate based on age and duration
        $dailyRate = $this->findDailyRateInTable($rateTable, $age, $days);
        
        if (!$dailyRate) {
            return null;
        }

        return $dailyRate * $days;
    }

    /**
     * Determine which rate table type to use
     * @param array $parameters
     * @return string|null
     */
    private function determineRateTableType(array $parameters): ?string
    {
        // Extract relevant parameters
        $destination = $parameters['destination'] ?? null;
        $rateCategory = $parameters['rate_category'] ?? null;
        $companySlug = $this->company->slug ?? null;

        // Determine based on company and parameters
        if ($companySlug === 'jf') {
            return ($destination === 'worldwide_including_usa') ? 
                self::RATE_TABLE_JF_USA : self::RATE_TABLE_JF_NON_USA;
        } elseif ($companySlug === 'manulife') {
            return match($rateCategory) {
                'B' => self::RATE_TABLE_MANULIFE_B,
                'C' => self::RATE_TABLE_MANULIFE_C,
                default => self::RATE_TABLE_MANULIFE_A,
            };
        }

        return null;
    }

    /**
     * Find daily rate in rate table
     * @param array $rateTable
     * @param int $age
     * @param int $days
     * @return float|null
     */
    private function findDailyRateInTable(array $rateTable, int $age, int $days): ?float
    {
        // This is a simplified implementation
        // In production, this would need to match the actual rate table structure
        
        // Check for age bracket
        foreach ($rateTable as $ageBracket => $rates) {
            if ($this->isAgeInBracket($age, $ageBracket)) {
                // Find rate based on duration
                foreach ($rates as $durationRange => $rate) {
                    if ($this->isDaysInRange($days, $durationRange)) {
                        return (float) $rate;
                    }
                }
            }
        }

        return null;
    }

    /**
     * Check if age is in bracket
     * @param int $age
     * @param string $bracket
     * @return bool
     */
    private function isAgeInBracket(int $age, string $bracket): bool
    {
        if ($bracket === '85+') {
            return $age >= 85;
        }

        $parts = explode('-', $bracket);
        if (count($parts) === 2) {
            $min = (int) $parts[0];
            $max = (int) $parts[1];
            return $age >= $min && $age <= $max;
        }

        return false;
    }

    /**
     * Check if days are in range
     * @param int $days
     * @param string $range
     * @return bool
     */
    private function isDaysInRange(int $days, string $range): bool
    {
        $parts = explode('-', $range);
        if (count($parts) === 2) {
            $min = (int) $parts[0];
            $max = (int) $parts[1];
            return $days >= $min && $days <= $max;
        }

        // Handle open-ended ranges like "365+"
        if (str_ends_with($range, '+')) {
            $min = (int) str_replace('+', '', $range);
            return $days >= $min;
        }

        return false;
    }

    /**
     * Check if plan covers all given ages
     * @param array $ages
     * @return bool
     */
    public function coversAllAges(array $ages): bool
    {
        // First try using rate tables if available
        if (!empty($this->rate_tables)) {
            foreach ($ages as $age) {
                // Check if age is covered in any rate table
                $covered = false;
                
                foreach ($this->rate_tables as $rateTable) {
                    foreach ($rateTable as $ageBracket => $rates) {
                        if ($this->isAgeInBracket($age, $ageBracket)) {
                            $covered = true;
                            break 2;
                        }
                    }
                }
                
                if (!$covered) {
                    return false;
                }
            }
            return true;
        }

        // Fall back to original method using prices
        foreach ($ages as $age) {
            $matched = $this->prices->first(fn(Price $price) => 
                $price->age_from <= $age && (is_null($price->age_to) || $price->age_to >= $age));
            
            if (!$matched) {
                return false;
            }
        }
        
        return true;
    }

    /**
     * Get plans that support outbound insurance
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeSupportsOutbound($query)
    {
        return $query->where('supports_outbound', true);
    }

    /**
     * Get plans that support inbound insurance
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeSupportsInbound($query)
    {
        return $query->where('supports_inbound', true);
    }

    /**
     * Get plans that require questionnaire
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeRequiresQuestionnaire($query)
    {
        return $query->where('requires_questionnaire', true);
    }

    /**
     * Get plans by questionnaire type
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param string $questionnaireType
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeByQuestionnaireType($query, string $questionnaireType)
    {
        return $query->where('questionnaire_type', $questionnaireType);
    }

    /**
     * Get plans for specific insurance type
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param bool $isEntry
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeForInsuranceType($query, bool $isEntry)
    {
        return $isEntry 
            ? $query->where('supports_inbound', true)
            : $query->where('supports_outbound', true);
    }

    /**
     * Get active plans
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    /**
     * Get plans by company
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param int|string $companyIdOrSlug
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeByCompany($query, $companyIdOrSlug)
    {
        if (is_numeric($companyIdOrSlug)) {
            return $query->where('company_id', $companyIdOrSlug);
        }
        
        return $query->whereHas('company', function ($q) use ($companyIdOrSlug) {
            $q->where('slug', $companyIdOrSlug);
        });
    }

    /**
     * Get available destinations for this plan
     * @return array
     */
    public function getAvailableDestinations(): array
    {
        $destinations = [];
        
        // Add Canada if supports inbound or outbound
        if ($this->supports_inbound || $this->supports_outbound) {
            $destinations[] = 'canada';
        }
        
        // Add worldwide options based on rate tables
        if (!empty($this->rate_tables)) {
            if (isset($this->rate_tables[self::RATE_TABLE_JF_USA]) || 
                isset($this->rate_tables[self::RATE_TABLE_MANULIFE_A])) {
                $destinations[] = 'worldwide_including_usa';
            }
            
            if (isset($this->rate_tables[self::RATE_TABLE_JF_NON_USA])) {
                $destinations[] = 'worldwide_excluding_usa';
            }
        }
        
        return array_unique($destinations);
    }

    /**
     * Get deductible options for this plan
     * @return array
     */
    public function getDeductibleOptions(): array
    {
        $options = [
            0 => '$0',
            500 => '$500',
            1000 => '$1,000',
            5000 => '$5,000',
            10000 => '$10,000',
        ];
        
        // Plan-specific adjustments could be made here
        return $options;
    }

    /**
     * Check if plan is suitable for the given parameters
     * @param array $parameters
     * @return bool
     */
    public function isSuitableForParameters(array $parameters): bool
    {
        // Check insurance type support
        $isEntry = $parameters['is_entry'] ?? true;
        if (!$this->supportsInsuranceType($isEntry)) {
            return false;
        }
        
        // Check destination support
        $destination = $parameters['destination'] ?? null;
        if ($destination && !in_array($destination, $this->getAvailableDestinations())) {
            return false;
        }
        
        // Check age coverage
        $ages = $parameters['ages'] ?? [];
        if (!empty($ages) && !$this->coversAllAges($ages)) {
            return false;
        }
        
        return true;
    }

    /**
     * Relationship: Plan belongs to a Company.
     * @return BelongsTo
     */
    public function company(): BelongsTo
    {
        return $this->belongsTo(Company::class, 'company_id');
    }

    /**
     * Relationship: Plan has many Prices.
     * @return HasMany
     */
    public function prices(): HasMany
    {
        return $this->hasMany(Price::class, 'plan_id');
    }

    /**
     * Relationship: Plan has many Translations.
     * @return HasMany
     */
    public function translations(): HasMany
    {
        return $this->hasMany(PlanTranslation::class, 'plan_id');
    }

    /**
     * The attributes that should be cast.
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'is_active' => 'boolean',
            'requires_questionnaire' => 'boolean',
            'supports_outbound' => 'boolean',
            'supports_inbound' => 'boolean',
            'rate_tables' => 'array',
        ];
    }
}