"""
Tests for park filtering functionality including search, status filtering,
date ranges, and numeric validations.
"""

from datetime import date

from django.test import TestCase

from apps.parks.filters import ParkFilter
from apps.parks.models import Company, Park, ParkLocation

# NOTE: These tests need to be updated to work with the new ParkLocation model
# instead of the generic Location model


class ParkFilterTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        """Set up test data for all filter tests"""
        # Create operators
        cls.operator1 = Company.objects.create(name="Thrilling Adventures Inc", slug="thrilling-adventures")
        cls.operator2 = Company.objects.create(name="Family Fun Corp", slug="family-fun")

        # Create parks with various attributes for testing all filters
        cls.park1 = Park.objects.create(
            name="Thrilling Adventures Park",
            description="A thrilling park with lots of roller coasters",
            status="OPERATING",
            operator=cls.operator1,
            opening_date=date(2020, 1, 1),
            size_acres=100,
            ride_count=20,
            coaster_count=5,
            average_rating=4.5,
        )
        ParkLocation.objects.create(
            park=cls.park1,
            street_address="123 Thrill St",
            city="Thrill City",
            state="Thrill State",
            country="USA",
            postal_code="12345",
        )

        cls.park2 = Park.objects.create(
            name="Family Fun Park",
            description="Family-friendly entertainment and attractions",
            status="CLOSED_TEMP",
            operator=cls.operator2,
            opening_date=date(2015, 6, 15),
            size_acres=50,
            ride_count=15,
            coaster_count=2,
            average_rating=4.0,
        )
        ParkLocation.objects.create(
            park=cls.park2,
            street_address="456 Fun St",
            city="Fun City",
            state="Fun State",
            country="Canada",
            postal_code="54321",
        )

        # Park with minimal data for edge case testing
        cls.park3 = Park.objects.create(
            name="Incomplete Park",
            status="UNDER_CONSTRUCTION",
            operator=cls.operator1,
        )

    def test_text_search(self):
        """Test search functionality across different fields"""
        # Test name search
        queryset = ParkFilter(data={"search": "Thrilling"}).qs
        self.assertEqual(queryset.count(), 1)
        self.assertIn(self.park1, queryset)

        # Test description search
        queryset = ParkFilter(data={"search": "family-friendly"}).qs
        self.assertEqual(queryset.count(), 1)
        self.assertIn(self.park2, queryset)

        # Test location search
        queryset = ParkFilter(data={"search": "Thrill City"}).qs
        self.assertEqual(queryset.count(), 1)
        self.assertIn(self.park1, queryset)

        # Test combined field search
        queryset = ParkFilter(data={"search": "Park"}).qs
        self.assertEqual(queryset.count(), 3)

        # Test empty search
        queryset = ParkFilter(data={}).qs
        self.assertEqual(queryset.count(), 3)

    def test_status_filtering(self):
        """Test status filter with various values"""
        # Test each status
        status_tests = {
            "OPERATING": [self.park1],
            "CLOSED_TEMP": [self.park2],
            "UNDER_CONSTRUCTION": [self.park3],
        }

        for status, expected_parks in status_tests.items():
            queryset = ParkFilter(data={"status": status}).qs
            self.assertEqual(queryset.count(), len(expected_parks))
            for park in expected_parks:
                self.assertIn(park, queryset)

        # Test empty status (should return all)
        queryset = ParkFilter(data={}).qs
        self.assertEqual(queryset.count(), 3)

        # Test empty string status (should return all)
        queryset = ParkFilter(data={"status": ""}).qs
        self.assertEqual(queryset.count(), 3)

        # Test invalid status (should return no results)
        queryset = ParkFilter(data={"status": "INVALID"}).qs
        self.assertEqual(queryset.count(), 0)

    def test_date_range_filtering(self):
        """Test date range filter functionality"""
        # Test various date range scenarios
        test_cases = [
            # Start date only
            ({"opening_date_after": "2019-01-01"}, [self.park1]),
            # End date only
            ({"opening_date_before": "2016-01-01"}, [self.park2]),
            # Date range including one park
            (
                {
                    "opening_date_after": "2014-01-01",
                    "opening_date_before": "2016-01-01",
                },
                [self.park2],
            ),
            # Date range including multiple parks
            (
                {
                    "opening_date_after": "2014-01-01",
                    "opening_date_before": "2022-01-01",
                },
                [self.park1, self.park2],
            ),
            # Empty filter (should return all)
            ({}, [self.park1, self.park2, self.park3]),
            # Future date (should return none)
            ({"opening_date_after": "2030-01-01"}, []),
        ]

        for filter_data, expected_parks in test_cases:
            queryset = ParkFilter(data=filter_data).qs
            self.assertEqual(
                set(queryset),
                set(expected_parks),
                f"Failed for filter: {filter_data}",
            )

        # Test invalid date formats
        invalid_dates = [
            {"opening_date_after": "invalid-date"},
            {"opening_date_before": "2023-13-01"},  # Invalid month
            {"opening_date_after": "2023-01-32"},  # Invalid day
            {"opening_date_before": "not-a-date"},
        ]

        for invalid_data in invalid_dates:
            filter_instance = ParkFilter(data=invalid_data)
            self.assertFalse(
                filter_instance.is_valid(),
                f"Filter should be invalid for data: {invalid_data}",
            )

    def test_numeric_filtering(self):
        """Test numeric filters with validation"""
        # Test minimum rides filter
        test_cases = [
            ({"min_rides": "18"}, [self.park1]),  # Only park1 has >= 18 rides
            (
                {"min_rides": "10"},
                [self.park1, self.park2],
            ),  # Both park1 and park2 have >= 10 rides
            (
                {"min_rides": "0"},
                [self.park1, self.park2, self.park3],
            ),  # All parks have >= 0 rides
            # No filter should return all
            ({}, [self.park1, self.park2, self.park3]),
        ]

        for filter_data, expected_parks in test_cases:
            queryset = ParkFilter(data=filter_data).qs
            self.assertEqual(
                set(queryset),
                set(expected_parks),
                f"Failed for filter: {filter_data}",
            )

        # Test coaster count filter
        queryset = ParkFilter(data={"min_coasters": "3"}).qs
        self.assertEqual(queryset.count(), 1)
        self.assertIn(self.park1, queryset)

        # Test size filter
        queryset = ParkFilter(data={"min_size": "75"}).qs
        self.assertEqual(queryset.count(), 1)
        self.assertIn(self.park1, queryset)

        # Test validation
        invalid_values = ["-1", "invalid", "0.5"]
        for value in invalid_values:
            filter_instance = ParkFilter(data={"min_rides": value})
            self.assertFalse(
                filter_instance.is_valid(),
                f"Filter should be invalid for value: {value}",
            )
