import uuid from datetime import datetime, timedelta from typing import Optional from fastapi import HTTPException, status from sqlalchemy.orm import Session from app.models.ticket import Ticket, TicketStatus from app.models.vehicle import Schedule, VehicleType def generate_ticket_number() -> str: """Generate a unique ticket number.""" return str(uuid.uuid4()) def validate_purchase_time(schedule: Schedule) -> None: """ Validate that a ticket can be purchased based on departure time. Args: schedule: The schedule for which the ticket is being purchased Raises: HTTPException: If ticket cannot be purchased due to time restrictions """ now = datetime.utcnow() # Check if it's less than 10 minutes to departure if schedule.departure_time - now < timedelta(minutes=10): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Cannot purchase ticket less than 10 minutes before departure", ) def validate_cancellation_time(ticket: Ticket) -> None: """ Validate that a ticket can be cancelled based on departure time. Args: ticket: The ticket to be cancelled Raises: HTTPException: If ticket cannot be cancelled due to time restrictions """ now = datetime.utcnow() # Check if it's less than 3 minutes to departure if ticket.schedule.departure_time - now < timedelta(minutes=3): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Cannot cancel ticket less than 3 minutes before departure", ) # Check if ticket is already used, cancelled, or expired if ticket.status != TicketStatus.ACTIVE: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"Cannot cancel ticket with status {ticket.status}", ) def assign_seat_number(db: Session, schedule: Schedule, vehicle_type: VehicleType) -> Optional[str]: """ Assign a seat number for a ticket if vehicle is a train. For cars and buses, seat number is None. Args: db: Database session schedule: The schedule for which the ticket is being purchased vehicle_type: Type of vehicle Returns: Seat number string for trains, None for other vehicle types """ if vehicle_type != VehicleType.TRAIN: return None # For trains, find the next available seat number # Get all occupied seats for this schedule occupied_seats = db.query(Ticket.seat_number).filter( Ticket.schedule_id == schedule.id, Ticket.status == TicketStatus.ACTIVE, Ticket.seat_number.isnot(None) ).all() occupied_seat_numbers = [int(seat[0]) for seat in occupied_seats if seat[0] and seat[0].isdigit()] # Find the first available seat for seat_num in range(1, schedule.vehicle.capacity + 1): if seat_num not in occupied_seat_numbers: return str(seat_num) # This should not happen if available_seats is managed correctly raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="No available seats", )