""" Services for ride-related business logic. Following Django styleguide pattern for business logic encapsulation. """ from typing import Optional, Dict, Any from django.db import transaction from django.contrib.auth import get_user_model from django.contrib.auth.models import AbstractBaseUser from apps.rides.models import Ride # Use AbstractBaseUser for type hinting UserType = AbstractBaseUser User = get_user_model() class RideService: """Service for managing ride operations.""" @staticmethod def create_ride( *, name: str, park_id: int, description: str = "", status: str = "OPERATING", category: str = "", manufacturer_id: Optional[int] = None, designer_id: Optional[int] = None, ride_model_id: Optional[int] = None, park_area_id: Optional[int] = None, opening_date: Optional[str] = None, closing_date: Optional[str] = None, created_by: Optional[UserType] = None, ) -> Ride: """ Create a new ride with validation. Args: name: Ride name park_id: ID of the park description: Ride description status: Operating status category: Ride category manufacturer_id: ID of manufacturer company designer_id: ID of designer company ride_model_id: ID of ride model park_area_id: ID of park area opening_date: Opening date closing_date: Closing date created_by: User creating the ride Returns: Created Ride instance Raises: ValidationError: If ride data is invalid """ with transaction.atomic(): from apps.parks.models import Park # Get park park = Park.objects.get(id=park_id) # Create ride instance ride = Ride( name=name, park=park, description=description, status=status, category=category, opening_date=opening_date, closing_date=closing_date, ) # Set foreign key relationships if provided if park_area_id: from apps.parks.models import ParkArea ride.park_area = ParkArea.objects.get(id=park_area_id) if manufacturer_id: from apps.rides.models import Company ride.manufacturer = Company.objects.get(id=manufacturer_id) if designer_id: from apps.rides.models import Company ride.designer = Company.objects.get(id=designer_id) if ride_model_id: from apps.rides.models import RideModel ride.ride_model = RideModel.objects.get(id=ride_model_id) # CRITICAL STYLEGUIDE FIX: Call full_clean before save ride.full_clean() ride.save() return ride @staticmethod def update_ride( *, ride_id: int, updates: Dict[str, Any], updated_by: Optional[UserType] = None, ) -> Ride: """ Update an existing ride with validation. Args: ride_id: ID of ride to update updates: Dictionary of field updates updated_by: User performing the update Returns: Updated Ride instance Raises: Ride.DoesNotExist: If ride doesn't exist ValidationError: If update data is invalid """ with transaction.atomic(): ride = Ride.objects.select_for_update().get(id=ride_id) # Apply updates for field, value in updates.items(): if hasattr(ride, field): setattr(ride, field, value) # CRITICAL STYLEGUIDE FIX: Call full_clean before save ride.full_clean() ride.save() return ride @staticmethod def close_ride_temporarily( *, ride_id: int, user: Optional[UserType] = None ) -> Ride: """ Temporarily close a ride. Args: ride_id: ID of ride to close temporarily user: User performing the action Returns: Updated Ride instance Raises: Ride.DoesNotExist: If ride doesn't exist """ with transaction.atomic(): ride = Ride.objects.select_for_update().get(id=ride_id) ride.close_temporarily(user=user) return ride @staticmethod def mark_ride_sbno( *, ride_id: int, user: Optional[UserType] = None ) -> Ride: """ Mark a ride as SBNO (Standing But Not Operating). Args: ride_id: ID of ride to mark as SBNO user: User performing the action Returns: Updated Ride instance Raises: Ride.DoesNotExist: If ride doesn't exist """ with transaction.atomic(): ride = Ride.objects.select_for_update().get(id=ride_id) ride.mark_sbno(user=user) return ride @staticmethod def schedule_ride_closing( *, ride_id: int, closing_date, post_closing_status: str, user: Optional[UserType] = None, ) -> Ride: """ Schedule a ride to close on a specific date with a post-closing status. Args: ride_id: ID of ride to schedule for closing closing_date: Date when ride will close post_closing_status: Status to transition to after closing user: User performing the action Returns: Updated Ride instance Raises: Ride.DoesNotExist: If ride doesn't exist ValidationError: If post_closing_status is not set """ with transaction.atomic(): ride = Ride.objects.select_for_update().get(id=ride_id) ride.mark_closing( closing_date=closing_date, post_closing_status=post_closing_status, user=user, ) return ride @staticmethod def close_ride_permanently( *, ride_id: int, user: Optional[UserType] = None ) -> Ride: """ Permanently close a ride. Args: ride_id: ID of ride to close permanently user: User performing the action Returns: Updated Ride instance Raises: Ride.DoesNotExist: If ride doesn't exist """ with transaction.atomic(): ride = Ride.objects.select_for_update().get(id=ride_id) ride.close_permanently(user=user) return ride @staticmethod def demolish_ride(*, ride_id: int, user: Optional[UserType] = None) -> Ride: """ Mark a ride as demolished. Args: ride_id: ID of ride to demolish user: User performing the action Returns: Updated Ride instance Raises: Ride.DoesNotExist: If ride doesn't exist """ with transaction.atomic(): ride = Ride.objects.select_for_update().get(id=ride_id) ride.demolish(user=user) return ride @staticmethod def relocate_ride( *, ride_id: int, new_park_id: int, user: Optional[UserType] = None ) -> Ride: """ Relocate a ride to a new park. Args: ride_id: ID of ride to relocate new_park_id: ID of the new park user: User performing the action Returns: Updated Ride instance Raises: Ride.DoesNotExist: If ride doesn't exist """ with transaction.atomic(): from apps.parks.models import Park ride = Ride.objects.select_for_update().get(id=ride_id) new_park = Park.objects.get(id=new_park_id) # Mark as relocated first ride.relocate(user=user) # Move to new park ride.move_to_park(new_park, clear_park_area=True) return ride @staticmethod def reopen_ride(*, ride_id: int, user: Optional[UserType] = None) -> Ride: """ Reopen a ride for operation. Args: ride_id: ID of ride to reopen user: User performing the action Returns: Updated Ride instance Raises: Ride.DoesNotExist: If ride doesn't exist """ with transaction.atomic(): ride = Ride.objects.select_for_update().get(id=ride_id) ride.open(user=user) return ride