"""
Test cases for Parks models following Django styleguide patterns.
Uses proper naming conventions and comprehensive coverage.
"""

from datetime import date, timedelta

from django.db import IntegrityError
from django.test import TestCase
from django.utils import timezone

from apps.parks.models import Company, Park
from tests.factories import (
    CompanyFactory,
    ParkAreaFactory,
    ParkFactory,
    ParkReviewFactory,
    TestScenarios,
    UserFactory,
)


class TestParkModel(TestCase):
    """Test cases for the Park model."""

    def setUp(self):
        """Set up test data."""
        self.company = CompanyFactory(roles=["OPERATOR"])
        self.user = UserFactory()

    def test__park_creation__with_valid_data__succeeds(self):
        """Test that park can be created with valid data."""
        park = ParkFactory(operator=self.company)

        self.assertIsInstance(park.id, int)
        self.assertEqual(park.operator, self.company)
        self.assertEqual(park.status, "OPERATING")
        self.assertIsNotNone(park.created_at)

    def test__park_str_representation__returns_park_name(self):
        """Test that park string representation returns the name."""
        park = ParkFactory(name="Test Park", operator=self.company)

        self.assertEqual(str(park), "Test Park")

    def test__park_slug__is_automatically_generated(self):
        """Test that park slug is generated from name."""
        park = ParkFactory(name="Amazing Theme Park", operator=self.company)

        self.assertEqual(park.slug, "amazing-theme-park")

    def test__park_constraints__closing_date_after_opening__is_enforced(self):
        """Test that closing date must be after opening date."""
        with self.assertRaises(IntegrityError):
            ParkFactory(
                operator=self.company,
                opening_date=date(2020, 6, 1),
                closing_date=date(2020, 5, 1),  # Before opening
            )

    def test__park_constraints__positive_size__is_enforced(self):
        """Test that park size must be positive."""
        with self.assertRaises(IntegrityError):
            ParkFactory(operator=self.company, size_acres=-10)

    def test__park_constraints__rating_range__is_enforced(self):
        """Test that rating must be within valid range."""
        # Test upper bound
        with self.assertRaises(IntegrityError):
            ParkFactory(operator=self.company, average_rating=11)

        # Test lower bound
        with self.assertRaises(IntegrityError):
            ParkFactory(operator=self.company, average_rating=0)

    def test__park_constraints__coaster_count_lte_ride_count__is_enforced(
        self,
    ):
        """Test that coaster count cannot exceed ride count."""
        with self.assertRaises(IntegrityError):
            ParkFactory(
                operator=self.company,
                ride_count=5,
                coaster_count=10,  # More coasters than total rides
            )

    def test__park_relationships__operator_is_required(self):
        """Test that park must have an operator."""
        with self.assertRaises(IntegrityError):
            Park.objects.create(
                name="Test Park",
                slug="test-park",
                # Missing operator
            )

    def test__park_relationships__property_owner_is_optional(self):
        """Test that property owner is optional."""
        park = ParkFactory(operator=self.company, property_owner=None)

        self.assertIsNone(park.property_owner)
        self.assertEqual(park.operator, self.company)


class TestParkModelManagers(TestCase):
    """Test cases for Park model custom managers."""

    def setUp(self):
        """Set up test data."""
        self.company = CompanyFactory(roles=["OPERATOR"])
        self.operating_park = ParkFactory(operator=self.company, status="OPERATING")
        self.closed_park = ParkFactory(operator=self.company, status="CLOSED_TEMP")

    def test__park_manager__operating_filter__returns_only_operating_parks(
        self,
    ):
        """Test that operating() filter returns only operating parks."""
        operating_parks = Park.objects.operating()

        self.assertEqual(operating_parks.count(), 1)
        self.assertEqual(operating_parks.first(), self.operating_park)

    def test__park_manager__closed_filter__returns_only_closed_parks(self):
        """Test that closed() filter returns only closed parks."""
        closed_parks = Park.objects.closed()

        self.assertEqual(closed_parks.count(), 1)
        self.assertEqual(closed_parks.first(), self.closed_park)

    def test__park_manager__optimized_for_list__includes_stats(self):
        """Test that optimized_for_list includes statistical annotations."""
        parks = Park.objects.optimized_for_list()
        park = parks.first()

        # Check that statistical fields are available
        self.assertTrue(hasattr(park, "ride_count_calculated"))
        self.assertTrue(hasattr(park, "coaster_count_calculated"))
        self.assertTrue(hasattr(park, "area_count"))


class TestParkAreaModel(TestCase):
    """Test cases for the ParkArea model."""

    def setUp(self):
        """Set up test data."""
        self.company = CompanyFactory(roles=["OPERATOR"])
        self.park = ParkFactory(operator=self.company)

    def test__park_area_creation__with_valid_data__succeeds(self):
        """Test that park area can be created with valid data."""
        area = ParkAreaFactory(park=self.park)

        self.assertIsInstance(area.id, int)
        self.assertEqual(area.park, self.park)
        self.assertIsNotNone(area.created_at)

    def test__park_area_unique_constraint__park_and_slug__is_enforced(self):
        """Test that park+slug combination must be unique."""
        ParkAreaFactory(park=self.park, slug="test-area")

        with self.assertRaises(IntegrityError):
            ParkAreaFactory(park=self.park, slug="test-area")  # Duplicate


class TestCompanyModel(TestCase):
    """Test cases for the Company model."""

    def test__company_creation__with_valid_data__succeeds(self):
        """Test that company can be created with valid data."""
        company = CompanyFactory()

        self.assertIsInstance(company.id, int)
        self.assertIsInstance(company.roles, list)
        self.assertIsNotNone(company.created_at)

    def test__company_manager__operators_filter__returns_only_operators(self):
        """Test that operators() filter works correctly."""
        operator = CompanyFactory(roles=["OPERATOR"])
        manufacturer = CompanyFactory(roles=["MANUFACTURER"])

        operators = Company.objects.operators()

        self.assertIn(operator, operators)
        self.assertNotIn(manufacturer, operators)


class TestParkReviewModel(TestCase):
    """Test cases for the ParkReview model."""

    def setUp(self):
        """Set up test data."""
        self.company = CompanyFactory(roles=["OPERATOR"])
        self.park = ParkFactory(operator=self.company)
        self.user = UserFactory()

    def test__park_review_creation__with_valid_data__succeeds(self):
        """Test that park review can be created with valid data."""
        review = ParkReviewFactory(park=self.park, user=self.user)

        self.assertIsInstance(review.id, int)
        self.assertEqual(review.park, self.park)
        self.assertEqual(review.user, self.user)
        self.assertTrue(1 <= review.rating <= 10)

    def test__park_review_constraints__rating_range__is_enforced(self):
        """Test that review rating must be within valid range."""
        with self.assertRaises(IntegrityError):
            ParkReviewFactory(park=self.park, user=self.user, rating=11)

    def test__park_review_constraints__visit_date_not_future__is_enforced(
        self,
    ):
        """Test that visit date cannot be in the future."""
        future_date = timezone.now().date() + timedelta(days=1)

        with self.assertRaises(IntegrityError):
            ParkReviewFactory(park=self.park, user=self.user, visit_date=future_date)

    def test__park_review_unique_constraint__park_and_user__is_enforced(self):
        """Test that user can only review each park once."""
        ParkReviewFactory(park=self.park, user=self.user)

        with self.assertRaises(IntegrityError):
            ParkReviewFactory(park=self.park, user=self.user)  # Duplicate


class TestParkModelIntegration(TestCase):
    """Integration tests for Park model with related models."""

    def test__complete_park_scenario__with_all_relationships__works_correctly(
        self,
    ):
        """Test complete park creation with all relationships."""
        scenario = TestScenarios.complete_park_with_rides(num_rides=3)

        park = scenario["park"]
        scenario["rides"]
        areas = scenario["areas"]
        reviews = scenario["reviews"]

        # Verify all relationships are properly created
        self.assertEqual(park.rides.count(), 3)
        self.assertEqual(park.areas.count(), len(areas))
        self.assertEqual(park.reviews.filter(is_published=True).count(), len(reviews))

        # Test that park statistics are calculated correctly
        parks_with_stats = Park.objects.with_complete_stats()
        park_with_stats = parks_with_stats.get(id=park.id)

        self.assertEqual(park_with_stats.ride_count_calculated, 3)
        self.assertIsNotNone(park_with_stats.average_rating_calculated)
