from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from datetime import datetime
from . import repository
from .schema import (
    BookingCreate,
    BookingUpdate,
    BookingResponse,
    BookingseatCreate,
    BookingseatUpdate,
    BookingseatResponse,
    TicketCreate,
    TicketUpdate,
    TicketResponse,
    BookingDetailResponse,
)
from reservation_management.models import Reservation
from user_management.models import User
from schedule_management.models import Schedule


def _get_or_raise(db: Session, entity_id: str, repo_module, entity_name: str):
    entity = repo_module.get_by_id(db, entity_id) if hasattr(repo_module, "get_by_id") else repo_module.get_booking_by_id(db, entity_id) if entity_name == "Booking" else repo_module.get_bookingseat_by_id(db, entity_id) if entity_name == "Bookingseat" else repo_module.get_ticket_by_id(db, entity_id)
    if not entity:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"{entity_name} not found")
    return entity


def create_booking(db: Session, data: BookingCreate) -> BookingResponse:
    reservation = db.query(Reservation).filter(Reservation.id == data.reservation_id).first()
    if not reservation:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reservation not found")
    
    user = db.query(User).filter(User.id == data.user_id).first()
    if not user:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    
    schedule = db.query(Schedule).filter(Schedule.id == data.schedule_id).first()
    if not schedule:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Schedule not found")
    
    existing_booking = repository.get_booking_by_reference(db, data.booking_reference)
    if existing_booking:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Booking reference already exists")
    
    booking_data = data.model_dump()
    booking = repository.create_booking(db, booking_data)
    db.commit()
    db.refresh(booking)
    return BookingResponse.model_validate(booking)


def get_booking(db: Session, booking_id: str) -> BookingResponse:
    booking = repository.get_booking_by_id(db, booking_id)
    if not booking:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    return BookingResponse.model_validate(booking)


def list_bookings(
    db: Session,
    limit: int,
    offset: int,
    user_id: Optional[str] = None,
    schedule_id: Optional[str] = None,
    status: Optional[str] = None,
) -> List[BookingResponse]:
    bookings = repository.list_bookings(db, limit, offset, user_id, schedule_id, status)
    return [BookingResponse.model_validate(b) for b in bookings]


def update_booking(db: Session, booking_id: str, data: BookingUpdate) -> BookingResponse:
    booking = repository.get_booking_by_id(db, booking_id)
    if not booking:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "reservation_id" in update_data:
        reservation = db.query(Reservation).filter(Reservation.id == update_data["reservation_id"]).first()
        if not reservation:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Reservation not found")
    
    if "user_id" in update_data:
        user = db.query(User).filter(User.id == update_data["user_id"]).first()
        if not user:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
    
    if "schedule_id" in update_data:
        schedule = db.query(Schedule).filter(Schedule.id == update_data["schedule_id"]).first()
        if not schedule:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Schedule not found")
    
    if "booking_reference" in update_data:
        existing_booking = repository.get_booking_by_reference(db, update_data["booking_reference"])
        if existing_booking and existing_booking.id != booking_id:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Booking reference already exists")
    
    updated_booking = repository.update_booking(db, booking_id, update_data)
    db.commit()
    db.refresh(updated_booking)
    return BookingResponse.model_validate(updated_booking)


def delete_booking(db: Session, booking_id: str) -> dict:
    success = repository.delete_booking(db, booking_id)
    if not success:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    db.commit()
    return {"message": "Booking deleted successfully"}


def get_booking_details(db: Session, booking_id: str) -> BookingDetailResponse:
    booking = repository.get_booking_with_details(db, booking_id)
    if not booking:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    return BookingDetailResponse.model_validate(booking)


def create_bookingseat(db: Session, data: BookingseatCreate) -> BookingseatResponse:
    booking = repository.get_booking_by_id(db, data.booking_id)
    if not booking:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    
    from schedule_management.models import Seat
    seat = db.query(Seat).filter(Seat.id == data.seat_id).first()
    if not seat:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Seat not found")
    
    bookingseat_data = data.model_dump()
    bookingseat = repository.create_bookingseat(db, bookingseat_data)
    db.commit()
    db.refresh(bookingseat)
    return BookingseatResponse.model_validate(bookingseat)


def get_bookingseat(db: Session, bookingseat_id: str) -> BookingseatResponse:
    bookingseat = repository.get_bookingseat_by_id(db, bookingseat_id)
    if not bookingseat:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking seat not found")
    return BookingseatResponse.model_validate(bookingseat)


def list_bookingseats(
    db: Session,
    limit: int,
    offset: int,
    booking_id: Optional[str] = None,
) -> List[BookingseatResponse]:
    bookingseats = repository.list_bookingseats(db, limit, offset, booking_id)
    return [BookingseatResponse.model_validate(bs) for bs in bookingseats]


def update_bookingseat(db: Session, bookingseat_id: str, data: BookingseatUpdate) -> BookingseatResponse:
    bookingseat = repository.get_bookingseat_by_id(db, bookingseat_id)
    if not bookingseat:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking seat not found")
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "booking_id" in update_data:
        booking = repository.get_booking_by_id(db, update_data["booking_id"])
        if not booking:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    
    if "seat_id" in update_data:
        from schedule_management.models import Seat
        seat = db.query(Seat).filter(Seat.id == update_data["seat_id"]).first()
        if not seat:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Seat not found")
    
    updated_bookingseat = repository.update_bookingseat(db, bookingseat_id, update_data)
    db.commit()
    db.refresh(updated_bookingseat)
    return BookingseatResponse.model_validate(updated_bookingseat)


def delete_bookingseat(db: Session, bookingseat_id: str) -> dict:
    success = repository.delete_bookingseat(db, bookingseat_id)
    if not success:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking seat not found")
    db.commit()
    return {"message": "Booking seat deleted successfully"}


def create_ticket(db: Session, data: TicketCreate) -> TicketResponse:
    booking = repository.get_booking_by_id(db, data.booking_id)
    if not booking:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
    
    existing_ticket = repository.get_ticket_by_booking_id(db, data.booking_id)
    if existing_ticket:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Ticket already exists for this booking")
    
    existing_ticket_number = repository.get_ticket_by_number(db, data.ticket_number)
    if existing_ticket_number:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Ticket number already exists")
    
    ticket_data = data.model_dump()
    ticket = repository.create_ticket(db, ticket_data)
    db.commit()
    db.refresh(ticket)
    return TicketResponse.model_validate(ticket)


def get_ticket(db: Session, ticket_id: str) -> TicketResponse:
    ticket = repository.get_ticket_by_id(db, ticket_id)
    if not ticket:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Ticket not found")
    return TicketResponse.model_validate(ticket)


def list_tickets(
    db: Session,
    limit: int,
    offset: int,
    validity_status: Optional[str] = None,
) -> List[TicketResponse]:
    tickets = repository.list_tickets(db, limit, offset, validity_status)
    return [TicketResponse.model_validate(t) for t in tickets]


def update_ticket(db: Session, ticket_id: str, data: TicketUpdate) -> TicketResponse:
    ticket = repository.get_ticket_by_id(db, ticket_id)
    if not ticket:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Ticket not found")
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "booking_id" in update_data:
        booking = repository.get_booking_by_id(db, update_data["booking_id"])
        if not booking:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Booking not found")
        
        existing_ticket = repository.get_ticket_by_booking_id(db, update_data["booking_id"])
        if existing_ticket and existing_ticket.id != ticket_id:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Ticket already exists for this booking")
    
    if "ticket_number" in update_data:
        existing_ticket_number = repository.get_ticket_by_number(db, update_data["ticket_number"])
        if existing_ticket_number and existing_ticket_number.id != ticket_id:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Ticket number already exists")
    
    updated_ticket = repository.update_ticket(db, ticket_id, update_data)
    db.commit()
    db.refresh(updated_ticket)
    return TicketResponse.model_validate(updated_ticket)


def delete_ticket(db: Session, ticket_id: str) -> dict:
    success = repository.delete_ticket(db, ticket_id)
    if not success:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Ticket not found")
    db.commit()
    return {"message": "Ticket deleted successfully"}