from datetime import datetime from typing import Any, List, Optional from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app.core.auth import get_current_active_user from app.db.session import get_db from app.models.ticket import Ticket, TicketStatus from app.models.user import User from app.models.vehicle import Schedule, Vehicle, VehicleType from app.schemas.ticket import ( Ticket as TicketSchema, ) from app.schemas.ticket import ( TicketCreate, ) from app.services.ticket_service import ( assign_seat_number, generate_ticket_number, validate_cancellation_time, validate_purchase_time, ) router = APIRouter() @router.post("/", response_model=TicketSchema, status_code=status.HTTP_201_CREATED) def create_ticket( *, db: Session = Depends(get_db), ticket_in: TicketCreate, current_user: User = Depends(get_current_active_user), ) -> Any: """ Purchase a ticket. """ # Check if schedule exists and is active schedule = db.query(Schedule).join(Vehicle).filter( Schedule.id == ticket_in.schedule_id, Schedule.is_active == True, Vehicle.is_active == True ).first() if not schedule: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Schedule not found", ) # Check if there are available seats if schedule.available_seats <= 0: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="No available seats for this schedule", ) # Validate purchase time (at least 10 minutes before departure) validate_purchase_time(schedule) # For trains, assign a seat number vehicle_type = schedule.vehicle.vehicle_type if vehicle_type == VehicleType.TRAIN: seat_number = assign_seat_number(db, schedule, vehicle_type) else: seat_number = ticket_in.seat_number # Create ticket ticket = Ticket( user_id=current_user.id, schedule_id=ticket_in.schedule_id, seat_number=seat_number, purchase_time=datetime.utcnow(), status=TicketStatus.ACTIVE, is_active=True, ticket_number=generate_ticket_number(), ) # Update available seats in schedule schedule.available_seats -= 1 db.add(ticket) db.add(schedule) db.commit() db.refresh(ticket) return ticket @router.get("/", response_model=List[TicketSchema]) def list_tickets( *, db: Session = Depends(get_db), skip: int = 0, limit: int = 100, status: Optional[TicketStatus] = None, current_user: User = Depends(get_current_active_user), ) -> Any: """ Retrieve all tickets for the current user. """ query = db.query(Ticket).filter( Ticket.user_id == current_user.id, Ticket.is_active == True ) if status: query = query.filter(Ticket.status == status) tickets = query.offset(skip).limit(limit).all() return tickets @router.get("/active", response_model=List[TicketSchema]) def list_active_tickets( *, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user), ) -> Any: """ Retrieve all active tickets for the current user. """ tickets = db.query(Ticket).filter( Ticket.user_id == current_user.id, Ticket.status == TicketStatus.ACTIVE, Ticket.is_active == True ).all() return tickets @router.get("/history", response_model=List[TicketSchema]) def list_ticket_history( *, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user), ) -> Any: """ Retrieve ticket history (used, cancelled, expired) for the current user. """ tickets = db.query(Ticket).filter( Ticket.user_id == current_user.id, Ticket.status != TicketStatus.ACTIVE, Ticket.is_active == True ).all() return tickets @router.get("/{ticket_id}", response_model=TicketSchema) def get_ticket( *, db: Session = Depends(get_db), ticket_id: int, current_user: User = Depends(get_current_active_user), ) -> Any: """ Get ticket by ID. """ ticket = db.query(Ticket).filter( Ticket.id == ticket_id, Ticket.user_id == current_user.id, Ticket.is_active == True ).first() if not ticket: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Ticket not found", ) return ticket @router.get("/by-ticket-number/{ticket_number}", response_model=TicketSchema) def get_ticket_by_number( *, db: Session = Depends(get_db), ticket_number: str, current_user: User = Depends(get_current_active_user), ) -> Any: """ Get ticket by ticket number. """ ticket = db.query(Ticket).filter( Ticket.ticket_number == ticket_number, Ticket.user_id == current_user.id, Ticket.is_active == True ).first() if not ticket: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Ticket not found", ) return ticket @router.put("/{ticket_id}/cancel", response_model=TicketSchema) def cancel_ticket( *, db: Session = Depends(get_db), ticket_id: int, current_user: User = Depends(get_current_active_user), ) -> Any: """ Cancel a ticket. """ ticket = db.query(Ticket).filter( Ticket.id == ticket_id, Ticket.user_id == current_user.id, Ticket.is_active == True, Ticket.status == TicketStatus.ACTIVE ).first() if not ticket: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Active ticket not found", ) # Validate cancellation time (at least 3 minutes before departure) validate_cancellation_time(ticket) # Update ticket status ticket.status = TicketStatus.CANCELLED # Increment available seats in schedule schedule = db.query(Schedule).filter(Schedule.id == ticket.schedule_id).first() schedule.available_seats += 1 db.add(ticket) db.add(schedule) db.commit() db.refresh(ticket) return ticket