<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;

class StoreAddressRequest extends FormRequest
{
    public function authorize(): bool
    {
        return (bool) $this->user();
    }

    public function rules(): array
    {
        return [
            'name' => [
                'required',
                'string',
                'min:2',
                'max:50',
                'regex:/^[A-Za-z ]+$/',
            ],
            'mobile' => ['required', 'string', 'regex:/^\+91[5-9]\d{9}$/'],
            'country_id' => ['required', 'integer', Rule::exists('countries', 'id')->where('status', 1)],
            'state_id' => [
                'required',
                'integer',
                Rule::exists('states', 'id')
                    ->where('status', 1)
                    ->where(fn ($q) => $q->where('country_id', $this->input('country_id'))),
            ],
            'city_id' => [
                'nullable',
                'integer',
                'prohibited_if:city_custom,*',
                Rule::exists('cities', 'id')
                    ->where('status', 1)
                    ->where(fn ($q) => $q->where('state_id', $this->input('state_id'))),
            ],
            'city_custom' => [
                'nullable',
                'string',
                'min:2',
                'max:100',
                'regex:/^[\p{L}\p{M}\s.\-\']+$/u',
                'prohibited_if:city_id,*',
            ],
            'line1' => [
                'required',
                'string',
                'min:5',
                'max:100',
                'regex:/^(?=.*[A-Za-z0-9])[A-Za-z0-9\s,\.\-\/#()]+$/',
                function ($attribute, $value, $fail) {
                    if (!$this->hasAtLeastTwoWords((string) $value)) {
                        $fail('Address Line 1 must contain at least 2 words.');
                    }
                    if ($this->isRepeatedSpam((string) $value)) {
                        $fail('Address Line 1 looks invalid. Please enter a meaningful address.');
                    }
                },
            ],
            'line2' => [
                'nullable',
                'string',
                'max:100',
                'regex:/^[A-Za-z0-9\s,\.\-\/#()]+$/',
                function ($attribute, $value, $fail) {
                    if ($value !== null && $value !== '' && $this->isRepeatedSpam((string) $value)) {
                        $fail('Address Line 2 looks invalid. Please enter a meaningful value.');
                    }
                },
            ],
            'city' => [
                'nullable',
                'string',
                function ($attribute, $value, $fail) {
                    if ($value !== null && mb_strlen((string) $value) > 100) {
                        $fail('The city field must not exceed 100 characters.');
                    }
                },
            ],
            'state' => [
                'nullable',
                'string',
                function ($attribute, $value, $fail) {
                    if ($value !== null && mb_strlen((string) $value) > 100) {
                        $fail('The state field must not exceed 100 characters.');
                    }
                },
            ],
            'pin' => 'required|string|regex:/^[1-9]\d{5}$/',
            'is_default' => 'nullable|boolean',
        ];
    }

    public function messages(): array
    {
        return [
            'name.required' => 'Name is required.',
            'name.min' => 'Name must be between 2 and 50 characters.',
            'name.max' => 'Name must be between 2 and 50 characters.',
            'name.regex' => 'Name can contain only letters and spaces.',
            'mobile.required' => 'Mobile number is required.',
            'mobile.regex' => 'Mobile number must be 10 digits and start with 5 to 9.',
            'line1.required' => 'Address Line 1 is required.',
            'line1.min' => 'Address Line 1 must be between 5 and 100 characters.',
            'line1.max' => 'Address Line 1 must be between 5 and 100 characters.',
            'line1.regex' => 'Address Line 1 must use English letters/numbers, basic punctuation, and include at least one letter or number.',
            'line2.max' => 'Address Line 2 cannot exceed 100 characters.',
            'line2.regex' => 'Address Line 2 can contain only English letters, numbers, and basic punctuation.',
            'country_id.required' => 'Country is required.',
            'state_id.required' => 'State is required.',
            'city_id.prohibited_if' => 'Choose either a city from the list or enter a custom city name, not both.',
            'city_custom.prohibited_if' => 'Choose either a city from the list or enter a custom city name, not both.',
            'city_custom.regex' => 'City name may only contain letters, spaces, hyphens, apostrophes, and periods.',
            'city_custom.min' => 'City name must be at least 2 characters.',
            'pin.required' => 'PIN Code is required.',
            'pin.regex' => 'PIN Code must be 6 digits and cannot start with 0.',
        ];
    }

    public function withValidator(Validator $validator): void
    {
        $validator->after(function (Validator $validator) {
            if (!$this->filled('city_id') && !$this->filled('city_custom')) {
                $validator->errors()->add('city_id', 'Please select a city or enter your city name.');
            }
        });
    }

    protected function prepareForValidation(): void
    {
        if ($this->has('city_custom')) {
            $custom = strip_tags(trim((string) $this->input('city_custom')));
            $custom = preg_replace('/\s+/u', ' ', $custom) ?? '';
            $this->merge([
                'city_custom' => $custom !== '' ? $custom : null,
            ]);
        }

        $this->merge([
            'name' => $this->normalizeSpaces($this->input('name')),
            'line1' => $this->normalizeSpaces($this->input('line1')),
            'line2' => $this->normalizeSpaces($this->input('line2')),
            'city' => $this->normalizeSpaces($this->input('city')),
            'state' => $this->normalizeSpaces($this->input('state')),
            'pin' => preg_replace('/\D+/', '', (string) $this->input('pin')),
        ]);

        if ($this->has('mobile')) {
            $mobile = preg_replace('/\D+/', '', (string) $this->input('mobile'));
            if (!str_starts_with($mobile, '+91')) {
                $mobile = '+91' . ltrim($mobile, '+');
            }
            $this->merge([
                'mobile' => $mobile,
            ]);
        }
    }

    private function normalizeSpaces(mixed $value): ?string
    {
        if ($value === null) {
            return null;
        }

        return trim((string) preg_replace('/\s+/', ' ', (string) $value));
    }

    private function hasAtLeastTwoWords(string $value): bool
    {
        $parts = preg_split('/\s+/', trim($value));
        $parts = array_filter($parts, static fn ($part) => $part !== '');

        return count($parts) >= 2;
    }

    private function isRepeatedSpam(string $value, int $threshold = 6): bool
    {
        $normalized = strtolower(preg_replace('/\s+/', '', trim($value)));

        if (mb_strlen($normalized) < $threshold) {
            return false;
        }

        return (bool) preg_match('/^([a-z0-9])\1+$/', $normalized);
    }
}
