"""
Tests for Ride managers and querysets.

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

import pytest
from django.test import TestCase

from apps.rides.managers import (
    RideModelQuerySet,
    RideQuerySet,
    RideReviewQuerySet,
)
from apps.rides.models import Ride, RideModel, RideReview
from tests.factories import (
    CoasterFactory,
    DesignerCompanyFactory,
    ManufacturerCompanyFactory,
    ParkFactory,
    RideFactory,
    RideModelFactory,
    RideReviewFactory,
    UserFactory,
)


@pytest.mark.django_db
class TestRideQuerySet(TestCase):
    """Tests for RideQuerySet."""

    def test__by_category__single_category__filters_correctly(self):
        """Test by_category filters by single category."""
        coaster = RideFactory(category="RC")
        water_ride = RideFactory(category="WR")

        result = Ride.objects.get_queryset().by_category(category="RC")

        assert coaster in result
        assert water_ride not in result

    def test__by_category__multiple_categories__filters_correctly(self):
        """Test by_category filters by multiple categories."""
        rc = RideFactory(category="RC")
        wc = RideFactory(category="WC")
        tr = RideFactory(category="TR")

        result = Ride.objects.get_queryset().by_category(category=["RC", "WC"])

        assert rc in result
        assert wc in result
        assert tr not in result

    def test__coasters__filters_roller_coasters(self):
        """Test coasters filters for RC and WC categories."""
        steel = RideFactory(category="RC")
        wooden = RideFactory(category="WC")
        thrill = RideFactory(category="TR")

        result = Ride.objects.coasters()

        assert steel in result
        assert wooden in result
        assert thrill not in result

    def test__thrill_rides__filters_thrill_categories(self):
        """Test thrill_rides filters for thrill ride categories."""
        coaster = RideFactory(category="RC")
        flat_ride = RideFactory(category="FR")
        family = RideFactory(category="DR")  # Dark ride

        result = Ride.objects.thrill_rides()

        assert coaster in result
        assert flat_ride in result
        assert family not in result

    def test__family_friendly__filters_by_height_requirement(self):
        """Test family_friendly filters by max height requirement."""
        family_ride = RideFactory(min_height_in=36)
        thrill_ride = RideFactory(min_height_in=54)
        no_restriction = RideFactory(min_height_in=None)

        result = Ride.objects.family_friendly(max_height_requirement=42)

        assert family_ride in result
        assert no_restriction in result
        assert thrill_ride not in result

    def test__by_park__filters_by_park_id(self):
        """Test by_park filters rides by park."""
        park1 = ParkFactory()
        park2 = ParkFactory()
        ride1 = RideFactory(park=park1)
        ride2 = RideFactory(park=park2)

        result = Ride.objects.by_park(park_id=park1.pk)

        assert ride1 in result
        assert ride2 not in result

    def test__by_manufacturer__filters_by_manufacturer_id(self):
        """Test by_manufacturer filters by manufacturer."""
        mfr1 = ManufacturerCompanyFactory()
        mfr2 = ManufacturerCompanyFactory()
        ride1 = RideFactory(manufacturer=mfr1)
        ride2 = RideFactory(manufacturer=mfr2)

        result = Ride.objects.get_queryset().by_manufacturer(manufacturer_id=mfr1.pk)

        assert ride1 in result
        assert ride2 not in result

    def test__by_designer__filters_by_designer_id(self):
        """Test by_designer filters by designer."""
        designer1 = DesignerCompanyFactory()
        designer2 = DesignerCompanyFactory()
        ride1 = RideFactory(designer=designer1)
        ride2 = RideFactory(designer=designer2)

        result = Ride.objects.get_queryset().by_designer(designer_id=designer1.pk)

        assert ride1 in result
        assert ride2 not in result

    def test__with_capacity_info__annotates_capacity_data(self):
        """Test with_capacity_info adds capacity annotations."""
        ride = RideFactory(capacity_per_hour=1500, ride_duration_seconds=180)

        result = Ride.objects.get_queryset().with_capacity_info().get(pk=ride.pk)

        assert result.estimated_daily_capacity == 15000  # 1500 * 10
        assert result.duration_minutes == 3.0  # 180 / 60

    def test__high_capacity__filters_by_minimum_capacity(self):
        """Test high_capacity filters by minimum capacity."""
        high_cap = RideFactory(capacity_per_hour=2000)
        low_cap = RideFactory(capacity_per_hour=500)

        result = Ride.objects.high_capacity(min_capacity=1000)

        assert high_cap in result
        assert low_cap not in result

    def test__optimized_for_list__returns_prefetched_data(self):
        """Test optimized_for_list prefetches related data."""
        RideFactory()
        RideFactory()

        queryset = Ride.objects.optimized_for_list()

        # Should return results with prefetched data
        assert queryset.count() == 2


@pytest.mark.django_db
class TestRideManager(TestCase):
    """Tests for RideManager."""

    def test__get_queryset__returns_ride_queryset(self):
        """Test get_queryset returns RideQuerySet."""
        queryset = Ride.objects.get_queryset()

        assert isinstance(queryset, RideQuerySet)

    def test__operating__filters_operating_rides(self):
        """Test operating filters for operating status."""
        operating = RideFactory(status="OPERATING")
        closed = RideFactory(status="CLOSED_PERM")

        result = Ride.objects.operating()

        assert operating in result
        assert closed not in result

    def test__coasters__delegates_to_queryset(self):
        """Test coasters method delegates to queryset."""
        coaster = CoasterFactory(category="RC")
        thrill = RideFactory(category="TR")

        result = Ride.objects.coasters()

        assert coaster in result
        assert thrill not in result

    def test__with_coaster_stats__prefetches_stats(self):
        """Test with_coaster_stats prefetches coaster_stats."""
        ride = CoasterFactory()

        queryset = Ride.objects.with_coaster_stats()

        assert ride in queryset


@pytest.mark.django_db
class TestRideModelQuerySet(TestCase):
    """Tests for RideModelQuerySet."""

    def test__by_manufacturer__filters_by_manufacturer_id(self):
        """Test by_manufacturer filters ride models by manufacturer."""
        mfr1 = ManufacturerCompanyFactory()
        mfr2 = ManufacturerCompanyFactory()
        model1 = RideModelFactory(manufacturer=mfr1)
        model2 = RideModelFactory(manufacturer=mfr2)

        result = RideModel.objects.by_manufacturer(manufacturer_id=mfr1.pk)

        assert model1 in result
        assert model2 not in result

    def test__with_ride_counts__annotates_ride_counts(self):
        """Test with_ride_counts adds ride count annotation."""
        model = RideModelFactory()
        RideFactory(ride_model=model, status="OPERATING")
        RideFactory(ride_model=model, status="OPERATING")
        RideFactory(ride_model=model, status="CLOSED_PERM")

        result = RideModel.objects.get_queryset().with_ride_counts().get(pk=model.pk)

        assert result.ride_count == 3
        assert result.operating_rides_count == 2

    def test__popular_models__filters_by_minimum_installations(self):
        """Test popular_models filters by minimum ride count."""
        popular = RideModelFactory()
        unpopular = RideModelFactory()

        for _ in range(6):
            RideFactory(ride_model=popular)
        for _ in range(2):
            RideFactory(ride_model=unpopular)

        result = RideModel.objects.popular_models(min_installations=5)

        assert popular in result
        assert unpopular not in result


@pytest.mark.django_db
class TestRideModelManager(TestCase):
    """Tests for RideModelManager."""

    def test__get_queryset__returns_ride_model_queryset(self):
        """Test get_queryset returns RideModelQuerySet."""
        queryset = RideModel.objects.get_queryset()

        assert isinstance(queryset, RideModelQuerySet)

    def test__by_manufacturer__delegates_to_queryset(self):
        """Test by_manufacturer delegates to queryset."""
        mfr = ManufacturerCompanyFactory()
        model = RideModelFactory(manufacturer=mfr)

        result = RideModel.objects.by_manufacturer(manufacturer_id=mfr.pk)

        assert model in result


@pytest.mark.django_db
class TestRideReviewQuerySet(TestCase):
    """Tests for RideReviewQuerySet."""

    def test__for_ride__filters_by_ride_id(self):
        """Test for_ride filters reviews by ride."""
        ride1 = RideFactory()
        ride2 = RideFactory()
        user = UserFactory()
        review1 = RideReviewFactory(ride=ride1, user=user)
        user2 = UserFactory()
        review2 = RideReviewFactory(ride=ride2, user=user2)

        result = RideReview.objects.for_ride(ride_id=ride1.pk)

        assert review1 in result
        assert review2 not in result

    def test__by_user__filters_by_user_id(self):
        """Test by_user filters reviews by user."""
        user1 = UserFactory()
        user2 = UserFactory()
        review1 = RideReviewFactory(user=user1)
        review2 = RideReviewFactory(user=user2)

        result = RideReview.objects.get_queryset().by_user(user_id=user1.pk)

        assert review1 in result
        assert review2 not in result

    def test__by_rating_range__filters_by_rating(self):
        """Test by_rating_range filters by rating range."""
        user1 = UserFactory()
        user2 = UserFactory()
        user3 = UserFactory()
        high = RideReviewFactory(rating=9, user=user1)
        mid = RideReviewFactory(rating=5, user=user2)
        low = RideReviewFactory(rating=2, user=user3)

        result = RideReview.objects.by_rating_range(min_rating=7, max_rating=10)

        assert high in result
        assert mid not in result
        assert low not in result

    def test__optimized_for_display__selects_related(self):
        """Test optimized_for_display selects related data."""
        review = RideReviewFactory()

        queryset = RideReview.objects.get_queryset().optimized_for_display()

        # Should include the review
        assert review in queryset


@pytest.mark.django_db
class TestRideReviewManager(TestCase):
    """Tests for RideReviewManager."""

    def test__get_queryset__returns_ride_review_queryset(self):
        """Test get_queryset returns RideReviewQuerySet."""
        queryset = RideReview.objects.get_queryset()

        assert isinstance(queryset, RideReviewQuerySet)

    def test__for_ride__delegates_to_queryset(self):
        """Test for_ride delegates to queryset."""
        ride = RideFactory()
        user = UserFactory()
        review = RideReviewFactory(ride=ride, user=user)

        result = RideReview.objects.for_ride(ride_id=ride.pk)

        assert review in result


@pytest.mark.django_db
class TestRideQuerySetStatusMethods(TestCase):
    """Tests for status-related RideQuerySet methods."""

    def test__operating__filters_operating_rides(self):
        """Test operating filters for OPERATING status."""
        operating = RideFactory(status="OPERATING")
        sbno = RideFactory(status="SBNO")
        closed = RideFactory(status="CLOSED_PERM")

        result = Ride.objects.operating()

        assert operating in result
        assert sbno not in result
        assert closed not in result

    def test__closed__filters_closed_rides(self):
        """Test closed filters for closed statuses."""
        operating = RideFactory(status="OPERATING")
        closed_temp = RideFactory(status="CLOSED_TEMP")
        closed_perm = RideFactory(status="CLOSED_PERM")

        result = Ride.objects.closed()

        assert operating not in result
        assert closed_temp in result
        assert closed_perm in result


@pytest.mark.django_db
class TestRideQuerySetReviewMethods(TestCase):
    """Tests for review-related RideQuerySet methods."""

    def test__with_review_stats__annotates_review_data(self):
        """Test with_review_stats adds review statistics."""
        ride = RideFactory()
        user1 = UserFactory()
        user2 = UserFactory()
        RideReviewFactory(ride=ride, user=user1, is_published=True, rating=8)
        RideReviewFactory(ride=ride, user=user2, is_published=True, rating=6)

        result = Ride.objects.get_queryset().with_review_stats().get(pk=ride.pk)

        assert result.review_count == 2
        assert result.average_rating == 7.0

    def test__highly_rated__filters_by_minimum_rating(self):
        """Test highly_rated filters by minimum average rating."""
        ride1 = RideFactory()
        ride2 = RideFactory()

        user1 = UserFactory()
        user2 = UserFactory()
        # High rated ride
        RideReviewFactory(ride=ride1, user=user1, is_published=True, rating=9)
        RideReviewFactory(ride=ride1, user=user2, is_published=True, rating=10)

        user3 = UserFactory()
        user4 = UserFactory()
        # Low rated ride
        RideReviewFactory(ride=ride2, user=user3, is_published=True, rating=4)
        RideReviewFactory(ride=ride2, user=user4, is_published=True, rating=5)

        result = Ride.objects.get_queryset().highly_rated(min_rating=8.0)

        assert ride1 in result
        assert ride2 not in result
