<?php

namespace App\Models;

use App\Helpers\AesHelper;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;

/**
 * Class Inquiry
 * @package App\Models
 *
 * @property string $id
 * @property string $reference_number
 * @property int $agent_id
 * @property int $city_id
 * @property int $insurance_cover
 * @property string|null $destination
 * @property int|null $trip_duration
 * @property float|null $base_premium
 * @property float|null $total_premium
 * @property float $tax_amount
 * @property float|null $final_amount
 * @property string $address
 * @property string $postal_code
 * @property string $phone
 * @property string $email
 * @property string $card_number
 * @property string $card_cvv
 * @property string $card_name
 * @property string $card_expiration
 * @property string $transfer_password
 * @property string $message
 * @property string $beneficiary_first_name
 * @property string $beneficiary_last_name
 * @property Carbon $beneficiary_born_at
 * @property Carbon $started_at
 * @property Carbon $ended_at
 * @property Carbon $arrived_at
 * @property Carbon $inquired_at
 * @property Carbon $reviewed_at
 * @property Carbon $local_submitted_at
 * @property Carbon|null $questionnaire_completed_at
 * @property bool $is_beneficiary_male
 * @property bool $is_entry
 * @property bool $is_deal
 * @property bool $canada_half_price_applied
 * @property bool $has_smoking_surcharge
 * @property bool $questionnaire_required
 * @property bool $questionnaire_completed
 * @property string $status
 * @property int $number_of_people
 * @property string|null $destination_country
 * @property string|null $trip_purpose
 *
 * @property Agent $agent
 * @property City $city
 * @property Collection<Person> $people
 */
class Inquiry extends Model
{
    use HasUlids;

    /**
     * Destination types for travel insurance
     */
    const DESTINATION_CANADA = 'canada';
    const DESTINATION_WORLDWIDE_EXCLUDING_USA = 'worldwide_excluding_usa';
    const DESTINATION_WORLDWIDE_INCLUDING_USA = 'worldwide_including_usa';

    /**
     * Trip purposes
     */
    const PURPOSE_TOURISM = 'tourism';
    const PURPOSE_BUSINESS = 'business';
    const PURPOSE_EDUCATION = 'education';
    const PURPOSE_MEDICAL = 'medical';
    const PURPOSE_OTHER = 'other';

    /**
     * Status constants
     */
    const STATUS_PENDING = 'pending';
    const STATUS_UNDER_REVIEW = 'under_review';
    const STATUS_APPROVED = 'approved';
    const STATUS_REJECTED = 'rejected';
    const STATUS_COMPLETED = 'completed';

    /**
     * Indicates if the model should be timestamped.
     * @var bool
     */
    public $timestamps = false;

    /**
     * The table associated with the model.
     * @var string
     */
    protected $table = 'inquiries';

    /**
     * The attributes that are mass assignable.
     * @var array<int, string>
     */
    protected $fillable = [
        'id',
        'reference_number',
        'agent_id',
        'city_id',
        'insurance_cover',
        'destination',
        'trip_duration',
        'base_premium',
        'total_premium',
        'tax_amount',
        'final_amount',
        'address',
        'postal_code',
        'phone',
        'email',
        'card_number',
        'card_cvv',
        'card_name',
        'card_expiration',
        'transfer_password',
        'message',
        'beneficiary_first_name',
        'beneficiary_last_name',
        'beneficiary_born_at',
        'started_at',
        'ended_at',
        'arrived_at',
        'inquired_at',
        'reviewed_at',
        'local_submitted_at',
        'questionnaire_completed_at',
        'is_beneficiary_male',
        'is_entry',
        'is_deal',
        'canada_half_price_applied',
        'has_smoking_surcharge',
        'questionnaire_required',
        'questionnaire_completed',
        'status',
        'number_of_people',
        'destination_country',
        'trip_purpose',
    ];

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

        // Generate reference number before creating
        static::creating(function (Inquiry $inquiry) {
            if (empty($inquiry->reference_number)) {
                $inquiry->reference_number = 'INQ-' . strtoupper(Str::random(8));
            }
            
            // Set local submission time
            if (empty($inquiry->local_submitted_at)) {
                $inquiry->local_submitted_at = now();
            }
            
            // Set inquiry date if not provided
            if (empty($inquiry->inquired_at)) {
                $inquiry->inquired_at = now();
            }
            
            // Initialize status as pending if not set
            if (empty($inquiry->status)) {
                $inquiry->status = self::STATUS_PENDING;
            }
            
            // Set default number of people to 1 if not specified
            if (empty($inquiry->number_of_people)) {
                $inquiry->number_of_people = 1;
            }
            
            // Initialize boolean fields
            $booleanFields = [
                'is_deal' => false,
                'canada_half_price_applied' => false,
                'has_smoking_surcharge' => false,
                'questionnaire_required' => false,
                'questionnaire_completed' => false,
            ];
            
            foreach ($booleanFields as $field => $default) {
                if (!isset($inquiry->{$field})) {
                    $inquiry->{$field} = $default;
                }
            }
            
            // Initialize financial fields
            $financialFields = [
                'tax_amount' => 0,
                'base_premium' => 0,
                'total_premium' => 0,
                'final_amount' => 0,
            ];
            
            foreach ($financialFields as $field => $default) {
                if (!isset($inquiry->{$field}) || is_null($inquiry->{$field})) {
                    $inquiry->{$field} = $default;
                }
            }
            
            // Set default dates if not provided
            $dateFields = ['started_at', 'ended_at', 'arrived_at'];
            foreach ($dateFields as $field) {
                if (empty($inquiry->{$field})) {
                    $inquiry->{$field} = now();
                }
            }
            
            // Calculate trip duration if not set
            if (empty($inquiry->trip_duration) && $inquiry->started_at && $inquiry->ended_at) {
                $inquiry->trip_duration = $inquiry->getInsuranceDays();
            }
        });

        // After creating, calculate total premium if not set
        static::created(function (Inquiry $inquiry) {
            if (empty($inquiry->total_premium) || $inquiry->total_premium == 0) {
                $inquiry->calculateTotalPremium();
            }
        });

        // Before saving, ensure number_of_people matches actual count
        static::saving(function (Inquiry $inquiry) {
            if ($inquiry->people && $inquiry->people->count() > 0) {
                $inquiry->number_of_people = $inquiry->people->count();
            }
        });
    }

    /**
     * Get the decrypted transfer password.
     * @param string|null $value
     * @return string|null
     */
    public function getTransferPasswordAttribute(?string $value): ?string
    {
        return $value ? AesHelper::decrypt($value) : null;
    }

    /**
     * Set the encrypted transfer password.
     * @param string|null $value
     * @return void
     */
    public function setTransferPasswordAttribute(?string $value): void
    {
        $this->attributes['transfer_password'] = $value ? AesHelper::encrypt($value) : null;
    }

    /**
     * Get destination options
     * @return array
     */
    public static function getDestinationOptions(): array
    {
        return [
            self::DESTINATION_CANADA => 'Canada (Out of Province)',
            self::DESTINATION_WORLDWIDE_EXCLUDING_USA => 'Worldwide (Excluding USA)',
            self::DESTINATION_WORLDWIDE_INCLUDING_USA => 'Worldwide (Including USA)',
        ];
    }

    /**
     * Get trip purpose options
     * @return array
     */
    public static function getTripPurposeOptions(): array
    {
        return [
            self::PURPOSE_TOURISM => 'Tourism',
            self::PURPOSE_BUSINESS => 'Business',
            self::PURPOSE_EDUCATION => 'Education',
            self::PURPOSE_MEDICAL => 'Medical',
            self::PURPOSE_OTHER => 'Other',
        ];
    }

    /**
     * Get status options
     * @return array
     */
    public static function getStatusOptions(): array
    {
        return [
            self::STATUS_PENDING => 'Pending',
            self::STATUS_UNDER_REVIEW => 'Under Review',
            self::STATUS_APPROVED => 'Approved',
            self::STATUS_REJECTED => 'Rejected',
            self::STATUS_COMPLETED => 'Completed',
        ];
    }

    /**
     * Get destination label
     * @return string
     */
    public function getDestinationLabel(): string
    {
        $options = self::getDestinationOptions();
        return $options[$this->destination] ?? ucfirst(str_replace('_', ' ', $this->destination));
    }

    /**
     * Get trip purpose label
     * @return string
     */
    public function getTripPurposeLabel(): string
    {
        $options = self::getTripPurposeOptions();
        return $options[$this->trip_purpose] ?? ucfirst($this->trip_purpose);
    }

    /**
     * Relationship: Inquiry belongs to an Agent.
     * @return BelongsTo
     */
    public function agent(): BelongsTo
    {
        return $this->belongsTo(Agent::class, 'agent_id');
    }

    /**
     * Relationship: Inquiry belongs to a City.
     * @return BelongsTo
     */
    public function city(): BelongsTo
    {
        return $this->belongsTo(City::class, 'city_id');
    }

    /**
     * Relationship: Inquiry has many People (insured persons).
     * @return HasMany
     */
    public function people(): HasMany
    {
        return $this->hasMany(Person::class, 'inquiry_id');
    }

    /**
     * Get ages of all insured persons.
     * @return array<int>
     */
    public function getPeopleAges(): array
    {
        $now = now();
        return $this->people
            ->map(fn(Person $person) => intval($person->born_at->diffInYears($now)))
            ->toArray();
    }

    /**
     * Calculate the total premium based on insured persons and duration.
     * @return float
     */
    public function calculateTotalPremium(): float
    {
        $total = 0;
        
        foreach ($this->people as $person) {
            if ($person->price && $person->price->daily_cost) {
                $days = $this->getInsuranceDays();
                $total += $person->price->daily_cost * $days;
            }
        }
        
        $this->total_premium = round($total, 2);
        
        // Calculate final amount including tax
        $this->final_amount = $this->total_premium + $this->tax_amount;
        
        $this->saveQuietly(); // Save without triggering events
        
        return $this->total_premium;
    }

    /**
     * Calculate the number of insurance days.
     * @return int
     */
    public function getInsuranceDays(): int
    {
        if (!$this->started_at || !$this->ended_at) {
            return 0;
        }
        
        return $this->started_at->diffInDays($this->ended_at) + 1;
    }

    /**
     * Get the insurance duration in a formatted string.
     * @return string
     */
    public function getInsuranceDurationFormatted(): string
    {
        $days = $this->getInsuranceDays();
        return "{$days} day" . ($days !== 1 ? 's' : '');
    }

    /**
     * Get the status in a user-friendly format.
     * @return string
     */
    public function getStatusFormatted(): string
    {
        return ucfirst(str_replace('_', ' ', $this->status));
    }

    /**
     * Get the insurance type in a user-friendly format.
     * @return string
     */
    public function getInsuranceTypeFormatted(): string
    {
        return $this->is_entry ? 'Entry to Canada' : 'Exit from Canada';
    }

    /**
     * Get the insurance type in Persian.
     * @return string
     */
    public function getInsuranceTypeFa(): string
    {
        return $this->is_entry ? 'ورود به کانادا' : 'خروج از کانادا';
    }

    /**
     * Get the status in Persian.
     * @return string
     */
    public function getStatusFa(): string
    {
        return match($this->status) {
            self::STATUS_PENDING => 'در انتظار',
            self::STATUS_UNDER_REVIEW => 'در حال بررسی',
            self::STATUS_APPROVED => 'تایید شده',
            self::STATUS_REJECTED => 'رد شده',
            self::STATUS_COMPLETED => 'تکمیل شده',
            default => $this->status,
        };
    }

    /**
     * Get translated status based on language code.
     * @param string|null $languageCode
     * @return string
     */
    public function getTranslatedStatus(?string $languageCode = null): string
    {
        if (!$languageCode) {
            $languageCode = request()->header('Accept-Language', 'en');
        }
        
        return match($languageCode) {
            'zh' => match($this->status) {
                self::STATUS_PENDING => '待处理',
                self::STATUS_UNDER_REVIEW => '审核中',
                self::STATUS_APPROVED => '已批准',
                self::STATUS_REJECTED => '已拒绝',
                self::STATUS_COMPLETED => '已完成',
                default => $this->status,
            },
            'fa' => $this->getStatusFa(),
            default => $this->getStatusFormatted(),
        };
    }

    /**
     * Get translated insurance type based on language code.
     * @param string|null $languageCode
     * @return string
     */
    public function getTranslatedInsuranceType(?string $languageCode = null): string
    {
        if (!$languageCode) {
            $languageCode = request()->header('Accept-Language', 'en');
        }
        
        return match($languageCode) {
            'zh' => $this->is_entry ? '进入加拿大' : '离开加拿大',
            'fa' => $this->getInsuranceTypeFa(),
            default => $this->getInsuranceTypeFormatted(),
        };
    }

    /**
     * Check if this inquiry needs a medical questionnaire.
     * @return bool
     */
    public function needsQuestionnaire(): bool
    {
        foreach ($this->people as $person) {
            if ($person->age >= 60) {
                return true;
            }
        }
        return false;
    }

    /**
     * Get all people who need a questionnaire (age 60+).
     * @return Collection<Person>
     */
    public function getPeopleNeedingQuestionnaire(): Collection
    {
        return $this->people->filter(function ($person) {
            return $person->age >= 60;
        });
    }

    /**
     * Check if all required questionnaires are completed.
     * @return bool
     */
    public function allQuestionnairesCompleted(): bool
    {
        foreach ($this->people as $person) {
            if ($person->age >= 60 && !$person->questionnaire_completed) {
                return false;
            }
        }
        return true;
    }

    /**
     * Get the primary insured person.
     * @return Person|null
     */
    public function getPrimaryPerson(): ?Person
    {
        return $this->people->where('is_primary', true)->first();
    }

    /**
     * Format monetary amounts for display.
     * @param float|null $amount
     * @param string $currency
     * @return string
     */
    public static function formatMoney(?float $amount, string $currency = 'CAD'): string
    {
        if ($amount === null) {
            return 'N/A';
        }
        
        $symbol = match(strtoupper($currency)) {
            'USD' => '$',
            'EUR' => '€',
            'GBP' => '£',
            default => '$',
        };
        
        return $symbol . number_format($amount, 2);
    }

    /**
     * Get the formatted total premium.
     * @return string
     */
    public function getTotalPremiumFormatted(): string
    {
        return self::formatMoney($this->total_premium, 'CAD');
    }

    /**
     * Get the formatted base premium.
     * @return string
     */
    public function getBasePremiumFormatted(): string
    {
        return self::formatMoney($this->base_premium, 'CAD');
    }

    /**
     * Get the formatted final amount.
     * @return string
     */
    public function getFinalAmountFormatted(): string
    {
        return self::formatMoney($this->final_amount, 'CAD');
    }

    /**
     * Get the formatted tax amount.
     * @return string
     */
    public function getTaxAmountFormatted(): string
    {
        return self::formatMoney($this->tax_amount, 'CAD');
    }

    /**
     * Get the insurance cover in a formatted string.
     * @return string
     */
    public function getInsuranceCoverFormatted(): string
    {
        return self::formatMoney($this->insurance_cover, 'CAD');
    }

    /**
     * Check if this inquiry can be edited.
     * @return bool
     */
    public function canBeEdited(): bool
    {
        return in_array($this->status, [self::STATUS_PENDING, self::STATUS_UNDER_REVIEW]);
    }

    /**
     * Check if this inquiry can be deleted.
     * @return bool
     */
    public function canBeDeleted(): bool
    {
        return $this->status === self::STATUS_PENDING;
    }

    /**
     * Get the inquiry submission date in a formatted string.
     * @param string $format
     * @return string
     */
    public function getSubmittedDate(string $format = 'Y-m-d H:i:s'): string
    {
        return $this->inquired_at->format($format);
    }

    /**
     * Get the local submission date in a formatted string.
     * @param string $format
     * @return string
     */
    public function getLocalSubmittedDate(string $format = 'Y-m-d H:i:s'): string
    {
        return $this->local_submitted_at?->format($format) ?? 'N/A';
    }

    /**
     * Get questionnaire completion date in a formatted string.
     * @param string $format
     * @return string
     */
    public function getQuestionnaireCompletedDate(string $format = 'Y-m-d H:i:s'): string
    {
        return $this->questionnaire_completed_at?->format($format) ?? 'N/A';
    }

    /**
     * Get destination country formatted.
     * @return string
     */
    public function getDestinationCountryFormatted(): string
    {
        if (!$this->destination_country) {
            return 'Not specified';
        }
        
        return ucwords(str_replace('_', ' ', $this->destination_country));
    }

    /**
     * Get trip purpose formatted.
     * @return string
     */
    public function getTripPurposeFormatted(): string
    {
        if (!$this->trip_purpose) {
            return 'Not specified';
        }
        
        return ucfirst($this->trip_purpose);
    }

    /**
     * Check if this is an exit insurance inquiry.
     * @return bool
     */
    public function isExitInsurance(): bool
    {
        return !$this->is_entry;
    }

    /**
     * Check if this is an entry insurance inquiry.
     * @return bool
     */
    public function isEntryInsurance(): bool
    {
        return $this->is_entry;
    }

    /**
     * Check if destination includes USA.
     * @return bool
     */
    public function includesUsa(): bool
    {
        return $this->destination === self::DESTINATION_WORLDWIDE_INCLUDING_USA;
    }

    /**
     * Check if destination is Canada only.
     * @return bool
     */
    public function isCanadaOnly(): bool
    {
        return $this->destination === self::DESTINATION_CANADA;
    }

    /**
     * Calculate trip duration in days.
     * @return int
     */
    public function calculateTripDuration(): int
    {
        if ($this->trip_duration) {
            return $this->trip_duration;
        }
        
        return $this->getInsuranceDays();
    }

    /**
     * Mark questionnaire as completed.
     * @return void
     */
    public function markQuestionnaireCompleted(): void
    {
        $this->questionnaire_completed = true;
        $this->questionnaire_completed_at = now();
        $this->save();
    }

    /**
     * Get card expiration in MM/YY format.
     * @return string|null
     */
    public function getCardExpirationFormatted(): ?string
    {
        if (strlen($this->card_expiration) === 4) {
            $year = substr($this->card_expiration, 0, 2);
            $month = substr($this->card_expiration, 2, 2);
            return $month . '/' . $year;
        }
        
        return $this->card_expiration;
    }

    /**
     * Scope: Only pending inquiries.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePending($query)
    {
        return $query->where('status', self::STATUS_PENDING);
    }

    /**
     * Scope: Only approved inquiries.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeApproved($query)
    {
        return $query->where('status', self::STATUS_APPROVED);
    }

    /**
     * Scope: Only entry insurance inquiries.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeEntry($query)
    {
        return $query->where('is_entry', true);
    }

    /**
     * Scope: Only exit insurance inquiries.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeExit($query)
    {
        return $query->where('is_entry', false);
    }

    /**
     * Scope: Inquiries within a date range.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param Carbon $startDate
     * @param Carbon $endDate
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeBetweenDates($query, Carbon $startDate, Carbon $endDate)
    {
        return $query->whereBetween('inquired_at', [$startDate, $endDate]);
    }

    /**
     * Scope: Inquiries for a specific agent.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param int $agentId
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeForAgent($query, int $agentId)
    {
        return $query->where('agent_id', $agentId);
    }

    /**
     * Scope: Inquiries that need questionnaire.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeNeedsQuestionnaire($query)
    {
        return $query->where('questionnaire_required', true)
                    ->where('questionnaire_completed', false);
    }

    /**
     * Scope: Inquiries with specific destination.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param string $destination
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeDestination($query, string $destination)
    {
        return $query->where('destination', $destination);
    }

    /**
     * Scope: Inquiries with USA coverage.
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeIncludesUsa($query)
    {
        return $query->where('destination', self::DESTINATION_WORLDWIDE_INCLUDING_USA);
    }

    /**
     * The attributes that should be cast.
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'beneficiary_born_at' => 'datetime',
            'started_at' => 'datetime',
            'ended_at' => 'datetime',
            'arrived_at' => 'datetime',
            'inquired_at' => 'datetime',
            'reviewed_at' => 'datetime',
            'local_submitted_at' => 'datetime',
            'questionnaire_completed_at' => 'datetime',
            'is_beneficiary_male' => 'boolean',
            'is_entry' => 'boolean',
            'is_deal' => 'boolean',
            'canada_half_price_applied' => 'boolean',
            'has_smoking_surcharge' => 'boolean',
            'questionnaire_required' => 'boolean',
            'questionnaire_completed' => 'boolean',
            'base_premium' => 'decimal:2',
            'total_premium' => 'decimal:2',
            'tax_amount' => 'decimal:2',
            'final_amount' => 'decimal:2',
        ];
    }
}