"""
Tests for Park forms.

Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
"""

import pytest
from django.test import TestCase

from apps.parks.forms import (
    ParkAutocomplete,
    ParkForm,
    ParkSearchForm,
)
from tests.factories import (
    OperatorCompanyFactory,
    ParkFactory,
)


@pytest.mark.django_db
class TestParkForm(TestCase):
    """Tests for ParkForm."""

    def test__init__new_park__no_location_prefilled(self):
        """Test initializing form for new park has no location prefilled."""
        form = ParkForm()

        assert form.fields["latitude"].initial is None
        assert form.fields["longitude"].initial is None
        assert form.fields["city"].initial is None

    def test__init__existing_park_with_location__prefills_location_fields(self):
        """Test initializing form for existing park prefills location fields."""
        park = ParkFactory()
        # Create location via factory's post_generation hook

        form = ParkForm(instance=park)

        # Location should be prefilled if it exists
        if park.location.exists():
            location = park.location.first()
            assert form.fields["latitude"].initial == location.latitude
            assert form.fields["longitude"].initial == location.longitude
            assert form.fields["city"].initial == location.city

    def test__clean_latitude__valid_value__returns_normalized_value(self):
        """Test clean_latitude normalizes valid latitude."""
        operator = OperatorCompanyFactory()
        data = {
            "name": "Test Park",
            "operator": operator.pk,
            "status": "OPERATING",
            "latitude": "37.123456789",  # Too many decimal places
            "longitude": "-122.123456",
        }

        form = ParkForm(data=data)
        form.is_valid()

        if "latitude" in form.cleaned_data:
            # Should be rounded to 6 decimal places
            assert len(form.cleaned_data["latitude"].split(".")[-1]) <= 6

    def test__clean_latitude__out_of_range__returns_error(self):
        """Test clean_latitude rejects out-of-range latitude."""
        operator = OperatorCompanyFactory()
        data = {
            "name": "Test Park",
            "operator": operator.pk,
            "status": "OPERATING",
            "latitude": "95.0",  # Invalid: > 90
            "longitude": "-122.0",
        }

        form = ParkForm(data=data)
        is_valid = form.is_valid()

        assert not is_valid
        assert "latitude" in form.errors

    def test__clean_latitude__negative_ninety__is_valid(self):
        """Test clean_latitude accepts -90 (edge case)."""
        operator = OperatorCompanyFactory()
        data = {
            "name": "Test Park",
            "operator": operator.pk,
            "status": "OPERATING",
            "latitude": "-90.0",
            "longitude": "0.0",
        }

        form = ParkForm(data=data)
        is_valid = form.is_valid()

        # Should be valid (form may have other errors but not latitude)
        if not is_valid:
            assert "latitude" not in form.errors

    def test__clean_longitude__valid_value__returns_normalized_value(self):
        """Test clean_longitude normalizes valid longitude."""
        operator = OperatorCompanyFactory()
        data = {
            "name": "Test Park",
            "operator": operator.pk,
            "status": "OPERATING",
            "latitude": "37.0",
            "longitude": "-122.123456789",  # Too many decimal places
        }

        form = ParkForm(data=data)
        form.is_valid()

        if "longitude" in form.cleaned_data:
            # Should be rounded to 6 decimal places
            assert len(form.cleaned_data["longitude"].split(".")[-1]) <= 6

    def test__clean_longitude__out_of_range__returns_error(self):
        """Test clean_longitude rejects out-of-range longitude."""
        operator = OperatorCompanyFactory()
        data = {
            "name": "Test Park",
            "operator": operator.pk,
            "status": "OPERATING",
            "latitude": "37.0",
            "longitude": "-200.0",  # Invalid: < -180
        }

        form = ParkForm(data=data)
        is_valid = form.is_valid()

        assert not is_valid
        assert "longitude" in form.errors

    def test__clean_longitude__positive_180__is_valid(self):
        """Test clean_longitude accepts 180 (edge case)."""
        operator = OperatorCompanyFactory()
        data = {
            "name": "Test Park",
            "operator": operator.pk,
            "status": "OPERATING",
            "latitude": "0.0",
            "longitude": "180.0",
        }

        form = ParkForm(data=data)
        is_valid = form.is_valid()

        # Should be valid (form may have other errors but not longitude)
        if not is_valid:
            assert "longitude" not in form.errors

    def test__save__new_park_with_location__creates_park_and_location(self):
        """Test saving new park creates both park and location."""
        operator = OperatorCompanyFactory()
        data = {
            "name": "New Test Park",
            "operator": operator.pk,
            "status": "OPERATING",
            "latitude": "37.123456",
            "longitude": "-122.123456",
            "city": "San Francisco",
            "state": "CA",
            "country": "USA",
        }

        form = ParkForm(data=data)

        if form.is_valid():
            park = form.save()

            assert park.name == "New Test Park"
            # Location should be created
            assert park.location.exists() or hasattr(park, "location")

    def test__save__existing_park__updates_location(self):
        """Test saving existing park updates location."""
        park = ParkFactory()

        # Update data
        data = {
            "name": park.name,
            "operator": park.operator.pk,
            "status": park.status,
            "latitude": "40.0",
            "longitude": "-74.0",
            "city": "New York",
            "state": "NY",
            "country": "USA",
        }

        form = ParkForm(instance=park, data=data)

        if form.is_valid():
            updated_park = form.save()
            # Location should be updated
            assert updated_park.pk == park.pk

    def test__meta__fields__includes_all_expected_fields(self):
        """Test Meta.fields includes all expected park and location fields."""
        expected_fields = [
            "name",
            "description",
            "operator",
            "property_owner",
            "status",
            "opening_date",
            "closing_date",
            "operating_season",
            "size_acres",
            "website",
            "latitude",
            "longitude",
            "street_address",
            "city",
            "state",
            "country",
            "postal_code",
        ]

        for field in expected_fields:
            assert field in ParkForm.Meta.fields

    def test__widgets__latitude_longitude_hidden__are_hidden_inputs(self):
        """Test latitude and longitude use HiddenInput widgets."""
        form = ParkForm()

        assert form.fields["latitude"].widget.input_type == "hidden"
        assert form.fields["longitude"].widget.input_type == "hidden"

    def test__widgets__text_fields__have_styling_classes(self):
        """Test text fields have appropriate CSS classes."""
        form = ParkForm()

        # Check city field has expected styling
        city_widget = form.fields["city"].widget
        assert "class" in city_widget.attrs
        assert "rounded-lg" in city_widget.attrs["class"]


@pytest.mark.django_db
class TestParkSearchForm(TestCase):
    """Tests for ParkSearchForm."""

    def test__init__creates_park_field(self):
        """Test initializing form creates park field."""
        form = ParkSearchForm()

        assert "park" in form.fields

    def test__park_field__uses_autocomplete_widget(self):
        """Test park field uses AutocompleteWidget."""
        form = ParkSearchForm()

        # Check the widget type
        widget = form.fields["park"].widget
        widget_class_name = widget.__class__.__name__
        assert "Autocomplete" in widget_class_name or "Select" in widget_class_name

    def test__park_field__not_required(self):
        """Test park field is not required."""
        form = ParkSearchForm()

        assert form.fields["park"].required is False

    def test__validate__empty_form__is_valid(self):
        """Test empty form is valid."""
        form = ParkSearchForm(data={})

        assert form.is_valid()

    def test__validate__with_park__is_valid(self):
        """Test form with valid park is valid."""
        park = ParkFactory()
        form = ParkSearchForm(data={"park": park.pk})

        assert form.is_valid()


@pytest.mark.django_db
class TestParkAutocomplete(TestCase):
    """Tests for ParkAutocomplete."""

    def test__model__is_park(self):
        """Test autocomplete model is Park."""
        from apps.parks.models import Park

        assert ParkAutocomplete.model == Park

    def test__search_attrs__includes_name(self):
        """Test search_attrs includes name field."""
        assert "name" in ParkAutocomplete.search_attrs

    def test__search__matching_name__returns_results(self):
        """Test searching by name returns matching parks."""
        park1 = ParkFactory(name="Cedar Point")
        park2 = ParkFactory(name="Kings Island")

        # The autocomplete should return Cedar Point when searching for "Cedar"
        queryset = ParkAutocomplete.model.objects.filter(name__icontains="Cedar")

        assert park1 in queryset
        assert park2 not in queryset

    def test__search__no_match__returns_empty(self):
        """Test searching with no match returns empty queryset."""
        ParkFactory(name="Cedar Point")

        queryset = ParkAutocomplete.model.objects.filter(name__icontains="NoMatchHere")

        assert queryset.count() == 0
