""" Parks domain serializers for ThrillWiki API v1. This module contains all serializers related to parks, park areas, park locations, and park search functionality. """ from rest_framework import serializers from drf_spectacular.utils import ( extend_schema_serializer, extend_schema_field, OpenApiExample, ) from .shared import LocationOutputSerializer, CompanyOutputSerializer, ModelChoices # === PARK SERIALIZERS === @extend_schema_serializer( examples=[ OpenApiExample( "Park List Example", summary="Example park list response", description="A typical park in the list view", value={ "id": 1, "name": "Cedar Point", "slug": "cedar-point", "status": "OPERATING", "description": "America's Roller Coast", "average_rating": 4.5, "coaster_count": 17, "ride_count": 70, "location": { "city": "Sandusky", "state": "Ohio", "country": "United States", }, "operator": {"id": 1, "name": "Cedar Fair", "slug": "cedar-fair"}, }, ) ] ) class ParkListOutputSerializer(serializers.Serializer): """Output serializer for park list view.""" id = serializers.IntegerField() name = serializers.CharField() slug = serializers.CharField() status = serializers.CharField() description = serializers.CharField() # Statistics average_rating = serializers.DecimalField( max_digits=3, decimal_places=2, allow_null=True ) coaster_count = serializers.IntegerField(allow_null=True) ride_count = serializers.IntegerField(allow_null=True) # Location (simplified for list view) location = LocationOutputSerializer(allow_null=True) # Operator info operator = CompanyOutputSerializer() # Metadata created_at = serializers.DateTimeField() updated_at = serializers.DateTimeField() @extend_schema_serializer( examples=[ OpenApiExample( "Park Detail Example", summary="Example park detail response", description="A complete park detail response", value={ "id": 1, "name": "Cedar Point", "slug": "cedar-point", "status": "OPERATING", "description": "America's Roller Coast", "opening_date": "1870-01-01", "website": "https://cedarpoint.com", "size_acres": 364.0, "average_rating": 4.5, "coaster_count": 17, "ride_count": 70, "location": { "latitude": 41.4793, "longitude": -82.6833, "city": "Sandusky", "state": "Ohio", "country": "United States", }, "operator": {"id": 1, "name": "Cedar Fair", "slug": "cedar-fair"}, }, ) ] ) class ParkDetailOutputSerializer(serializers.Serializer): """Output serializer for park detail view.""" id = serializers.IntegerField() name = serializers.CharField() slug = serializers.CharField() status = serializers.CharField() description = serializers.CharField() # Details opening_date = serializers.DateField(allow_null=True) closing_date = serializers.DateField(allow_null=True) operating_season = serializers.CharField() size_acres = serializers.DecimalField( max_digits=10, decimal_places=2, allow_null=True ) website = serializers.URLField() # Statistics average_rating = serializers.DecimalField( max_digits=3, decimal_places=2, allow_null=True ) coaster_count = serializers.IntegerField(allow_null=True) ride_count = serializers.IntegerField(allow_null=True) # Location (full details) location = LocationOutputSerializer(allow_null=True) # Companies operator = CompanyOutputSerializer() property_owner = CompanyOutputSerializer(allow_null=True) # Areas areas = serializers.SerializerMethodField() @extend_schema_field(serializers.ListField(child=serializers.DictField())) def get_areas(self, obj): """Get simplified area information.""" if hasattr(obj, "areas"): return [ { "id": area.id, "name": area.name, "slug": area.slug, "description": area.description, } for area in obj.areas.all() ] return [] # Metadata created_at = serializers.DateTimeField() updated_at = serializers.DateTimeField() class ParkCreateInputSerializer(serializers.Serializer): """Input serializer for creating parks.""" name = serializers.CharField(max_length=255) description = serializers.CharField(allow_blank=True, default="") status = serializers.ChoiceField( choices=ModelChoices.get_park_status_choices(), default="OPERATING" ) # Optional details opening_date = serializers.DateField(required=False, allow_null=True) closing_date = serializers.DateField(required=False, allow_null=True) operating_season = serializers.CharField( max_length=255, required=False, allow_blank=True ) size_acres = serializers.DecimalField( max_digits=10, decimal_places=2, required=False, allow_null=True ) website = serializers.URLField(required=False, allow_blank=True) # Required operator operator_id = serializers.IntegerField() # Optional property owner property_owner_id = serializers.IntegerField(required=False, allow_null=True) def validate(self, attrs): """Cross-field 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" ) return attrs class ParkUpdateInputSerializer(serializers.Serializer): """Input serializer for updating parks.""" name = serializers.CharField(max_length=255, required=False) description = serializers.CharField(allow_blank=True, required=False) status = serializers.ChoiceField( choices=ModelChoices.get_park_status_choices(), required=False ) # Optional details opening_date = serializers.DateField(required=False, allow_null=True) closing_date = serializers.DateField(required=False, allow_null=True) operating_season = serializers.CharField( max_length=255, required=False, allow_blank=True ) size_acres = serializers.DecimalField( max_digits=10, decimal_places=2, required=False, allow_null=True ) website = serializers.URLField(required=False, allow_blank=True) # Companies operator_id = serializers.IntegerField(required=False) property_owner_id = serializers.IntegerField(required=False, allow_null=True) def validate(self, attrs): """Cross-field 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" ) return attrs class ParkFilterInputSerializer(serializers.Serializer): """Input serializer for park filtering and search.""" # Search search = serializers.CharField(required=False, allow_blank=True) # Status filter status = serializers.MultipleChoiceField( choices=[], required=False, # Choices set dynamically ) # Location filters country = serializers.CharField(required=False, allow_blank=True) state = serializers.CharField(required=False, allow_blank=True) city = serializers.CharField(required=False, allow_blank=True) # Rating filter min_rating = serializers.DecimalField( max_digits=3, decimal_places=2, required=False, min_value=1, max_value=10, ) # Size filter min_size_acres = serializers.DecimalField( max_digits=10, decimal_places=2, required=False, min_value=0 ) max_size_acres = serializers.DecimalField( max_digits=10, decimal_places=2, required=False, min_value=0 ) # Company filters operator_id = serializers.IntegerField(required=False) property_owner_id = serializers.IntegerField(required=False) # Ordering ordering = serializers.ChoiceField( choices=[ "name", "-name", "opening_date", "-opening_date", "average_rating", "-average_rating", "coaster_count", "-coaster_count", "created_at", "-created_at", ], required=False, default="name", ) # === PARK AREA SERIALIZERS === @extend_schema_serializer( examples=[ OpenApiExample( "Park Area Example", summary="Example park area response", description="A themed area within a park", value={ "id": 1, "name": "Tomorrowland", "slug": "tomorrowland", "description": "A futuristic themed area", "park": {"id": 1, "name": "Magic Kingdom", "slug": "magic-kingdom"}, "opening_date": "1971-10-01", "closing_date": None, }, ) ] ) class ParkAreaDetailOutputSerializer(serializers.Serializer): """Output serializer for park areas.""" id = serializers.IntegerField() name = serializers.CharField() slug = serializers.CharField() description = serializers.CharField() opening_date = serializers.DateField(allow_null=True) closing_date = serializers.DateField(allow_null=True) # Park info park = serializers.SerializerMethodField() @extend_schema_field(serializers.DictField()) def get_park(self, obj) -> dict: return { "id": obj.park.id, "name": obj.park.name, "slug": obj.park.slug, } class ParkAreaCreateInputSerializer(serializers.Serializer): """Input serializer for creating park areas.""" name = serializers.CharField(max_length=255) description = serializers.CharField(allow_blank=True, default="") park_id = serializers.IntegerField() opening_date = serializers.DateField(required=False, allow_null=True) closing_date = serializers.DateField(required=False, allow_null=True) def validate(self, attrs): """Cross-field 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" ) return attrs class ParkAreaUpdateInputSerializer(serializers.Serializer): """Input serializer for updating park areas.""" name = serializers.CharField(max_length=255, required=False) description = serializers.CharField(allow_blank=True, required=False) opening_date = serializers.DateField(required=False, allow_null=True) closing_date = serializers.DateField(required=False, allow_null=True) def validate(self, attrs): """Cross-field 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" ) return attrs # === PARK LOCATION SERIALIZERS === class ParkLocationOutputSerializer(serializers.Serializer): """Output serializer for park locations.""" id = serializers.IntegerField() latitude = serializers.FloatField(allow_null=True) longitude = serializers.FloatField(allow_null=True) address = serializers.CharField() city = serializers.CharField() state = serializers.CharField() country = serializers.CharField() postal_code = serializers.CharField() formatted_address = serializers.CharField() # Park info park = serializers.SerializerMethodField() @extend_schema_field(serializers.DictField()) def get_park(self, obj) -> dict: return { "id": obj.park.id, "name": obj.park.name, "slug": obj.park.slug, } class ParkLocationCreateInputSerializer(serializers.Serializer): """Input serializer for creating park locations.""" park_id = serializers.IntegerField() latitude = serializers.FloatField(required=False, allow_null=True) longitude = serializers.FloatField(required=False, allow_null=True) address = serializers.CharField(max_length=255, allow_blank=True, default="") city = serializers.CharField(max_length=100) state = serializers.CharField(max_length=100) country = serializers.CharField(max_length=100) postal_code = serializers.CharField(max_length=20, allow_blank=True, default="") class ParkLocationUpdateInputSerializer(serializers.Serializer): """Input serializer for updating park locations.""" latitude = serializers.FloatField(required=False, allow_null=True) longitude = serializers.FloatField(required=False, allow_null=True) address = serializers.CharField(max_length=255, allow_blank=True, required=False) city = serializers.CharField(max_length=100, required=False) state = serializers.CharField(max_length=100, required=False) country = serializers.CharField(max_length=100, required=False) postal_code = serializers.CharField(max_length=20, allow_blank=True, required=False) # === PARKS SEARCH SERIALIZERS === class ParkSuggestionSerializer(serializers.Serializer): """Serializer for park search suggestions.""" id = serializers.IntegerField() name = serializers.CharField() slug = serializers.CharField() location = serializers.CharField() status = serializers.CharField() coaster_count = serializers.IntegerField() class ParkSuggestionOutputSerializer(serializers.Serializer): """Output serializer for park suggestions.""" results = ParkSuggestionSerializer(many=True) query = serializers.CharField() count = serializers.IntegerField()