""" Services domain serializers for ThrillWiki API v1. This module contains serializers for various services like email, maps, history tracking, moderation, and roadtrip planning. """ from rest_framework import serializers from drf_spectacular.utils import ( extend_schema_serializer, extend_schema_field, OpenApiExample, ) # === EMAIL SERVICE SERIALIZERS === class EmailSendInputSerializer(serializers.Serializer): """Input serializer for sending emails.""" to = serializers.EmailField() subject = serializers.CharField(max_length=255) text = serializers.CharField() html = serializers.CharField(required=False) template = serializers.CharField(required=False) context = serializers.JSONField(required=False) class EmailTemplateOutputSerializer(serializers.Serializer): """Output serializer for email templates.""" id = serializers.CharField() name = serializers.CharField() subject = serializers.CharField() text_template = serializers.CharField() html_template = serializers.CharField(required=False) # === MAP SERVICE SERIALIZERS === class MapDataOutputSerializer(serializers.Serializer): """Output serializer for map data.""" parks = serializers.ListField(child=serializers.DictField()) rides = serializers.ListField(child=serializers.DictField()) bounds = serializers.DictField() zoom_level = serializers.IntegerField() class CoordinateInputSerializer(serializers.Serializer): """Input serializer for coordinate-based requests.""" latitude = serializers.FloatField(min_value=-90, max_value=90) longitude = serializers.FloatField(min_value=-180, max_value=180) radius_km = serializers.FloatField(min_value=0, max_value=1000, default=10) # === HISTORY SERIALIZERS === class HistoryEventSerializer(serializers.Serializer): """Base serializer for history events from pghistory.""" pgh_id = serializers.IntegerField(read_only=True) pgh_created_at = serializers.DateTimeField(read_only=True) pgh_label = serializers.CharField(read_only=True) pgh_obj_id = serializers.IntegerField(read_only=True) pgh_context = serializers.JSONField(read_only=True, allow_null=True) pgh_diff = serializers.SerializerMethodField() @extend_schema_field(serializers.DictField()) def get_pgh_diff(self, obj) -> dict: """Get diff from previous version if available.""" if hasattr(obj, "diff_against_previous"): return obj.diff_against_previous() return {} class HistoryEntryOutputSerializer(serializers.Serializer): """Output serializer for history entries.""" id = serializers.IntegerField() model_type = serializers.CharField() object_id = serializers.IntegerField() object_name = serializers.CharField() action = serializers.CharField() changes = serializers.JSONField() timestamp = serializers.DateTimeField() user = serializers.SerializerMethodField() @extend_schema_field(serializers.DictField(allow_null=True)) def get_user(self, obj) -> dict | None: if hasattr(obj, "user") and obj.user: return { "id": obj.user.id, "username": obj.user.username, } return None class HistoryCreateInputSerializer(serializers.Serializer): """Input serializer for creating history entries.""" action = serializers.CharField(max_length=50) description = serializers.CharField(max_length=500) metadata = serializers.JSONField(required=False) # === MODERATION SERIALIZERS === class ModerationSubmissionSerializer(serializers.Serializer): """Serializer for moderation submissions.""" submission_type = serializers.ChoiceField( choices=["EDIT", "PHOTO", "REVIEW"], help_text="Type of submission" ) content_type = serializers.CharField(help_text="Content type being modified") object_id = serializers.IntegerField(help_text="ID of object being modified") changes = serializers.JSONField(help_text="Changes being submitted") reason = serializers.CharField( max_length=500, required=False, allow_blank=True, help_text="Reason for the changes", ) class ModerationSubmissionOutputSerializer(serializers.Serializer): """Output serializer for moderation submission responses.""" status = serializers.CharField() message = serializers.CharField() submission_id = serializers.IntegerField(required=False) auto_approved = serializers.BooleanField(required=False) # === ROADTRIP SERIALIZERS === class RoadtripParkSerializer(serializers.Serializer): """Serializer for parks in roadtrip planning.""" id = serializers.IntegerField() name = serializers.CharField() slug = serializers.CharField() latitude = serializers.FloatField() longitude = serializers.FloatField() coaster_count = serializers.IntegerField() status = serializers.CharField() class RoadtripCreateInputSerializer(serializers.Serializer): """Input serializer for creating roadtrips.""" name = serializers.CharField(max_length=255) park_ids = serializers.ListField( child=serializers.IntegerField(), min_length=2, max_length=10, help_text="List of park IDs (2-10 parks)", ) start_date = serializers.DateField(required=False) end_date = serializers.DateField(required=False) notes = serializers.CharField(max_length=1000, required=False, allow_blank=True) def validate_park_ids(self, value): """Validate park IDs.""" if len(value) < 2: raise serializers.ValidationError("At least 2 parks are required") if len(value) > 10: raise serializers.ValidationError("Maximum 10 parks allowed") if len(set(value)) != len(value): raise serializers.ValidationError("Duplicate park IDs not allowed") return value class RoadtripOutputSerializer(serializers.Serializer): """Output serializer for roadtrip responses.""" id = serializers.CharField() name = serializers.CharField() parks = RoadtripParkSerializer(many=True) total_distance_miles = serializers.FloatField() estimated_drive_time_hours = serializers.FloatField() route_coordinates = serializers.ListField( child=serializers.ListField(child=serializers.FloatField()) ) created_at = serializers.DateTimeField() class GeocodeInputSerializer(serializers.Serializer): """Input serializer for geocoding requests.""" address = serializers.CharField(max_length=500, help_text="Address to geocode") class GeocodeOutputSerializer(serializers.Serializer): """Output serializer for geocoding responses.""" status = serializers.CharField() coordinates = serializers.JSONField(required=False) formatted_address = serializers.CharField(required=False) # === DISTANCE CALCULATION SERIALIZERS === class DistanceCalculationInputSerializer(serializers.Serializer): """Input serializer for distance calculation requests.""" park1_id = serializers.IntegerField(help_text="ID of first park") park2_id = serializers.IntegerField(help_text="ID of second park") def validate(self, data): """Validate that park IDs are different.""" if data["park1_id"] == data["park2_id"]: raise serializers.ValidationError("Park IDs must be different") return data class DistanceCalculationOutputSerializer(serializers.Serializer): """Output serializer for distance calculation responses.""" status = serializers.CharField() distance_miles = serializers.FloatField(required=False) distance_km = serializers.FloatField(required=False) drive_time_hours = serializers.FloatField(required=False) message = serializers.CharField(required=False)