"""
Tests for Ride serializers.

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

from unittest.mock import Mock

import pytest
from django.test import TestCase

from apps.api.v1.rides.serializers import (
    HybridRideSerializer,
    RidePhotoApprovalInputSerializer,
    RidePhotoCreateInputSerializer,
    RidePhotoListOutputSerializer,
    RidePhotoOutputSerializer,
    RidePhotoSerializer,
    RidePhotoStatsOutputSerializer,
    RidePhotoTypeFilterSerializer,
    RidePhotoUpdateInputSerializer,
    RideSerializer,
)
from tests.factories import (
    CloudflareImageFactory,
    DesignerCompanyFactory,
    ManufacturerCompanyFactory,
    RideFactory,
    RidePhotoFactory,
    UserFactory,
)


@pytest.mark.django_db
class TestRidePhotoOutputSerializer(TestCase):
    """Tests for RidePhotoOutputSerializer."""

    def test__serialize__valid_photo__returns_all_fields(self):
        """Test serializing a ride photo returns all expected fields."""
        user = UserFactory()
        ride = RideFactory()
        image = CloudflareImageFactory()
        photo = RidePhotoFactory(
            ride=ride,
            uploaded_by=user,
            image=image,
            caption="Test caption",
            alt_text="Test alt text",
            is_primary=True,
            is_approved=True,
        )

        serializer = RidePhotoOutputSerializer(photo)
        data = serializer.data

        assert "id" in data
        assert data["caption"] == "Test caption"
        assert data["alt_text"] == "Test alt text"
        assert data["is_primary"] is True
        assert data["is_approved"] is True
        assert data["uploaded_by_username"] == user.username
        assert data["ride_slug"] == ride.slug
        assert data["ride_name"] == ride.name
        assert data["park_slug"] == ride.park.slug
        assert data["park_name"] == ride.park.name

    def test__serialize__photo_with_image__returns_image_url(self):
        """Test serializing a photo with image returns URL."""
        photo = RidePhotoFactory()

        serializer = RidePhotoOutputSerializer(photo)
        data = serializer.data

        assert "image_url" in data
        assert "image_variants" in data

    def test__serialize__photo_without_image__returns_none_for_image_fields(self):
        """Test serializing photo without image returns None for image fields."""
        photo = RidePhotoFactory()
        photo.image = None
        photo.save()

        serializer = RidePhotoOutputSerializer(photo)
        data = serializer.data

        assert data["image_url"] is None
        assert data["image_variants"] == {}

    def test__get_image_variants__photo_with_image__returns_variant_urls(self):
        """Test get_image_variants returns all variant URLs."""
        image = CloudflareImageFactory()
        photo = RidePhotoFactory(image=image)

        serializer = RidePhotoOutputSerializer(photo)
        data = serializer.data

        if photo.image:
            variants = data["image_variants"]
            assert "thumbnail" in variants
            assert "medium" in variants
            assert "large" in variants
            assert "public" in variants

    def test__serialize__includes_photo_type(self):
        """Test serializing includes photo_type field."""
        photo = RidePhotoFactory()

        serializer = RidePhotoOutputSerializer(photo)
        data = serializer.data

        assert "photo_type" in data


@pytest.mark.django_db
class TestRidePhotoCreateInputSerializer(TestCase):
    """Tests for RidePhotoCreateInputSerializer."""

    def test__serialize__valid_data__returns_expected_fields(self):
        """Test serializing valid create data."""
        image = CloudflareImageFactory()
        data = {
            "image": image.pk,
            "caption": "New photo caption",
            "alt_text": "Description of the image",
            "photo_type": "exterior",
            "is_primary": False,
        }

        serializer = RidePhotoCreateInputSerializer(data=data)

        assert serializer.is_valid(), serializer.errors
        assert "caption" in serializer.validated_data
        assert "alt_text" in serializer.validated_data
        assert "photo_type" in serializer.validated_data
        assert "is_primary" in serializer.validated_data

    def test__validate__missing_required_fields__returns_error(self):
        """Test validation fails with missing required fields."""
        data = {}

        serializer = RidePhotoCreateInputSerializer(data=data)

        assert not serializer.is_valid()

    def test__meta__fields__includes_photo_type(self):
        """Test Meta.fields includes photo_type for ride photos."""
        expected_fields = ["image", "caption", "alt_text", "photo_type", "is_primary"]

        assert list(RidePhotoCreateInputSerializer.Meta.fields) == expected_fields


@pytest.mark.django_db
class TestRidePhotoUpdateInputSerializer(TestCase):
    """Tests for RidePhotoUpdateInputSerializer."""

    def test__serialize__valid_data__returns_expected_fields(self):
        """Test serializing valid update data."""
        data = {
            "caption": "Updated caption",
            "alt_text": "Updated alt text",
            "photo_type": "queue",
            "is_primary": True,
        }

        serializer = RidePhotoUpdateInputSerializer(data=data)

        assert serializer.is_valid(), serializer.errors
        assert serializer.validated_data["caption"] == "Updated caption"
        assert serializer.validated_data["photo_type"] == "queue"

    def test__serialize__partial_update__validates_partial_data(self):
        """Test partial update with only some fields."""
        photo = RidePhotoFactory()
        data = {"caption": "Only caption updated"}

        serializer = RidePhotoUpdateInputSerializer(photo, data=data, partial=True)

        assert serializer.is_valid(), serializer.errors

    def test__meta__fields__includes_photo_type(self):
        """Test Meta.fields includes photo_type for updates."""
        expected_fields = ["caption", "alt_text", "photo_type", "is_primary"]

        assert list(RidePhotoUpdateInputSerializer.Meta.fields) == expected_fields


@pytest.mark.django_db
class TestRidePhotoListOutputSerializer(TestCase):
    """Tests for RidePhotoListOutputSerializer."""

    def test__serialize__photo__returns_list_fields_only(self):
        """Test serializing returns only list-appropriate fields."""
        user = UserFactory()
        photo = RidePhotoFactory(uploaded_by=user)

        serializer = RidePhotoListOutputSerializer(photo)
        data = serializer.data

        assert "id" in data
        assert "image" in data
        assert "caption" in data
        assert "photo_type" in data
        assert "is_primary" in data
        assert "is_approved" in data
        assert "created_at" in data
        assert "uploaded_by_username" in data
        # Should NOT include detailed fields
        assert "image_variants" not in data
        assert "file_size" not in data
        assert "dimensions" not in data

    def test__serialize__multiple_photos__returns_list(self):
        """Test serializing multiple photos returns a list."""
        photos = [RidePhotoFactory() for _ in range(3)]

        serializer = RidePhotoListOutputSerializer(photos, many=True)

        assert len(serializer.data) == 3

    def test__meta__all_fields_read_only(self):
        """Test all fields are read-only for list serializer."""
        assert RidePhotoListOutputSerializer.Meta.read_only_fields == RidePhotoListOutputSerializer.Meta.fields


class TestRidePhotoApprovalInputSerializer(TestCase):
    """Tests for RidePhotoApprovalInputSerializer."""

    def test__validate__valid_photo_ids__returns_validated_data(self):
        """Test validation with valid photo IDs."""
        data = {
            "photo_ids": [1, 2, 3],
            "approve": True,
        }

        serializer = RidePhotoApprovalInputSerializer(data=data)

        assert serializer.is_valid(), serializer.errors
        assert serializer.validated_data["photo_ids"] == [1, 2, 3]
        assert serializer.validated_data["approve"] is True

    def test__validate__approve_default__defaults_to_true(self):
        """Test approve field defaults to True."""
        data = {"photo_ids": [1, 2]}

        serializer = RidePhotoApprovalInputSerializer(data=data)

        assert serializer.is_valid(), serializer.errors
        assert serializer.validated_data["approve"] is True

    def test__validate__empty_photo_ids__is_valid(self):
        """Test empty photo_ids list is valid."""
        data = {"photo_ids": [], "approve": False}

        serializer = RidePhotoApprovalInputSerializer(data=data)

        assert serializer.is_valid(), serializer.errors

    def test__validate__missing_photo_ids__returns_error(self):
        """Test validation fails without photo_ids."""
        data = {"approve": True}

        serializer = RidePhotoApprovalInputSerializer(data=data)

        assert not serializer.is_valid()
        assert "photo_ids" in serializer.errors


class TestRidePhotoStatsOutputSerializer(TestCase):
    """Tests for RidePhotoStatsOutputSerializer."""

    def test__serialize__stats_dict__returns_all_fields(self):
        """Test serializing stats dictionary."""
        stats = {
            "total_photos": 50,
            "approved_photos": 40,
            "pending_photos": 10,
            "has_primary": True,
            "recent_uploads": 3,
            "by_type": {"exterior": 20, "queue": 10, "onride": 10, "other": 10},
        }

        serializer = RidePhotoStatsOutputSerializer(data=stats)

        assert serializer.is_valid(), serializer.errors
        assert serializer.validated_data["total_photos"] == 50
        assert serializer.validated_data["by_type"]["exterior"] == 20

    def test__validate__includes_by_type_field(self):
        """Test stats include by_type breakdown."""
        stats = {
            "total_photos": 10,
            "approved_photos": 8,
            "pending_photos": 2,
            "has_primary": False,
            "recent_uploads": 1,
            "by_type": {"exterior": 10},
        }

        serializer = RidePhotoStatsOutputSerializer(data=stats)

        assert serializer.is_valid(), serializer.errors
        assert "by_type" in serializer.validated_data


class TestRidePhotoTypeFilterSerializer(TestCase):
    """Tests for RidePhotoTypeFilterSerializer."""

    def test__validate__valid_photo_type__returns_validated_data(self):
        """Test validation with valid photo type."""
        data = {"photo_type": "exterior"}

        serializer = RidePhotoTypeFilterSerializer(data=data)

        assert serializer.is_valid(), serializer.errors
        assert serializer.validated_data["photo_type"] == "exterior"

    def test__validate__all_photo_types__are_valid(self):
        """Test all defined photo types are valid."""
        valid_types = ["exterior", "queue", "station", "onride", "construction", "other"]

        for photo_type in valid_types:
            serializer = RidePhotoTypeFilterSerializer(data={"photo_type": photo_type})
            assert serializer.is_valid(), f"Photo type {photo_type} should be valid"

    def test__validate__invalid_photo_type__returns_error(self):
        """Test invalid photo type returns error."""
        data = {"photo_type": "invalid_type"}

        serializer = RidePhotoTypeFilterSerializer(data=data)

        assert not serializer.is_valid()
        assert "photo_type" in serializer.errors

    def test__validate__empty_photo_type__is_valid(self):
        """Test empty/missing photo_type is valid (optional field)."""
        data = {}

        serializer = RidePhotoTypeFilterSerializer(data=data)

        assert serializer.is_valid(), serializer.errors


@pytest.mark.django_db
class TestHybridRideSerializer(TestCase):
    """Tests for HybridRideSerializer."""

    def test__serialize__ride_with_all_fields__returns_complete_data(self):
        """Test serializing ride with all fields populated."""
        ride = RideFactory()

        serializer = HybridRideSerializer(ride)
        data = serializer.data

        assert "id" in data
        assert "name" in data
        assert "slug" in data
        assert "category" in data
        assert "status" in data
        assert "park_name" in data
        assert "park_slug" in data
        assert "manufacturer_name" in data

    def test__serialize__ride_with_manufacturer__returns_manufacturer_fields(self):
        """Test serializing includes manufacturer information."""
        manufacturer = ManufacturerCompanyFactory(name="Test Manufacturer")
        ride = RideFactory(manufacturer=manufacturer)

        serializer = HybridRideSerializer(ride)
        data = serializer.data

        assert data["manufacturer_name"] == "Test Manufacturer"
        assert "manufacturer_slug" in data

    def test__serialize__ride_with_designer__returns_designer_fields(self):
        """Test serializing includes designer information."""
        designer = DesignerCompanyFactory(name="Test Designer")
        ride = RideFactory(designer=designer)

        serializer = HybridRideSerializer(ride)
        data = serializer.data

        assert data["designer_name"] == "Test Designer"
        assert "designer_slug" in data

    def test__get_park_city__ride_with_park_location__returns_city(self):
        """Test get_park_city returns city from park location."""
        ride = RideFactory()

        mock_location = Mock()
        mock_location.city = "Orlando"
        mock_location.state = "FL"
        mock_location.country = "USA"

        ride.park.location = mock_location

        serializer = HybridRideSerializer(ride)

        assert serializer.get_park_city(ride) == "Orlando"

    def test__get_park_city__ride_without_park_location__returns_none(self):
        """Test get_park_city returns None when no location."""
        ride = RideFactory()
        ride.park.location = None

        serializer = HybridRideSerializer(ride)

        assert serializer.get_park_city(ride) is None

    def test__get_coaster_height_ft__ride_with_stats__returns_height(self):
        """Test get_coaster_height_ft returns height from coaster stats."""
        ride = RideFactory()

        mock_stats = Mock()
        mock_stats.height_ft = 205.5
        mock_stats.length_ft = 5000
        mock_stats.speed_mph = 70
        mock_stats.inversions = 4

        ride.coaster_stats = mock_stats

        serializer = HybridRideSerializer(ride)

        assert serializer.get_coaster_height_ft(ride) == 205.5

    def test__get_coaster_inversions__ride_with_stats__returns_inversions(self):
        """Test get_coaster_inversions returns inversions count."""
        ride = RideFactory()

        mock_stats = Mock()
        mock_stats.inversions = 7

        ride.coaster_stats = mock_stats

        serializer = HybridRideSerializer(ride)

        assert serializer.get_coaster_inversions(ride) == 7

    def test__get_coaster_height_ft__ride_without_stats__returns_none(self):
        """Test coaster stat methods return None when no stats."""
        ride = RideFactory()
        ride.coaster_stats = None

        serializer = HybridRideSerializer(ride)

        assert serializer.get_coaster_height_ft(ride) is None
        assert serializer.get_coaster_length_ft(ride) is None
        assert serializer.get_coaster_speed_mph(ride) is None
        assert serializer.get_coaster_inversions(ride) is None

    def test__get_banner_image_url__ride_with_banner__returns_url(self):
        """Test get_banner_image_url returns URL when banner exists."""
        ride = RideFactory()

        mock_image = Mock()
        mock_image.url = "https://example.com/ride-banner.jpg"

        mock_banner = Mock()
        mock_banner.image = mock_image

        ride.banner_image = mock_banner

        serializer = HybridRideSerializer(ride)

        assert serializer.get_banner_image_url(ride) == "https://example.com/ride-banner.jpg"

    def test__get_banner_image_url__ride_without_banner__returns_none(self):
        """Test get_banner_image_url returns None when no banner."""
        ride = RideFactory()
        ride.banner_image = None

        serializer = HybridRideSerializer(ride)

        assert serializer.get_banner_image_url(ride) is None

    def test__meta__all_fields_read_only(self):
        """Test all fields in HybridRideSerializer are read-only."""
        assert HybridRideSerializer.Meta.read_only_fields == HybridRideSerializer.Meta.fields

    def test__serialize__includes_ride_model_fields(self):
        """Test serializing includes ride model information."""
        ride = RideFactory()

        serializer = HybridRideSerializer(ride)
        data = serializer.data

        assert "ride_model_name" in data
        assert "ride_model_slug" in data
        assert "ride_model_category" in data


@pytest.mark.django_db
class TestRideSerializer(TestCase):
    """Tests for RideSerializer (legacy)."""

    def test__serialize__ride__returns_basic_fields(self):
        """Test serializing ride returns basic fields."""
        ride = RideFactory()

        serializer = RideSerializer(ride)
        data = serializer.data

        assert "id" in data
        assert "name" in data
        assert "slug" in data
        assert "category" in data
        assert "status" in data
        assert "opening_date" in data

    def test__serialize__multiple_rides__returns_list(self):
        """Test serializing multiple rides returns a list."""
        rides = [RideFactory() for _ in range(3)]

        serializer = RideSerializer(rides, many=True)

        assert len(serializer.data) == 3

    def test__meta__fields__matches_expected(self):
        """Test Meta.fields matches expected field list."""
        expected_fields = [
            "id",
            "name",
            "slug",
            "park",
            "manufacturer",
            "designer",
            "category",
            "status",
            "opening_date",
            "closing_date",
        ]

        assert list(RideSerializer.Meta.fields) == expected_fields


@pytest.mark.django_db
class TestRidePhotoSerializer(TestCase):
    """Tests for legacy RidePhotoSerializer."""

    def test__serialize__photo__returns_legacy_fields(self):
        """Test serializing photo returns legacy field set."""
        photo = RidePhotoFactory()

        serializer = RidePhotoSerializer(photo)
        data = serializer.data

        assert "id" in data
        assert "image" in data
        assert "caption" in data
        assert "alt_text" in data
        assert "is_primary" in data
        assert "photo_type" in data

    def test__meta__fields__matches_legacy_format(self):
        """Test Meta.fields matches legacy format."""
        expected_fields = [
            "id",
            "image",
            "caption",
            "alt_text",
            "is_primary",
            "photo_type",
            "uploaded_at",
            "uploaded_by",
        ]

        assert list(RidePhotoSerializer.Meta.fields) == expected_fields
