"""
Ride Search and Filter Forms

Forms for the comprehensive ride filtering system with 8 categories:
1. Search text
2. Basic info
3. Dates
4. Height/safety
5. Performance
6. Relationships
7. Roller coaster
8. Company

Each form handles validation and provides clean data for the RideSearchService.
"""

from typing import Any

from django import forms
from django.core.exceptions import ValidationError

from apps.parks.models import Park, ParkArea
from apps.rides.models.company import Company
from apps.rides.models.rides import RideModel


class BaseFilterForm(forms.Form):
    """Base form with common functionality for all filter forms."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Add common CSS classes
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-input"})


class SearchTextForm(BaseFilterForm):
    """Form for text-based search filters."""

    global_search = forms.CharField(
        required=False,
        max_length=255,
        widget=forms.TextInput(
            attrs={
                "placeholder": "Search rides, parks, manufacturers...",
                "class": (
                    "w-full px-4 py-2 border border-gray-300 "
                    "dark:border-gray-600 rounded-lg focus:ring-2 "
                    "focus:ring-blue-500 focus:border-transparent "
                    "bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
                ),
                "autocomplete": "off",
                "hx-get": "",
                "hx-trigger": "input changed delay:300ms",
                "hx-target": "#search-results",
                "hx-indicator": "#search-loading",
            }
        ),
    )

    name_search = forms.CharField(
        required=False,
        max_length=255,
        widget=forms.TextInput(
            attrs={
                "placeholder": "Search by ride name...",
                "class": (
                    "w-full px-3 py-2 border border-gray-300 "
                    "dark:border-gray-600 rounded-md focus:ring-2 "
                    "focus:ring-blue-500 focus:border-transparent "
                    "bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
                ),
            }
        ),
    )

    description_search = forms.CharField(
        required=False,
        max_length=255,
        widget=forms.TextInput(
            attrs={
                "placeholder": "Search in descriptions...",
                "class": (
                    "w-full px-3 py-2 border border-gray-300 "
                    "dark:border-gray-600 rounded-md focus:ring-2 "
                    "focus:ring-blue-500 focus:border-transparent "
                    "bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
                ),
            }
        ),
    )


class BasicInfoForm(BaseFilterForm):
    """Form for basic ride information filters."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Get choices from Rich Choice registry
        from apps.core.choices.registry import get_choices

        # Get choices - let exceptions propagate if registry fails
        category_choices = [(choice.value, choice.label) for choice in get_choices("categories", "rides")]
        status_choices = [(choice.value, choice.label) for choice in get_choices("statuses", "rides")]

        # Update field choices dynamically
        self.fields["category"].choices = category_choices
        self.fields["status"].choices = status_choices

    category = forms.MultipleChoiceField(
        choices=[],  # Will be populated in __init__
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "grid grid-cols-2 gap-2"}),
    )

    status = forms.MultipleChoiceField(
        choices=[],  # Will be populated in __init__
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "space-y-2"}),
    )

    park = forms.ModelMultipleChoiceField(
        queryset=Park.objects.all(),
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "max-h-48 overflow-y-auto space-y-1"}),
    )

    park_area = forms.ModelMultipleChoiceField(
        queryset=ParkArea.objects.all(),
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "max-h-48 overflow-y-auto space-y-1"}),
    )


class DateRangeWidget(forms.MultiWidget):
    """Custom widget for date range inputs."""

    def __init__(self, attrs=None):
        widgets = [
            forms.DateInput(
                attrs={
                    "type": "date",
                    "class": (
                        "px-3 py-2 border border-gray-300 "
                        "dark:border-gray-600 rounded-md focus:ring-2 "
                        "focus:ring-blue-500 focus:border-transparent "
                        "bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
                    ),
                }
            ),
            forms.DateInput(
                attrs={
                    "type": "date",
                    "class": (
                        "px-3 py-2 border border-gray-300 "
                        "dark:border-gray-600 rounded-md focus:ring-2 "
                        "focus:ring-blue-500 focus:border-transparent "
                        "bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
                    ),
                }
            ),
        ]
        super().__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return [value.get("start"), value.get("end")]
        return [None, None]


class DateRangeField(forms.MultiValueField):
    """Custom field for date ranges."""

    def __init__(self, *args, **kwargs):
        fields = [forms.DateField(required=False), forms.DateField(required=False)]
        super().__init__(fields, *args, **kwargs)
        self.widget = DateRangeWidget()

    def compress(self, data_list):
        if data_list:
            start_date, end_date = data_list
            if start_date or end_date:
                return {"start": start_date, "end": end_date}
        return None

    def validate(self, value):
        super().validate(value)
        if value and value.get("start") and value.get("end") and value["start"] > value["end"]:
            raise ValidationError("Start date must be before end date.")


class DateFiltersForm(BaseFilterForm):
    """Form for date range filters."""

    opening_date_range = DateRangeField(required=False, label="Opening Date Range")

    closing_date_range = DateRangeField(required=False, label="Closing Date Range")

    status_since_range = DateRangeField(required=False, label="Status Since Range")


class NumberRangeWidget(forms.MultiWidget):
    """Custom widget for number range inputs."""

    def __init__(self, attrs=None, min_val=None, max_val=None, step=None):
        widgets = [
            forms.NumberInput(
                attrs={
                    "class": (
                        "px-3 py-2 border border-gray-300 dark:border-gray-600 "
                        "rounded-md focus:ring-2 focus:ring-blue-500 "
                        "focus:border-transparent bg-white dark:bg-gray-800 "
                        "text-gray-900 dark:text-white"
                    ),
                    "placeholder": "Min",
                    "min": min_val,
                    "max": max_val,
                    "step": step,
                }
            ),
            forms.NumberInput(
                attrs={
                    "class": (
                        "px-3 py-2 border border-gray-300 dark:border-gray-600 "
                        "rounded-md focus:ring-2 focus:ring-blue-500 "
                        "focus:border-transparent bg-white dark:bg-gray-800 "
                        "text-gray-900 dark:text-white"
                    ),
                    "placeholder": "Max",
                    "min": min_val,
                    "max": max_val,
                    "step": step,
                }
            ),
        ]
        super().__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return [value.get("min"), value.get("max")]
        return [None, None]


class NumberRangeField(forms.MultiValueField):
    """Custom field for number ranges."""

    def __init__(self, min_val=None, max_val=None, step=None, *args, **kwargs):
        fields = [
            forms.FloatField(required=False, min_value=min_val, max_value=max_val),
            forms.FloatField(required=False, min_value=min_val, max_value=max_val),
        ]
        super().__init__(fields, *args, **kwargs)
        self.widget = NumberRangeWidget(min_val=min_val, max_val=max_val, step=step)

    def compress(self, data_list):
        if data_list:
            min_val, max_val = data_list
            if min_val is not None or max_val is not None:
                return {"min": min_val, "max": max_val}
        return None

    def validate(self, value):
        super().validate(value)
        if value and value.get("min") is not None and value.get("max") is not None:  # noqa: SIM102
            if value["min"] > value["max"]:
                raise ValidationError("Minimum value must be less than maximum value.")


class HeightSafetyForm(BaseFilterForm):
    """Form for height and safety requirement filters."""

    min_height_range = NumberRangeField(
        min_val=0,
        max_val=84,  # 7 feet in inches
        step=1,
        required=False,
        label="Minimum Height (inches)",
    )

    max_height_range = NumberRangeField(min_val=0, max_val=84, step=1, required=False, label="Maximum Height (inches)")


class PerformanceForm(BaseFilterForm):
    """Form for performance metric filters."""

    capacity_range = NumberRangeField(min_val=0, max_val=5000, step=50, required=False, label="Capacity per Hour")

    duration_range = NumberRangeField(
        min_val=0,
        max_val=1800,  # 30 minutes in seconds
        step=10,
        required=False,
        label="Duration (seconds)",
    )

    rating_range = NumberRangeField(min_val=0.0, max_val=10.0, step=0.1, required=False, label="Average Rating")


class RelationshipsForm(BaseFilterForm):
    """Form for relationship filters (manufacturer, designer, ride model)."""

    manufacturer = forms.ModelMultipleChoiceField(
        queryset=Company.objects.filter(roles__contains=["MANUFACTURER"]),
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "max-h-48 overflow-y-auto space-y-1"}),
    )

    designer = forms.ModelMultipleChoiceField(
        queryset=Company.objects.filter(roles__contains=["DESIGNER"]),
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "max-h-48 overflow-y-auto space-y-1"}),
    )

    ride_model = forms.ModelMultipleChoiceField(
        queryset=RideModel.objects.all(),
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "max-h-48 overflow-y-auto space-y-1"}),
    )


class RollerCoasterForm(BaseFilterForm):
    """Form for roller coaster specific filters."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Get choices from Rich Choice registry
        from apps.core.choices.registry import get_choices

        # Get choices - let exceptions propagate if registry fails
        track_material_choices = [(choice.value, choice.label) for choice in get_choices("track_materials", "rides")]
        coaster_type_choices = [(choice.value, choice.label) for choice in get_choices("coaster_types", "rides")]
        propulsion_system_choices = [
            (choice.value, choice.label) for choice in get_choices("propulsion_systems", "rides")
        ]

        # Update field choices dynamically
        self.fields["track_material"].choices = track_material_choices
        self.fields["coaster_type"].choices = coaster_type_choices
        self.fields["propulsion_system"].choices = propulsion_system_choices

    height_ft_range = NumberRangeField(min_val=0, max_val=500, step=1, required=False, label="Height (feet)")

    length_ft_range = NumberRangeField(min_val=0, max_val=10000, step=10, required=False, label="Length (feet)")

    speed_mph_range = NumberRangeField(min_val=0, max_val=150, step=1, required=False, label="Speed (mph)")

    inversions_range = NumberRangeField(min_val=0, max_val=20, step=1, required=False, label="Number of Inversions")

    track_material = forms.MultipleChoiceField(
        choices=[],  # Will be populated in __init__
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "space-y-2"}),
    )

    coaster_type = forms.MultipleChoiceField(
        choices=[],  # Will be populated in __init__
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "grid grid-cols-2 gap-2 max-h-48 overflow-y-auto"}),
    )

    propulsion_system = forms.MultipleChoiceField(
        choices=[],  # Will be populated in __init__
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "space-y-2 max-h-48 overflow-y-auto"}),
    )


class CompanyForm(BaseFilterForm):
    """Form for company-related filters."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Get choices from Rich Choice registry
        from apps.core.choices.registry import get_choices

        # Get both rides and parks company roles - let exceptions propagate if registry fails
        rides_roles = [(choice.value, choice.label) for choice in get_choices("company_roles", "rides")]
        parks_roles = [(choice.value, choice.label) for choice in get_choices("company_roles", "parks")]
        role_choices = rides_roles + parks_roles

        # Update field choices dynamically
        self.fields["manufacturer_roles"].choices = role_choices
        self.fields["designer_roles"].choices = role_choices

    manufacturer_roles = forms.MultipleChoiceField(
        choices=[],  # Will be populated in __init__
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "space-y-2"}),
    )

    designer_roles = forms.MultipleChoiceField(
        choices=[],  # Will be populated in __init__
        required=False,
        widget=forms.CheckboxSelectMultiple(attrs={"class": "space-y-2"}),
    )

    founded_date_range = DateRangeField(required=False, label="Company Founded Date Range")


class SortingForm(BaseFilterForm):
    """Form for sorting options."""

    # Static sorting choices - these are UI-specific and don't need Rich Choice Objects
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Static sort choices for UI functionality
        sort_choices = [
            ("relevance", "Relevance"),
            ("name_asc", "Name (A-Z)"),
            ("name_desc", "Name (Z-A)"),
            ("opening_date_asc", "Opening Date (Oldest)"),
            ("opening_date_desc", "Opening Date (Newest)"),
            ("rating_asc", "Rating (Lowest)"),
            ("rating_desc", "Rating (Highest)"),
            ("height_asc", "Height (Shortest)"),
            ("height_desc", "Height (Tallest)"),
            ("speed_asc", "Speed (Slowest)"),
            ("speed_desc", "Speed (Fastest)"),
            ("capacity_asc", "Capacity (Lowest)"),
            ("capacity_desc", "Capacity (Highest)"),
        ]

        self.fields["sort_by"].choices = sort_choices

    sort_by = forms.ChoiceField(
        choices=[],  # Will be populated in __init__
        required=False,
        initial="relevance",
        widget=forms.Select(
            attrs={
                "class": (
                    "px-3 py-2 border border-gray-300 dark:border-gray-600 "
                    "rounded-md focus:ring-2 focus:ring-blue-500 "
                    "focus:border-transparent bg-white dark:bg-gray-800 "
                    "text-gray-900 dark:text-white"
                ),
                "hx-get": "",
                "hx-trigger": "change",
                "hx-target": "#search-results",
                "hx-indicator": "#search-loading",
            }
        ),
    )


class MasterFilterForm(BaseFilterForm):
    """Master form that combines all filter categories."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Initialize all sub-forms
        self.search_text_form = SearchTextForm(*args, **kwargs)
        self.basic_info_form = BasicInfoForm(*args, **kwargs)
        self.date_filters_form = DateFiltersForm(*args, **kwargs)
        self.height_safety_form = HeightSafetyForm(*args, **kwargs)
        self.performance_form = PerformanceForm(*args, **kwargs)
        self.relationships_form = RelationshipsForm(*args, **kwargs)
        self.roller_coaster_form = RollerCoasterForm(*args, **kwargs)
        self.company_form = CompanyForm(*args, **kwargs)
        self.sorting_form = SortingForm(*args, **kwargs)

        # Add all fields to this form
        self.fields.update(self.search_text_form.fields)
        self.fields.update(self.basic_info_form.fields)
        self.fields.update(self.date_filters_form.fields)
        self.fields.update(self.height_safety_form.fields)
        self.fields.update(self.performance_form.fields)
        self.fields.update(self.relationships_form.fields)
        self.fields.update(self.roller_coaster_form.fields)
        self.fields.update(self.company_form.fields)
        self.fields.update(self.sorting_form.fields)

    def clean(self):
        cleaned_data = super().clean()

        # Validate all sub-forms
        for form in [
            self.search_text_form,
            self.basic_info_form,
            self.date_filters_form,
            self.height_safety_form,
            self.performance_form,
            self.relationships_form,
            self.roller_coaster_form,
            self.company_form,
            self.sorting_form,
        ]:
            if not form.is_valid():
                for field, errors in form.errors.items():
                    self.add_error(field, errors)

        return cleaned_data

    def get_filter_dict(self) -> dict[str, Any]:
        """Convert form data to search service filter format."""
        if not self.is_valid():
            return {}

        filters = {}

        # Add all cleaned data to filters
        for field_name, value in self.cleaned_data.items():
            if value:  # Only include non-empty values
                filters[field_name] = value

        return filters

    def get_search_filters(self) -> dict[str, Any]:
        """Alias for get_filter_dict for backward compatibility."""
        return self.get_filter_dict()

    def get_filter_summary(self) -> dict[str, Any]:
        """Get summary of active filters for display."""
        active_filters = {}

        if not self.is_valid():
            return active_filters

        # Group filters by category
        categories = {
            "Search": ["global_search", "name_search", "description_search"],
            "Basic Info": ["category", "status", "park", "park_area"],
            "Dates": ["opening_date_range", "closing_date_range", "status_since_range"],
            "Height & Safety": ["min_height_range", "max_height_range"],
            "Performance": ["capacity_range", "duration_range", "rating_range"],
            "Relationships": ["manufacturer", "designer", "ride_model"],
            "Roller Coaster": [
                "height_ft_range",
                "length_ft_range",
                "speed_mph_range",
                "inversions_range",
                "track_material",
                "coaster_type",
                "propulsion_system",
            ],
            "Company": ["manufacturer_roles", "designer_roles", "founded_date_range"],
        }

        for category, field_names in categories.items():
            category_filters = []
            for field_name in field_names:
                value = self.cleaned_data.get(field_name)
                if value:
                    category_filters.append(
                        {
                            "field": field_name,
                            "value": value,
                            "label": self.fields[field_name].label or field_name.replace("_", " ").title(),
                        }
                    )

            if category_filters:
                active_filters[category] = category_filters

        return active_filters

    def get_active_filters_summary(self) -> dict[str, Any]:
        """Alias for get_filter_summary for backward compatibility."""
        return self.get_filter_summary()

    def has_active_filters(self) -> bool:
        """Check if any filters are currently active."""
        if not self.is_valid():
            return False

        return any(value for field_name, value in self.cleaned_data.items())
