"""
Test cases demonstrating the factory pattern usage.
Following Django styleguide pattern for test data creation.
"""

from django.contrib.auth import get_user_model
from django.test import TestCase

from .factories import (
    CompanyFactory,
    ParkFactory,
    ParkReviewFactory,
    RideFactory,
    RideReviewFactory,
    Traits,
    UserFactory,
)

User = get_user_model()


class FactoryTestCase(TestCase):
    """Test case demonstrating factory usage patterns."""

    def test_user_factory_basic(self):
        """Test basic user factory functionality."""
        # Build without saving to database
        user = UserFactory.build()
        self.assertIsInstance(user, User)
        self.assertTrue(user.username.startswith("testuser"))
        self.assertIn("@example.com", user.email)

        # Create and save to database
        user = UserFactory()
        self.assertTrue(user.pk)
        self.assertTrue(user.check_password("testpass123"))

    def test_user_factory_with_custom_password(self):
        """Test user factory with custom password."""
        user = UserFactory(set_password__password="custompass")
        self.assertTrue(user.check_password("custompass"))

    def test_staff_user_factory(self):
        """Test staff user factory."""
        from .factories import StaffUserFactory

        staff = StaffUserFactory()
        self.assertTrue(staff.is_staff)
        self.assertFalse(staff.is_superuser)

    def test_company_factory_with_roles(self):
        """Test company factory with different roles."""
        # Operator company
        operator = CompanyFactory(roles=["OPERATOR"])
        self.assertEqual(operator.roles, ["OPERATOR"])

        # Manufacturer company
        manufacturer = CompanyFactory(roles=["MANUFACTURER"])
        self.assertEqual(manufacturer.roles, ["MANUFACTURER"])

        # Multi-role company
        multi_role = CompanyFactory(roles=["OPERATOR", "MANUFACTURER"])
        self.assertEqual(set(multi_role.roles), {"OPERATOR", "MANUFACTURER"})

    def test_park_factory_basic(self):
        """Test basic park factory functionality."""
        park = ParkFactory.build()
        self.assertTrue(park.name.startswith("Test Park"))
        self.assertEqual(park.status, "OPERATING")
        self.assertIsNotNone(park.operator)

        # Test that constraints are respected
        self.assertGreaterEqual(park.average_rating, 1)
        self.assertLessEqual(park.average_rating, 10)
        self.assertGreaterEqual(park.ride_count, 0)
        self.assertGreaterEqual(park.coaster_count, 0)

    def test_park_factory_with_traits(self):
        """Test park factory with traits."""
        # Closed park
        closed_park = ParkFactory.build(**Traits.closed_park())
        self.assertEqual(closed_park.status, "CLOSED_PERM")
        self.assertIsNotNone(closed_park.closing_date)

        # High rated park
        high_rated = ParkFactory.build(**Traits.high_rated())
        self.assertGreaterEqual(high_rated.average_rating, 8)

    def test_ride_factory_basic(self):
        """Test basic ride factory functionality."""
        ride = RideFactory.build()
        self.assertTrue(ride.name.startswith("Test Ride"))
        self.assertEqual(ride.status, "OPERATING")
        self.assertIsNotNone(ride.park)
        self.assertIsNotNone(ride.manufacturer)

        # Test constraints
        if ride.min_height_in and ride.max_height_in:
            self.assertLessEqual(ride.min_height_in, ride.max_height_in)
        self.assertGreaterEqual(ride.average_rating, 1)
        self.assertLessEqual(ride.average_rating, 10)

    def test_coaster_factory(self):
        """Test roller coaster specific factory."""
        from .factories import CoasterFactory

        coaster = CoasterFactory.build()
        self.assertIn(coaster.category, ["RC", "WC"])
        self.assertGreaterEqual(coaster.min_height_in, 42)
        self.assertLessEqual(coaster.min_height_in, 54)

    def test_review_factories(self):
        """Test review factory functionality."""
        park_review = ParkReviewFactory.build()
        self.assertGreaterEqual(park_review.rating, 1)
        self.assertLessEqual(park_review.rating, 10)
        self.assertTrue(park_review.is_published)

        ride_review = RideReviewFactory.build()
        self.assertGreaterEqual(ride_review.rating, 1)
        self.assertLessEqual(ride_review.rating, 10)

    def test_sequence_functionality(self):
        """Test that sequences work correctly."""
        users = [UserFactory.build() for _ in range(3)]
        usernames = [user.username for user in users]

        # Should have unique usernames
        self.assertEqual(len(set(usernames)), 3)
        self.assertTrue(all("testuser" in username for username in usernames))

    def test_lazy_attributes(self):
        """Test lazy attribute functionality."""
        park = ParkFactory.build(name="Custom Park Name")
        self.assertEqual(park.slug, "custom-park-name")

    def test_fuzzy_fields(self):
        """Test fuzzy field generation."""
        parks = [ParkFactory.build() for _ in range(10)]

        # Should have varied values
        ratings = [p.average_rating for p in parks if p.average_rating]
        # Should have different ratings
        self.assertGreater(len(set(ratings)), 1)

        ride_counts = [p.ride_count for p in parks if p.ride_count]
        # Should have different counts
        self.assertGreater(len(set(ride_counts)), 1)


class TestScenariosTestCase(TestCase):
    """Test case for pre-configured test scenarios."""

    def test_build_only_scenario(self):
        """Test scenarios using build() to avoid database operations."""
        # Create minimal scenario data using build()
        park = ParkFactory.build()
        rides = [RideFactory.build(park=park) for _ in range(3)]

        # Verify the scenario
        self.assertEqual(len(rides), 3)
        for ride in rides:
            self.assertEqual(ride.park, park)

    def test_review_scenario_build(self):
        """Test review scenario using build()."""
        park = ParkFactory.build()
        users = [UserFactory.build() for _ in range(3)]
        reviews = [ParkReviewFactory.build(park=park, user=user) for user in users]

        # Verify scenario
        self.assertEqual(len(reviews), 3)
        for review in reviews:
            self.assertEqual(review.park, park)
            self.assertIn(review.user, users)


class FactoryValidationTestCase(TestCase):
    """Test that factories respect model validation."""

    def test_rating_constraints(self):
        """Test that rating constraints are respected."""
        # Valid ratings
        valid_review = ParkReviewFactory.build(rating=5)
        self.assertEqual(valid_review.rating, 5)

        # Edge cases
        min_review = ParkReviewFactory.build(rating=1)
        self.assertEqual(min_review.rating, 1)

        max_review = ParkReviewFactory.build(rating=10)
        self.assertEqual(max_review.rating, 10)

    def test_date_constraints(self):
        """Test that date constraints are logical."""
        from datetime import date

        # Valid dates
        park = ParkFactory.build(opening_date=date(2020, 1, 1), closing_date=date(2023, 12, 31))

        # Verify opening is before closing
        if park.opening_date and park.closing_date:
            self.assertLessEqual(park.opening_date, park.closing_date)

    def test_height_requirements(self):
        """Test that height requirements are logical."""
        ride = RideFactory.build(min_height_in=48, max_height_in=72)

        if ride.min_height_in and ride.max_height_in:
            self.assertLessEqual(ride.min_height_in, ride.max_height_in)


class FactoryPerformanceTestCase(TestCase):
    """Test factory performance and bulk operations."""

    def test_bulk_creation_build(self):
        """Test bulk creation using build() for performance."""
        import time

        start_time = time.time()
        users = [UserFactory.build() for _ in range(100)]
        build_time = time.time() - start_time

        self.assertEqual(len(users), 100)
        self.assertLess(build_time, 1.0)  # Should be fast with build()

        # Verify uniqueness
        usernames = [user.username for user in users]
        self.assertEqual(len(set(usernames)), 100)

    def test_related_object_creation(self):
        """Test creation of objects with relationships."""
        # Build park with relationships
        park = ParkFactory.build()

        # Verify relationships exist
        self.assertIsNotNone(park.operator)
        self.assertIsNotNone(park.property_owner)

        # Build ride with park relationship
        ride = RideFactory.build(park=park)
        self.assertEqual(ride.park, park)
