"""
Rides domain serializers for ThrillWiki API v1.

This module contains all serializers related to rides, roller coaster statistics,
ride locations, and ride reviews.
"""

from drf_spectacular.utils import (
    OpenApiExample,
    extend_schema_field,
    extend_schema_serializer,
)
from rest_framework import serializers

from apps.core.choices.serializers import RichChoiceFieldSerializer
from config.django import base as settings

from .shared import ModelChoices

# === RIDE SERIALIZERS ===


class RideParkOutputSerializer(serializers.Serializer):
    """Output serializer for ride's park data."""

    id = serializers.IntegerField()
    name = serializers.CharField()
    slug = serializers.CharField()
    url = serializers.SerializerMethodField()

    @extend_schema_field(serializers.URLField())
    def get_url(self, obj) -> str:
        """Generate the frontend URL for this park."""
        return f"{settings.FRONTEND_DOMAIN}/parks/{obj.slug}/"


class RideModelOutputSerializer(serializers.Serializer):
    """Output serializer for ride model data."""

    id = serializers.IntegerField()
    name = serializers.CharField()
    description = serializers.CharField()
    category = serializers.CharField()
    manufacturer = serializers.SerializerMethodField()

    @extend_schema_field(serializers.DictField(allow_null=True))
    def get_manufacturer(self, obj) -> dict | None:
        if obj.manufacturer:
            return {
                "id": obj.manufacturer.id,
                "name": obj.manufacturer.name,
                "slug": obj.manufacturer.slug,
            }
        return None


@extend_schema_serializer(
    examples=[
        OpenApiExample(
            "Ride List Example",
            summary="Example ride list response",
            description="A typical ride in the list view",
            value={
                "id": 1,
                "name": "Steel Vengeance",
                "slug": "steel-vengeance",
                "category": "ROLLER_COASTER",
                "status": "OPERATING",
                "description": "Hybrid roller coaster",
                "park": {"id": 1, "name": "Cedar Point", "slug": "cedar-point"},
                "average_rating": 4.8,
                "capacity_per_hour": 1200,
                "opening_date": "2018-05-05",
            },
        )
    ]
)
class RideListOutputSerializer(serializers.Serializer):
    """Output serializer for ride list view."""

    id = serializers.IntegerField()
    name = serializers.CharField()
    slug = serializers.CharField()
    category = RichChoiceFieldSerializer(choice_group="categories", domain="rides")
    status = RichChoiceFieldSerializer(choice_group="statuses", domain="rides")
    description = serializers.CharField()

    # Park info
    park = RideParkOutputSerializer()

    # Statistics
    average_rating = serializers.DecimalField(max_digits=3, decimal_places=2, allow_null=True)
    capacity_per_hour = serializers.IntegerField(allow_null=True)

    # Dates
    opening_date = serializers.DateField(allow_null=True)
    closing_date = serializers.DateField(allow_null=True)

    # URL
    url = serializers.SerializerMethodField()

    # Metadata
    created_at = serializers.DateTimeField()
    updated_at = serializers.DateTimeField()

    @extend_schema_field(serializers.URLField())
    def get_url(self, obj) -> str:
        """Generate the frontend URL for this ride."""
        return f"{settings.FRONTEND_DOMAIN}/parks/{obj.park.slug}/rides/{obj.slug}/"


@extend_schema_serializer(
    examples=[
        OpenApiExample(
            "Ride Detail Example",
            summary="Example ride detail response",
            description="A complete ride detail response",
            value={
                "id": 1,
                "name": "Steel Vengeance",
                "slug": "steel-vengeance",
                "category": "ROLLER_COASTER",
                "status": "OPERATING",
                "description": "Hybrid roller coaster featuring RMC I-Box track",
                "park": {"id": 1, "name": "Cedar Point", "slug": "cedar-point"},
                "opening_date": "2018-05-05",
                "min_height_in": 48,
                "capacity_per_hour": 1200,
                "ride_duration_seconds": 150,
                "average_rating": 4.8,
                "manufacturer": {
                    "id": 1,
                    "name": "Rocky Mountain Construction",
                    "slug": "rocky-mountain-construction",
                },
                "photos": [
                    {
                        "id": 123,
                        "image_url": "https://imagedelivery.net/account-hash/abc123def456/public",
                        "image_variants": {
                            "thumbnail": "https://imagedelivery.net/account-hash/abc123def456/thumbnail",
                            "medium": "https://imagedelivery.net/account-hash/abc123def456/medium",
                            "large": "https://imagedelivery.net/account-hash/abc123def456/large",
                            "public": "https://imagedelivery.net/account-hash/abc123def456/public",
                        },
                        "caption": "Amazing roller coaster photo",
                        "is_primary": True,
                        "photo_type": "exterior",
                    }
                ],
                "primary_photo": {
                    "id": 123,
                    "image_url": "https://imagedelivery.net/account-hash/abc123def456/public",
                    "image_variants": {
                        "thumbnail": "https://imagedelivery.net/account-hash/abc123def456/thumbnail",
                        "medium": "https://imagedelivery.net/account-hash/abc123def456/medium",
                        "large": "https://imagedelivery.net/account-hash/abc123def456/large",
                        "public": "https://imagedelivery.net/account-hash/abc123def456/public",
                    },
                    "caption": "Amazing roller coaster photo",
                    "photo_type": "exterior",
                },
            },
        )
    ]
)
class RideDetailOutputSerializer(serializers.Serializer):
    """Output serializer for ride detail view."""

    id = serializers.IntegerField()
    name = serializers.CharField()
    slug = serializers.CharField()
    category = RichChoiceFieldSerializer(choice_group="categories", domain="rides")
    status = RichChoiceFieldSerializer(choice_group="statuses", domain="rides")
    post_closing_status = RichChoiceFieldSerializer(
        choice_group="post_closing_statuses", domain="rides", allow_null=True
    )
    description = serializers.CharField()

    # Park info
    park = RideParkOutputSerializer()
    park_area = serializers.SerializerMethodField()

    # Dates
    opening_date = serializers.DateField(allow_null=True)
    closing_date = serializers.DateField(allow_null=True)
    status_since = serializers.DateField(allow_null=True)

    # Physical specs
    min_height_in = serializers.IntegerField(allow_null=True)
    max_height_in = serializers.IntegerField(allow_null=True)
    capacity_per_hour = serializers.IntegerField(allow_null=True)
    ride_duration_seconds = serializers.IntegerField(allow_null=True)

    # Statistics
    average_rating = serializers.DecimalField(max_digits=3, decimal_places=2, allow_null=True)

    # Companies
    manufacturer = serializers.SerializerMethodField()
    designer = serializers.SerializerMethodField()

    # Model
    ride_model = RideModelOutputSerializer(allow_null=True)

    # Photos
    photos = serializers.SerializerMethodField()
    primary_photo = serializers.SerializerMethodField()
    banner_image = serializers.SerializerMethodField()
    card_image = serializers.SerializerMethodField()

    # Former names (name history)
    former_names = serializers.SerializerMethodField()

    # URL
    url = serializers.SerializerMethodField()

    # Metadata
    created_at = serializers.DateTimeField()
    updated_at = serializers.DateTimeField()

    @extend_schema_field(serializers.URLField())
    def get_url(self, obj) -> str:
        """Generate the frontend URL for this ride."""
        return f"{settings.FRONTEND_DOMAIN}/parks/{obj.park.slug}/rides/{obj.slug}/"

    @extend_schema_field(serializers.DictField(allow_null=True))
    def get_park_area(self, obj) -> dict | None:
        if obj.park_area:
            return {
                "id": obj.park_area.id,
                "name": obj.park_area.name,
                "slug": obj.park_area.slug,
            }
        return None

    @extend_schema_field(serializers.DictField(allow_null=True))
    def get_manufacturer(self, obj) -> dict | None:
        if obj.manufacturer:
            return {
                "id": obj.manufacturer.id,
                "name": obj.manufacturer.name,
                "slug": obj.manufacturer.slug,
            }
        return None

    @extend_schema_field(serializers.DictField(allow_null=True))
    def get_designer(self, obj) -> dict | None:
        if obj.designer:
            return {
                "id": obj.designer.id,
                "name": obj.designer.name,
                "slug": obj.designer.slug,
            }
        return None

    @extend_schema_field(serializers.ListField(child=serializers.DictField()))
    def get_photos(self, obj):
        """Get all approved photos for this ride."""
        from apps.rides.models import RidePhoto

        photos = RidePhoto.objects.filter(ride=obj, is_approved=True).order_by("-is_primary", "-created_at")[
            :10
        ]  # Limit to 10 photos

        return [
            {
                "id": photo.id,
                "image_url": photo.image.public_url if photo.image else None,
                "image_variants": (
                    {
                        "thumbnail": (f"{photo.image.public_url}/thumbnail" if photo.image else None),
                        "medium": f"{photo.image.public_url}/medium" if photo.image else None,
                        "large": f"{photo.image.public_url}/large" if photo.image else None,
                        "public": f"{photo.image.public_url}/public" if photo.image else None,
                    }
                    if photo.image
                    else {}
                ),
                "caption": photo.caption,
                "alt_text": photo.alt_text,
                "is_primary": photo.is_primary,
                "photo_type": photo.photo_type,
            }
            for photo in photos
        ]

    @extend_schema_field(serializers.DictField(allow_null=True))
    def get_primary_photo(self, obj):
        """Get the primary photo for this ride."""
        from apps.rides.models import RidePhoto

        try:
            photo = RidePhoto.objects.filter(ride=obj, is_primary=True, is_approved=True).first()

            if photo and photo.image:
                return {
                    "id": photo.id,
                    "image_url": photo.image.public_url,
                    "image_variants": {
                        "thumbnail": f"{photo.image.public_url}/thumbnail",
                        "medium": f"{photo.image.public_url}/medium",
                        "large": f"{photo.image.public_url}/large",
                        "public": f"{photo.image.public_url}/public",
                    },
                    "caption": photo.caption,
                    "alt_text": photo.alt_text,
                    "photo_type": photo.photo_type,
                }
        except Exception:
            pass

        return None

    @extend_schema_field(serializers.DictField(allow_null=True))
    def get_banner_image(self, obj):
        """Get the banner image for this ride with fallback to latest photo."""
        # First try the explicitly set banner image
        if obj.banner_image and obj.banner_image.image:
            return {
                "id": obj.banner_image.id,
                "image_url": obj.banner_image.image.public_url,
                "image_variants": {
                    "thumbnail": f"{obj.banner_image.image.public_url}/thumbnail",
                    "medium": f"{obj.banner_image.image.public_url}/medium",
                    "large": f"{obj.banner_image.image.public_url}/large",
                    "public": f"{obj.banner_image.image.public_url}/public",
                },
                "caption": obj.banner_image.caption,
                "alt_text": obj.banner_image.alt_text,
                "photo_type": obj.banner_image.photo_type,
            }

        # Fallback to latest approved photo
        from apps.rides.models import RidePhoto

        try:
            latest_photo = (
                RidePhoto.objects.filter(ride=obj, is_approved=True, image__isnull=False)
                .order_by("-created_at")
                .first()
            )

            if latest_photo and latest_photo.image:
                return {
                    "id": latest_photo.id,
                    "image_url": latest_photo.image.public_url,
                    "image_variants": {
                        "thumbnail": f"{latest_photo.image.public_url}/thumbnail",
                        "medium": f"{latest_photo.image.public_url}/medium",
                        "large": f"{latest_photo.image.public_url}/large",
                        "public": f"{latest_photo.image.public_url}/public",
                    },
                    "caption": latest_photo.caption,
                    "alt_text": latest_photo.alt_text,
                    "photo_type": latest_photo.photo_type,
                    "is_fallback": True,
                }
        except Exception:
            pass

        return None

    @extend_schema_field(serializers.DictField(allow_null=True))
    def get_card_image(self, obj):
        """Get the card image for this ride with fallback to latest photo."""
        # First try the explicitly set card image
        if obj.card_image and obj.card_image.image:
            return {
                "id": obj.card_image.id,
                "image_url": obj.card_image.image.public_url,
                "image_variants": {
                    "thumbnail": f"{obj.card_image.image.public_url}/thumbnail",
                    "medium": f"{obj.card_image.image.public_url}/medium",
                    "large": f"{obj.card_image.image.public_url}/large",
                    "public": f"{obj.card_image.image.public_url}/public",
                },
                "caption": obj.card_image.caption,
                "alt_text": obj.card_image.alt_text,
                "photo_type": obj.card_image.photo_type,
            }

        # Fallback to latest approved photo
        from apps.rides.models import RidePhoto

        try:
            latest_photo = (
                RidePhoto.objects.filter(ride=obj, is_approved=True, image__isnull=False)
                .order_by("-created_at")
                .first()
            )

            if latest_photo and latest_photo.image:
                return {
                    "id": latest_photo.id,
                    "image_url": latest_photo.image.public_url,
                    "image_variants": {
                        "thumbnail": f"{latest_photo.image.public_url}/thumbnail",
                        "medium": f"{latest_photo.image.public_url}/medium",
                        "large": f"{latest_photo.image.public_url}/large",
                        "public": f"{latest_photo.image.public_url}/public",
                    },
                    "caption": latest_photo.caption,
                    "alt_text": latest_photo.alt_text,
                    "photo_type": latest_photo.photo_type,
                    "is_fallback": True,
                }
        except Exception:
            pass

        return None

    @extend_schema_field(serializers.ListField(child=serializers.DictField()))
    def get_former_names(self, obj):
        """Get the former names (name history) for this ride."""
        from apps.rides.models import RideNameHistory

        former_names = RideNameHistory.objects.filter(ride=obj).order_by("-to_year", "-from_year")

        return [
            {
                "id": entry.id,
                "former_name": entry.former_name,
                "from_year": entry.from_year,
                "to_year": entry.to_year,
                "reason": entry.reason,
            }
            for entry in former_names
        ]


class RideImageSettingsInputSerializer(serializers.Serializer):
    """Input serializer for setting ride banner and card images."""

    banner_image_id = serializers.IntegerField(required=False, allow_null=True)
    card_image_id = serializers.IntegerField(required=False, allow_null=True)

    def validate_banner_image_id(self, value):
        """Validate that the banner image belongs to the same ride."""
        if value is not None:
            from apps.rides.models import RidePhoto

            try:
                RidePhoto.objects.get(id=value)
                # The ride will be validated in the view
                return value
            except RidePhoto.DoesNotExist:
                raise serializers.ValidationError("Photo not found") from None
        return value

    def validate_card_image_id(self, value):
        """Validate that the card image belongs to the same ride."""
        if value is not None:
            from apps.rides.models import RidePhoto

            try:
                RidePhoto.objects.get(id=value)
                # The ride will be validated in the view
                return value
            except RidePhoto.DoesNotExist:
                raise serializers.ValidationError("Photo not found") from None
        return value


class RideCreateInputSerializer(serializers.Serializer):
    """Input serializer for creating rides."""

    name = serializers.CharField(max_length=255)
    description = serializers.CharField(allow_blank=True, default="")
    category = serializers.ChoiceField(choices=ModelChoices.get_ride_category_choices())
    status = serializers.ChoiceField(choices=ModelChoices.get_ride_status_choices(), default="OPERATING")

    # Required park
    park_id = serializers.IntegerField()

    # Optional area
    park_area_id = serializers.IntegerField(required=False, allow_null=True)

    # Optional dates
    opening_date = serializers.DateField(required=False, allow_null=True)
    closing_date = serializers.DateField(required=False, allow_null=True)
    status_since = serializers.DateField(required=False, allow_null=True)

    # Optional specs
    min_height_in = serializers.IntegerField(required=False, allow_null=True, min_value=30, max_value=90)
    max_height_in = serializers.IntegerField(required=False, allow_null=True, min_value=30, max_value=90)
    capacity_per_hour = serializers.IntegerField(required=False, allow_null=True, min_value=1)
    ride_duration_seconds = serializers.IntegerField(required=False, allow_null=True, min_value=1)

    # Optional companies
    manufacturer_id = serializers.IntegerField(required=False, allow_null=True)
    designer_id = serializers.IntegerField(required=False, allow_null=True)

    # Optional model
    ride_model_id = serializers.IntegerField(required=False, allow_null=True)

    def validate(self, attrs):
        """Cross-field validation."""
        # Date validation
        opening_date = attrs.get("opening_date")
        closing_date = attrs.get("closing_date")

        if opening_date and closing_date and closing_date < opening_date:
            raise serializers.ValidationError("Closing date cannot be before opening date")

        # Height validation
        min_height = attrs.get("min_height_in")
        max_height = attrs.get("max_height_in")

        if min_height and max_height and min_height > max_height:
            raise serializers.ValidationError("Minimum height cannot be greater than maximum height")

        # Park area validation when park changes
        park_id = attrs.get("park_id")
        park_area_id = attrs.get("park_area_id")

        if park_id and park_area_id:
            try:
                from apps.parks.models import ParkArea

                park_area = ParkArea.objects.get(id=park_area_id)
                if park_area.park_id != park_id:
                    raise serializers.ValidationError(
                        f"Park area '{park_area.name}' does not belong to the selected park"
                    )
            except Exception:
                # If models aren't available or area doesn't exist, let the view handle it
                pass

        return attrs


class RideUpdateInputSerializer(serializers.Serializer):
    """Input serializer for updating rides."""

    name = serializers.CharField(max_length=255, required=False)
    description = serializers.CharField(allow_blank=True, required=False)
    category = serializers.ChoiceField(choices=ModelChoices.get_ride_category_choices(), required=False)
    status = serializers.ChoiceField(choices=ModelChoices.get_ride_status_choices(), required=False)
    post_closing_status = serializers.ChoiceField(
        choices=ModelChoices.get_ride_post_closing_choices(),
        required=False,
        allow_null=True,
    )

    # Park and area
    park_id = serializers.IntegerField(required=False)
    park_area_id = serializers.IntegerField(required=False, allow_null=True)

    # Dates
    opening_date = serializers.DateField(required=False, allow_null=True)
    closing_date = serializers.DateField(required=False, allow_null=True)
    status_since = serializers.DateField(required=False, allow_null=True)

    # Specs
    min_height_in = serializers.IntegerField(required=False, allow_null=True, min_value=30, max_value=90)
    max_height_in = serializers.IntegerField(required=False, allow_null=True, min_value=30, max_value=90)
    capacity_per_hour = serializers.IntegerField(required=False, allow_null=True, min_value=1)
    ride_duration_seconds = serializers.IntegerField(required=False, allow_null=True, min_value=1)

    # Companies
    manufacturer_id = serializers.IntegerField(required=False, allow_null=True)
    designer_id = serializers.IntegerField(required=False, allow_null=True)

    # Model
    ride_model_id = serializers.IntegerField(required=False, allow_null=True)

    def validate(self, attrs):
        """Cross-field validation."""
        # Date validation
        opening_date = attrs.get("opening_date")
        closing_date = attrs.get("closing_date")

        if opening_date and closing_date and closing_date < opening_date:
            raise serializers.ValidationError("Closing date cannot be before opening date")

        # Height validation
        min_height = attrs.get("min_height_in")
        max_height = attrs.get("max_height_in")

        if min_height and max_height and min_height > max_height:
            raise serializers.ValidationError("Minimum height cannot be greater than maximum height")

        return attrs


class RideFilterInputSerializer(serializers.Serializer):
    """Input serializer for ride filtering and search."""

    # Search
    search = serializers.CharField(required=False, allow_blank=True)

    # Category filter
    category = serializers.MultipleChoiceField(choices=ModelChoices.get_ride_category_choices(), required=False)

    # Status filter
    status = serializers.MultipleChoiceField(
        choices=ModelChoices.get_ride_status_choices(),
        required=False,
    )

    # Park filter
    park_id = serializers.IntegerField(required=False)
    park_slug = serializers.CharField(required=False, allow_blank=True)

    # Company filters
    manufacturer_id = serializers.IntegerField(required=False)
    designer_id = serializers.IntegerField(required=False)

    # Rating filter
    min_rating = serializers.DecimalField(
        max_digits=3,
        decimal_places=2,
        required=False,
        min_value=1,
        max_value=10,
    )

    # Height filters
    min_height_requirement = serializers.IntegerField(required=False)
    max_height_requirement = serializers.IntegerField(required=False)

    # Capacity filter
    min_capacity = serializers.IntegerField(required=False)

    # Ordering
    ordering = serializers.ChoiceField(
        choices=[
            "name",
            "-name",
            "opening_date",
            "-opening_date",
            "average_rating",
            "-average_rating",
            "capacity_per_hour",
            "-capacity_per_hour",
            "created_at",
            "-created_at",
        ],
        required=False,
        default="name",
    )


# === ROLLER COASTER STATS SERIALIZERS ===


@extend_schema_serializer(
    examples=[
        OpenApiExample(
            "Roller Coaster Stats Example",
            summary="Example roller coaster statistics",
            description="Detailed statistics for a roller coaster",
            value={
                "id": 1,
                "ride": {"id": 1, "name": "Steel Vengeance", "slug": "steel-vengeance"},
                "height_ft": 205.0,
                "length_ft": 5740.0,
                "speed_mph": 74.0,
                "inversions": 4,
                "ride_time_seconds": 150,
                "track_material": "HYBRID",
                "roller_coaster_type": "SITDOWN",
                "propulsion_system": "CHAIN",
            },
        )
    ]
)
class RollerCoasterStatsOutputSerializer(serializers.Serializer):
    """Output serializer for roller coaster statistics."""

    id = serializers.IntegerField()
    height_ft = serializers.DecimalField(max_digits=6, decimal_places=2, allow_null=True)
    length_ft = serializers.DecimalField(max_digits=7, decimal_places=2, allow_null=True)
    speed_mph = serializers.DecimalField(max_digits=5, decimal_places=2, allow_null=True)
    inversions = serializers.IntegerField()
    ride_time_seconds = serializers.IntegerField(allow_null=True)
    track_type = serializers.CharField()
    track_material = RichChoiceFieldSerializer(choice_group="track_materials", domain="rides")
    roller_coaster_type = RichChoiceFieldSerializer(choice_group="coaster_types", domain="rides")
    max_drop_height_ft = serializers.DecimalField(max_digits=6, decimal_places=2, allow_null=True)
    propulsion_system = RichChoiceFieldSerializer(choice_group="propulsion_systems", domain="rides")
    train_style = serializers.CharField()
    trains_count = serializers.IntegerField(allow_null=True)
    cars_per_train = serializers.IntegerField(allow_null=True)
    seats_per_car = serializers.IntegerField(allow_null=True)

    # Ride info
    ride = serializers.SerializerMethodField()

    @extend_schema_field(serializers.DictField())
    def get_ride(self, obj) -> dict:
        return {
            "id": obj.ride.id,
            "name": obj.ride.name,
            "slug": obj.ride.slug,
        }


class RollerCoasterStatsCreateInputSerializer(serializers.Serializer):
    """Input serializer for creating roller coaster statistics."""

    ride_id = serializers.IntegerField()
    height_ft = serializers.DecimalField(max_digits=6, decimal_places=2, required=False, allow_null=True)
    length_ft = serializers.DecimalField(max_digits=7, decimal_places=2, required=False, allow_null=True)
    speed_mph = serializers.DecimalField(max_digits=5, decimal_places=2, required=False, allow_null=True)
    inversions = serializers.IntegerField(default=0)
    ride_time_seconds = serializers.IntegerField(required=False, allow_null=True)
    track_type = serializers.CharField(max_length=255, allow_blank=True, default="")
    track_material = serializers.ChoiceField(choices=ModelChoices.get_coaster_track_choices(), default="STEEL")
    roller_coaster_type = serializers.ChoiceField(choices=ModelChoices.get_coaster_type_choices(), default="SITDOWN")
    max_drop_height_ft = serializers.DecimalField(max_digits=6, decimal_places=2, required=False, allow_null=True)
    propulsion_system = serializers.ChoiceField(choices=ModelChoices.get_propulsion_system_choices(), default="CHAIN")
    train_style = serializers.CharField(max_length=255, allow_blank=True, default="")
    trains_count = serializers.IntegerField(required=False, allow_null=True)
    cars_per_train = serializers.IntegerField(required=False, allow_null=True)
    seats_per_car = serializers.IntegerField(required=False, allow_null=True)


class RollerCoasterStatsUpdateInputSerializer(serializers.Serializer):
    """Input serializer for updating roller coaster statistics."""

    height_ft = serializers.DecimalField(max_digits=6, decimal_places=2, required=False, allow_null=True)
    length_ft = serializers.DecimalField(max_digits=7, decimal_places=2, required=False, allow_null=True)
    speed_mph = serializers.DecimalField(max_digits=5, decimal_places=2, required=False, allow_null=True)
    inversions = serializers.IntegerField(required=False)
    ride_time_seconds = serializers.IntegerField(required=False, allow_null=True)
    track_type = serializers.CharField(max_length=255, allow_blank=True, required=False)
    track_material = serializers.ChoiceField(choices=ModelChoices.get_coaster_track_choices(), required=False)
    roller_coaster_type = serializers.ChoiceField(choices=ModelChoices.get_coaster_type_choices(), required=False)
    max_drop_height_ft = serializers.DecimalField(max_digits=6, decimal_places=2, required=False, allow_null=True)
    propulsion_system = serializers.ChoiceField(choices=ModelChoices.get_propulsion_system_choices(), required=False)
    train_style = serializers.CharField(max_length=255, allow_blank=True, required=False)
    trains_count = serializers.IntegerField(required=False, allow_null=True)
    cars_per_train = serializers.IntegerField(required=False, allow_null=True)
    seats_per_car = serializers.IntegerField(required=False, allow_null=True)


# === RIDE LOCATION SERIALIZERS ===


class RideLocationOutputSerializer(serializers.Serializer):
    """Output serializer for ride locations."""

    id = serializers.IntegerField()
    latitude = serializers.FloatField(allow_null=True)
    longitude = serializers.FloatField(allow_null=True)
    coordinates = serializers.CharField()

    # Ride info
    ride = serializers.SerializerMethodField()

    @extend_schema_field(serializers.DictField())
    def get_ride(self, obj) -> dict:
        return {
            "id": obj.ride.id,
            "name": obj.ride.name,
            "slug": obj.ride.slug,
        }


class RideLocationCreateInputSerializer(serializers.Serializer):
    """Input serializer for creating ride locations."""

    ride_id = serializers.IntegerField()
    latitude = serializers.FloatField(required=False, allow_null=True)
    longitude = serializers.FloatField(required=False, allow_null=True)


class RideLocationUpdateInputSerializer(serializers.Serializer):
    """Input serializer for updating ride locations."""

    latitude = serializers.FloatField(required=False, allow_null=True)
    longitude = serializers.FloatField(required=False, allow_null=True)


# === RIDE REVIEW SERIALIZERS ===


@extend_schema_serializer(
    examples=[
        OpenApiExample(
            "Ride Review Example",
            summary="Example ride review response",
            description="A user review of a ride",
            value={
                "id": 1,
                "rating": 9,
                "title": "Amazing coaster!",
                "content": "This ride was incredible, the airtime was fantastic.",
                "visit_date": "2024-08-15",
                "ride": {"id": 1, "name": "Steel Vengeance", "slug": "steel-vengeance"},
                "user": {"username": "coaster_fan", "display_name": "Coaster Fan"},
                "created_at": "2024-08-16T10:30:00Z",
                "is_published": True,
            },
        )
    ]
)
class RideReviewOutputSerializer(serializers.Serializer):
    """Output serializer for ride reviews."""

    id = serializers.IntegerField()
    rating = serializers.IntegerField()
    title = serializers.CharField()
    content = serializers.CharField()
    visit_date = serializers.DateField()
    created_at = serializers.DateTimeField()
    updated_at = serializers.DateTimeField()
    is_published = serializers.BooleanField()

    # Ride info
    ride = serializers.SerializerMethodField()
    # User info (limited for privacy)
    user = serializers.SerializerMethodField()

    @extend_schema_field(serializers.DictField())
    def get_ride(self, obj) -> dict:
        return {
            "id": obj.ride.id,
            "name": obj.ride.name,
            "slug": obj.ride.slug,
        }

    @extend_schema_field(serializers.DictField())
    def get_user(self, obj) -> dict:
        return {
            "username": obj.user.username,
            "display_name": obj.user.get_display_name(),
        }


class RideReviewCreateInputSerializer(serializers.Serializer):
    """Input serializer for creating ride reviews."""

    ride_id = serializers.IntegerField()
    rating = serializers.IntegerField(min_value=1, max_value=10)
    title = serializers.CharField(max_length=200)
    content = serializers.CharField()
    visit_date = serializers.DateField()

    def validate_visit_date(self, value):
        """Validate visit date is not in the future."""
        from django.utils import timezone

        if value > timezone.now().date():
            raise serializers.ValidationError("Visit date cannot be in the future")
        return value


class RideReviewUpdateInputSerializer(serializers.Serializer):
    """Input serializer for updating ride reviews."""

    rating = serializers.IntegerField(min_value=1, max_value=10, required=False)
    title = serializers.CharField(max_length=200, required=False)
    content = serializers.CharField(required=False)
    visit_date = serializers.DateField(required=False)

    def validate_visit_date(self, value):
        """Validate visit date is not in the future."""
        from django.utils import timezone

        if value and value > timezone.now().date():
            raise serializers.ValidationError("Visit date cannot be in the future")
        return value


# === RIDE NAME HISTORY SERIALIZERS ===


class RideNameHistoryOutputSerializer(serializers.Serializer):
    """Output serializer for ride name history (former names)."""

    id = serializers.IntegerField()
    former_name = serializers.CharField()
    from_year = serializers.IntegerField(allow_null=True)
    to_year = serializers.IntegerField(allow_null=True)
    reason = serializers.CharField()
    created_at = serializers.DateTimeField()


class RideNameHistoryCreateInputSerializer(serializers.Serializer):
    """Input serializer for creating ride name history entries."""

    former_name = serializers.CharField(max_length=200)
    from_year = serializers.IntegerField(required=False, allow_null=True, min_value=1800, max_value=2100)
    to_year = serializers.IntegerField(required=False, allow_null=True, min_value=1800, max_value=2100)
    reason = serializers.CharField(max_length=500, required=False, allow_blank=True, default="")

    def validate(self, attrs):
        """Validate year range."""
        from_year = attrs.get("from_year")
        to_year = attrs.get("to_year")

        if from_year and to_year and from_year > to_year:
            raise serializers.ValidationError("From year cannot be after to year")

        return attrs

