<?php

namespace App\Http\Requests\Inquiry;

use App\Models\Inquiry;
use App\Models\Person;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class CalculateOutboundPremiumRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            // API authentication
            'apiKey' => 'required|string|exists:agents,id',
            'token' => 'required|string',
            
            // Trip details
            'started_at' => 'required|date|date_format:Y-m-d|after_or_equal:today',
            'ended_at' => 'required|date|date_format:Y-m-d|after_or_equal:started_at',
            'destination' => [
                'required',
                'string',
                Rule::in([
                    Inquiry::DESTINATION_CANADA,
                    Inquiry::DESTINATION_WORLDWIDE_EXCLUDING_USA,
                    Inquiry::DESTINATION_WORLDWIDE_INCLUDING_USA,
                ])
            ],
            'trip_purpose' => [
                'sometimes',
                'nullable',
                'string',
                Rule::in([
                    Inquiry::PURPOSE_TOURISM,
                    Inquiry::PURPOSE_BUSINESS,
                    Inquiry::PURPOSE_EDUCATION,
                    Inquiry::PURPOSE_MEDICAL,
                    Inquiry::PURPOSE_OTHER,
                ])
            ],
            'destination_country' => 'sometimes|nullable|string|max:100',
            
            // Insurance details
            'insurance_cover' => 'required|integer|min:1000|max:1000000',
            'deductible_amount' => 'sometimes|integer|in:0,500,1000,5000,10000',
            
            // People details
            'birthdays' => 'required|array|min:1|max:10',
            'birthdays.*' => 'required|date|date_format:Y-m-d|before_or_equal:today',
            
            // Optional: For returning calculated results
            'rate_categories' => 'sometimes|nullable|array',
            'rate_categories.*' => [
                'sometimes',
                'string',
                Rule::in([
                    Person::RATE_CATEGORY_A,
                    Person::RATE_CATEGORY_B,
                    Person::RATE_CATEGORY_C,
                ])
            ],
            'has_pre_existing_conditions' => 'sometimes|nullable|array',
            'has_pre_existing_conditions.*' => 'sometimes|boolean',
            'is_smokers' => 'sometimes|nullable|array',
            'is_smokers.*' => 'sometimes|boolean',
            'questionnaire_answers' => 'sometimes|nullable|array',
            
            // Company/plan filters (optional)
            'company_ids' => 'sometimes|nullable|array',
            'company_ids.*' => 'sometimes|integer|exists:companies,id',
            'plan_ids' => 'sometimes|nullable|array',
            'plan_ids.*' => 'sometimes|integer|exists:plans,id',
            
            // Language for response
            'language' => 'sometimes|string|in:en,zh,fa',
        ];
    }

    /**
     * Get custom messages for validator errors.
     *
     * @return array<string, string>
     */
    public function messages(): array
    {
        return [
            'destination.in' => 'The selected destination is invalid. Valid options are: Canada, Worldwide (excluding USA), Worldwide (including USA).',
            'trip_purpose.in' => 'The selected trip purpose is invalid. Valid options are: tourism, business, education, medical, other.',
            'rate_categories.*.in' => 'Invalid rate category. Valid options are: A, B, C.',
            'started_at.after_or_equal' => 'The trip start date must be today or in the future.',
            'ended_at.after_or_equal' => 'The trip end date must be after or equal to the start date.',
            'birthdays.*.before_or_equal' => 'The birthday must be today or in the past.',
            'insurance_cover.min' => 'Insurance cover must be at least $1,000.',
            'insurance_cover.max' => 'Insurance cover cannot exceed $1,000,000.',
            'deductible_amount.in' => 'Deductible amount must be one of: 0, 500, 1000, 5000, 10000.',
            'birthdays.max' => 'Maximum 10 people allowed per inquiry.',
        ];
    }

    /**
     * Get custom attributes for validator errors.
     *
     * @return array<string, string>
     */
    public function attributes(): array
    {
        return [
            'birthdays' => 'birthdays',
            'birthdays.*' => 'birthday',
            'rate_categories' => 'rate categories',
            'rate_categories.*' => 'rate category',
            'has_pre_existing_conditions' => 'pre-existing conditions',
            'has_pre_existing_conditions.*' => 'pre-existing condition',
            'is_smokers' => 'smoker status',
            'is_smokers.*' => 'smoker status',
            'company_ids' => 'company IDs',
            'company_ids.*' => 'company ID',
            'plan_ids' => 'plan IDs',
            'plan_ids.*' => 'plan ID',
        ];
    }

    /**
     * Prepare the data for validation.
     */
    protected function prepareForValidation(): void
    {
        // Get API key from header
        $apiKey = $this->header('API-Key');
        
        // Get language from header
        $language = $this->header('Accept-Language', 'en');
        
        // Get token from bearer token
        $token = $this->bearerToken();
        
        // Process comma-separated arrays from query parameters
        $this->merge([
            'apiKey' => $apiKey,
            'token' => $token,
            'started_at' => $this->query('started_at'),
            'ended_at' => $this->query('ended_at'),
            'destination' => $this->query('destination'),
            'trip_purpose' => $this->query('trip_purpose', Inquiry::PURPOSE_TOURISM),
            'destination_country' => $this->query('destination_country'),
            'insurance_cover' => (int) $this->query('insurance_cover'),
            'deductible_amount' => (int) $this->query('deductible_amount', 0),
            'birthdays' => $this->parseCommaSeparatedArray($this->query('birthdays')),
            'rate_categories' => $this->parseCommaSeparatedArray($this->query('rate_categories')),
            'has_pre_existing_conditions' => $this->parseCommaSeparatedArray($this->query('has_pre_existing_conditions'), true),
            'is_smokers' => $this->parseCommaSeparatedArray($this->query('is_smokers'), true),
            'company_ids' => $this->parseCommaSeparatedArray($this->query('company_ids'), false, true),
            'plan_ids' => $this->parseCommaSeparatedArray($this->query('plan_ids'), false, true),
            'language' => in_array($language, ['en', 'zh', 'fa']) ? $language : 'en',
        ]);
        
        // Parse questionnaire answers if provided
        if ($questionnaireAnswers = $this->query('questionnaire_answers')) {
            if (is_string($questionnaireAnswers)) {
                $decoded = json_decode($questionnaireAnswers, true);
                if (json_last_error() === JSON_ERROR_NONE) {
                    $this->merge(['questionnaire_answers' => $decoded]);
                }
            } elseif (is_array($questionnaireAnswers)) {
                $this->merge(['questionnaire_answers' => $questionnaireAnswers]);
            }
        }
        
        // Calculate trip duration
        if ($this->started_at && $this->ended_at) {
            try {
                $startDate = \Carbon\Carbon::parse($this->started_at);
                $endDate = \Carbon\Carbon::parse($this->ended_at);
                $tripDuration = $endDate->diffInDays($startDate) + 1; // Including both days
                $this->merge(['trip_duration' => $tripDuration]);
            } catch (\Exception $e) {
                // Duration calculation failed, will be handled by validation
            }
        }
    }

    /**
     * Parse comma-separated string into array.
     *
     * @param mixed $value
     * @param bool $isBoolean Whether to convert to boolean array
     * @param bool $isNumeric Whether to convert to numeric array
     * @return array
     */
    private function parseCommaSeparatedArray($value, bool $isBoolean = false, bool $isNumeric = false): array
    {
        if (is_array($value)) {
            // Already an array
            if ($isBoolean) {
                return array_map(function ($item) {
                    return filter_var($item, FILTER_VALIDATE_BOOLEAN);
                }, $value);
            }
            if ($isNumeric) {
                return array_map(function ($item) {
                    return (int) $item;
                }, $value);
            }
            return $value;
        }
        
        if (is_string($value)) {
            // Comma-separated string
            $array = explode(',', $value);
            $array = array_map('trim', $array);
            $array = array_filter($array); // Remove empty values
            
            if ($isBoolean) {
                return array_map(function ($item) {
                    return filter_var($item, FILTER_VALIDATE_BOOLEAN);
                }, $array);
            }
            
            if ($isNumeric) {
                return array_map(function ($item) {
                    return (int) $item;
                }, $array);
            }
            
            return $array;
        }
        
        return [];
    }

    /**
     * Get validated data with additional processing.
     *
     * @return array
     */
    public function validatedData(): array
    {
        $validated = $this->validated();
        
        // Calculate ages from birthdays
        $ages = [];
        foreach ($validated['birthdays'] as $birthday) {
            $birthDate = \Carbon\Carbon::parse($birthday);
            $age = now()->diffInYears($birthDate);
            $ages[] = $age;
        }
        
        $validated['ages'] = $ages;
        
        // Calculate trip duration
        $startDate = \Carbon\Carbon::parse($validated['started_at']);
        $endDate = \Carbon\Carbon::parse($validated['ended_at']);
        $validated['trip_duration'] = $endDate->diffInDays($startDate) + 1;
        
        // Set default values for optional arrays
        $count = count($validated['birthdays']);
        
        $optionalArrays = [
            'rate_categories' => Person::RATE_CATEGORY_A,
            'has_pre_existing_conditions' => false,
            'is_smokers' => false,
        ];
        
        foreach ($optionalArrays as $field => $default) {
            if (!isset($validated[$field]) || !is_array($validated[$field])) {
                $validated[$field] = array_fill(0, $count, $default);
            } elseif (count($validated[$field]) !== $count) {
                $validated[$field] = array_pad($validated[$field], $count, $default);
            }
        }
        
        // Ensure questionnaire answers is an array
        if (!isset($validated['questionnaire_answers']) || !is_array($validated['questionnaire_answers'])) {
            $validated['questionnaire_answers'] = [];
        }
        
        // Set is_entry to false for outbound insurance
        $validated['is_entry'] = false;
        
        return $validated;
    }

    /**
     * Get the age of each person based on birthdays.
     *
     * @return array
     */
    public function getAges(): array
    {
        $birthdays = $this->validated('birthdays', []);
        $ages = [];
        
        foreach ($birthdays as $birthday) {
            $birthDate = \Carbon\Carbon::parse($birthday);
            $age = now()->diffInYears($birthDate);
            $ages[] = $age;
        }
        
        return $ages;
    }

    /**
     * Check if any person requires questionnaire (age 60+).
     *
     * @return bool
     */
    public function requiresQuestionnaire(): bool
    {
        $ages = $this->getAges();
        
        foreach ($ages as $age) {
            if ($age >= Person::QUESTIONNAIRE_MIN_AGE) {
                return true;
            }
        }
        
        return false;
    }

    /**
     * Get the number of people in the inquiry.
     *
     * @return int
     */
    public function getNumberOfPeople(): int
    {
        return count($this->validated('birthdays', []));
    }

    /**
     * Get the trip duration in days.
     *
     * @return int
     */
    public function getTripDuration(): int
    {
        try {
            $startDate = \Carbon\Carbon::parse($this->validated('started_at'));
            $endDate = \Carbon\Carbon::parse($this->validated('ended_at'));
            return $endDate->diffInDays($startDate) + 1;
        } catch (\Exception $e) {
            return 0;
        }
    }

    /**
     * Determine if USA coverage is needed based on destination.
     *
     * @return bool
     */
    public function needsUsaCoverage(): bool
    {
        $destination = $this->validated('destination');
        return $destination === Inquiry::DESTINATION_WORLDWIDE_INCLUDING_USA;
    }

    /**
     * Determine if it's Canada-only destination.
     *
     * @return bool
     */
    public function isCanadaOnly(): bool
    {
        $destination = $this->validated('destination');
        return $destination === Inquiry::DESTINATION_CANADA;
    }

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

    /**
     * Get trip purpose label for display.
     *
     * @return string
     */
    public function getTripPurposeLabel(): string
    {
        $purpose = $this->validated('trip_purpose', Inquiry::PURPOSE_TOURISM);
        $options = Inquiry::getTripPurposeOptions();
        
        return $options[$purpose] ?? ucfirst($purpose);
    }
}