<?php

namespace App\Http\Requests\Inquiry;

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

class SubmitOutboundInquiryRequest 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 [
            // Basic inquiry information
            'address' => 'required|string|max:500',
            'apiKey' => 'required|string|exists:agents,id',
            'birthdays' => 'required|array|min:1',
            'birthdays.*' => 'required|date|date_format:Y-m-d|before_or_equal:today',
            'card_cvv' => 'sometimes|nullable|string|size:3',
            'card_expiration' => 'sometimes|nullable|date_format:Y-m|after_or_equal:today',
            'card_name' => 'sometimes|nullable|string|max:100',
            'card_number' => 'sometimes|nullable|string|regex:/^[0-9]{16}$/',
            'city' => 'required|string|max:255',
            'deductible_ids' => 'required|array|min:1',
            'deductible_ids.*' => 'required|integer|exists:deductibles,id',
            'destination' => [
                'required',
                'string',
                Rule::in([
                    Inquiry::DESTINATION_CANADA,
                    Inquiry::DESTINATION_WORLDWIDE_EXCLUDING_USA,
                    Inquiry::DESTINATION_WORLDWIDE_INCLUDING_USA,
                ])
            ],
            'email' => 'required|email:rfc,dns|max:255',
            'ended_at' => 'required|date|date_format:Y-m-d|after_or_equal:started_at',
            'first_names' => 'required|array|min:1',
            'first_names.*' => 'required|string|max:100',
            'genders' => 'required|array|min:1',
            'genders.*' => ['required', 'string', Rule::in(['Male', 'Female'])],
            'insurance_cover' => 'required|integer|min:1000|max:1000000',
            'last_names' => 'required|array|min:1',
            'last_names.*' => 'required|string|max:100',
            'message' => 'sometimes|nullable|string|max:2000',
            'phone' => 'required|string|max:20',
            'postal_code' => 'required|string|max:20',
            'price_ids' => 'required|array|min:1',
            'price_ids.*' => 'required|integer|exists:prices,id',
            'province_id' => 'required|integer|exists:provinces,id',
            'started_at' => 'required|date|date_format:Y-m-d|after_or_equal:today',
            'token' => 'required|string',
            'transfer_password' => 'sometimes|nullable|string|max:50',
            'trip_purpose' => [
                'sometimes',
                'nullable',
                'string',
                Rule::in([
                    Inquiry::PURPOSE_TOURISM,
                    Inquiry::PURPOSE_BUSINESS,
                    Inquiry::PURPOSE_EDUCATION,
                    Inquiry::PURPOSE_MEDICAL,
                    Inquiry::PURPOSE_OTHER,
                ])
            ],
            
            // Additional fields for outbound insurance
            'destination_country' => 'sometimes|nullable|string|max:100',
            'has_smoking_surcharge' => 'sometimes|boolean',
            'canada_half_price_applied' => 'sometimes|boolean',
            'base_premium' => 'sometimes|numeric|min:0',
            'total_premium' => 'sometimes|numeric|min:0',
            'tax_amount' => 'sometimes|numeric|min:0',
            'final_amount' => 'sometimes|numeric|min:0',
            
            // Questionnaire related fields
            'questionnaire_required' => 'sometimes|boolean',
            'questionnaire_completed' => 'sometimes|boolean',
            'questionnaire_answers' => 'sometimes|nullable|array',
            
            // Individual person data for age 60+ (optional, for batch processing)
            'individual_premiums' => 'sometimes|nullable|array',
            'individual_premiums.*' => 'sometimes|numeric|min:0',
            'rate_categories' => 'sometimes|nullable|array',
            'rate_categories.*' => [
                'sometimes',
                'string',
                Rule::in([
                    Person::RATE_CATEGORY_A,
                    Person::RATE_CATEGORY_B,
                    Person::RATE_CATEGORY_C,
                ])
            ],
            'has_smoking_surcharges' => 'sometimes|nullable|array',
            'has_smoking_surcharges.*' => 'sometimes|boolean',
            'is_eligible_persons' => 'sometimes|nullable|array',
            'is_eligible_persons.*' => 'sometimes|boolean',
            'ineligibility_reasons' => 'sometimes|nullable|array',
            'ineligibility_reasons.*' => 'sometimes|string|max:500',
            'has_pre_existing_conditions' => 'sometimes|nullable|array',
            'has_pre_existing_conditions.*' => 'sometimes|boolean',
            'is_smokers' => 'sometimes|nullable|array',
            'is_smokers.*' => 'sometimes|boolean',
            'deductible_amounts' => 'sometimes|nullable|array',
            'deductible_amounts.*' => 'sometimes|integer|min:0',
            'questionnaire_completions' => 'sometimes|nullable|array',
            'questionnaire_completions.*' => 'sometimes|boolean',
            'questionnaire_data' => 'sometimes|nullable|array',
            'questionnaire_data.*' => 'sometimes|array',
            
            // Validation for array sizes
            'birthdays' => 'required|array|size:' . count($this->input('first_names', [])),
            'deductible_ids' => 'required|array|size:' . count($this->input('first_names', [])),
            'genders' => 'required|array|size:' . count($this->input('first_names', [])),
            'last_names' => 'required|array|size:' . count($this->input('first_names', [])),
            'price_ids' => 'required|array|size:' . count($this->input('first_names', [])),
        ];
    }

    /**
     * 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.',
            'birthdays.size' => 'The number of birthdays must match the number of people.',
            'deductible_ids.size' => 'The number of deductible IDs must match the number of people.',
            'genders.size' => 'The number of genders must match the number of people.',
            'last_names.size' => 'The number of last names must match the number of people.',
            'price_ids.size' => 'The number of price IDs must match the number of people.',
            '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.',
            'card_expiration.after_or_equal' => 'The card expiration date must be in the future.',
            '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.',
        ];
    }

    /**
     * Get custom attributes for validator errors.
     *
     * @return array<string, string>
     */
    public function attributes(): array
    {
        return [
            'birthdays' => 'birthdays',
            'birthdays.*' => 'birthday',
            'deductible_ids' => 'deductible IDs',
            'deductible_ids.*' => 'deductible ID',
            'first_names' => 'first names',
            'first_names.*' => 'first name',
            'genders' => 'genders',
            'genders.*' => 'gender',
            'last_names' => 'last names',
            'last_names.*' => 'last name',
            'price_ids' => 'price IDs',
            'price_ids.*' => 'price ID',
            'individual_premiums' => 'individual premiums',
            'individual_premiums.*' => 'individual premium',
            'rate_categories' => 'rate categories',
            'rate_categories.*' => 'rate category',
            'has_smoking_surcharges' => 'smoking surcharges',
            'has_smoking_surcharges.*' => 'smoking surcharge',
            'is_eligible_persons' => 'eligibility status',
            'is_eligible_persons.*' => 'eligibility status',
            'ineligibility_reasons' => 'ineligibility reasons',
            'ineligibility_reasons.*' => 'ineligibility reason',
            'has_pre_existing_conditions' => 'pre-existing conditions',
            'has_pre_existing_conditions.*' => 'pre-existing condition',
            'is_smokers' => 'smoker status',
            'is_smokers.*' => 'smoker status',
            'deductible_amounts' => 'deductible amounts',
            'deductible_amounts.*' => 'deductible amount',
            'questionnaire_completions' => 'questionnaire completions',
            'questionnaire_completions.*' => 'questionnaire completion',
            'questionnaire_data' => 'questionnaire data',
            'questionnaire_data.*' => 'questionnaire data',
        ];
    }

    /**
     * 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 form data
        $this->merge([
            'address' => $this->input('address'),
            'apiKey' => $apiKey,
            'birthdays' => $this->parseCommaSeparatedArray('birthdays'),
            'card_cvv' => $this->input('card_cvv'),
            'card_expiration' => $this->input('card_expiration'),
            'card_name' => $this->input('card_name'),
            'card_number' => $this->input('card_number'),
            'city' => $this->input('city'),
            'deductible_ids' => $this->parseCommaSeparatedArray('deductible_ids'),
            'destination' => $this->input('destination'),
            'email' => $this->input('email'),
            'ended_at' => $this->input('ended_at'),
            'first_names' => $this->parseCommaSeparatedArray('first_names'),
            'genders' => $this->parseCommaSeparatedArray('genders'),
            'insurance_cover' => (int) $this->input('insurance_cover'),
            'last_names' => $this->parseCommaSeparatedArray('last_names'),
            'message' => $this->input('message'),
            'phone' => $this->input('phone'),
            'postal_code' => $this->input('postal_code'),
            'price_ids' => $this->parseCommaSeparatedArray('price_ids'),
            'province_id' => (int) $this->input('province_id'),
            'started_at' => $this->input('started_at'),
            'token' => $token,
            'transfer_password' => $this->input('transfer_password'),
            'trip_purpose' => $this->input('trip_purpose', Inquiry::PURPOSE_TOURISM),
            'destination_country' => $this->input('destination_country'),
            
            // Additional fields with defaults
            'has_smoking_surcharge' => $this->input('has_smoking_surcharge', false),
            'canada_half_price_applied' => $this->input('canada_half_price_applied', false),
            'base_premium' => (float) $this->input('base_premium', 0),
            'total_premium' => (float) $this->input('total_premium', 0),
            'tax_amount' => (float) $this->input('tax_amount', 0),
            'final_amount' => (float) $this->input('final_amount', 0),
            
            // Questionnaire fields
            'questionnaire_required' => $this->input('questionnaire_required', false),
            'questionnaire_completed' => $this->input('questionnaire_completed', false),
            'questionnaire_answers' => $this->input('questionnaire_answers', []),
            
            // Individual person data
            'individual_premiums' => $this->parseCommaSeparatedArray('individual_premiums', true),
            'rate_categories' => $this->parseCommaSeparatedArray('rate_categories'),
            'has_smoking_surcharges' => $this->parseCommaSeparatedArray('has_smoking_surcharges', true),
            'is_eligible_persons' => $this->parseCommaSeparatedArray('is_eligible_persons', true),
            'ineligibility_reasons' => $this->parseCommaSeparatedArray('ineligibility_reasons'),
            'has_pre_existing_conditions' => $this->parseCommaSeparatedArray('has_pre_existing_conditions', true),
            'is_smokers' => $this->parseCommaSeparatedArray('is_smokers', true),
            'deductible_amounts' => $this->parseCommaSeparatedArray('deductible_amounts', true),
            'questionnaire_completions' => $this->parseCommaSeparatedArray('questionnaire_completions', true),
            'questionnaire_data' => $this->input('questionnaire_data', []),
        ]);
        
        // Calculate trip duration if not provided
        if ($this->started_at && $this->ended_at && !$this->input('trip_duration')) {
            $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]);
        } else {
            $this->merge(['trip_duration' => (int) $this->input('trip_duration', 0)]);
        }
        
        // Set is_entry to false for outbound insurance
        $this->merge(['is_entry' => false]);
    }

    /**
     * Parse comma-separated string into array.
     *
     * @param string $key
     * @param bool $isBoolean Whether to convert to boolean array
     * @return array
     */
    private function parseCommaSeparatedArray(string $key, bool $isBoolean = false): array
    {
        $value = $this->input($key);
        
        if (is_array($value)) {
            // Already an array
            if ($isBoolean) {
                return array_map(function ($item) {
                    return filter_var($item, FILTER_VALIDATE_BOOLEAN);
                }, $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);
            }
            
            return $array;
        }
        
        return [];
    }

    /**
     * Get validated data with additional processing.
     *
     * @return array
     */
    public function validatedData(): array
    {
        $validated = $this->validated();
        
        // Ensure all arrays have the same length
        $arrayFields = [
            'birthdays',
            'first_names',
            'last_names',
            'genders',
            'deductible_ids',
            'price_ids',
        ];
        
        $count = count($validated['first_names'] ?? []);
        
        foreach ($arrayFields as $field) {
            if (isset($validated[$field]) && count($validated[$field]) !== $count) {
                // Pad or truncate arrays to match count
                $validated[$field] = array_slice($validated[$field], 0, $count);
                if (count($validated[$field]) < $count) {
                    // Pad with empty values
                    $validated[$field] = array_pad($validated[$field], $count, '');
                }
            }
        }
        
        // Process optional individual data arrays
        $optionalArrays = [
            'individual_premiums',
            'rate_categories',
            'has_smoking_surcharges',
            'is_eligible_persons',
            'ineligibility_reasons',
            'has_pre_existing_conditions',
            'is_smokers',
            'deductible_amounts',
            'questionnaire_completions',
        ];
        
        foreach ($optionalArrays as $field) {
            if (isset($validated[$field]) && is_array($validated[$field])) {
                if (count($validated[$field]) !== $count) {
                    // Pad with default values
                    $default = match($field) {
                        'individual_premiums' => 0.0,
                        'rate_categories' => Person::RATE_CATEGORY_A,
                        'has_smoking_surcharges' => false,
                        'is_eligible_persons' => true,
                        'ineligibility_reasons' => null,
                        'has_pre_existing_conditions' => false,
                        'is_smokers' => false,
                        'deductible_amounts' => 0,
                        'questionnaire_completions' => false,
                        default => null,
                    };
                    
                    $validated[$field] = array_pad($validated[$field], $count, $default);
                }
            }
        }
        
        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('first_names', []));
    }
}