from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import List, Optional
from . import repository
from .models import Airport, Route
from .schema import (
    AirportCreate,
    AirportUpdate,
    RouteCreate,
    RouteUpdate,
    RouteDetailResponse
)


def create_airport(db: Session, data: AirportCreate) -> Airport:
    existing = repository.get_airport_by_code(db, data.code)
    if existing:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Airport with code {data.code} already exists"
        )
    
    try:
        airport = repository.create(db, data.model_dump(), Airport)
        db.commit()
        db.refresh(airport)
        return airport
    except Exception:
        db.rollback()
        raise


def list_airports(
    db: Session,
    limit: int,
    offset: int,
    search: Optional[str] = None
) -> List[Airport]:
    return repository.list_all(db, Airport, limit=limit, offset=offset, search=search)


def get_airport(db: Session, airport_id: str) -> Airport:
    airport = repository.get_by_id(db, airport_id, Airport)
    if not airport:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Airport with id {airport_id} not found"
        )
    return airport


def update_airport(db: Session, airport_id: str, data: AirportUpdate) -> Airport:
    update_data = data.model_dump(exclude_unset=True)
    
    if "code" in update_data:
        existing = repository.get_airport_by_code(db, update_data["code"])
        if existing and existing.id != airport_id:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f"Airport with code {update_data['code']} already exists"
            )
    
    try:
        airport = repository.update(db, airport_id, update_data, Airport)
        if not airport:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Airport with id {airport_id} not found"
            )
        db.commit()
        db.refresh(airport)
        return airport
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def delete_airport(db: Session, airport_id: str) -> dict:
    airport = repository.get_by_id(db, airport_id, Airport)
    if not airport:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Airport with id {airport_id} not found"
        )
    
    routes_as_origin = db.query(Route).filter(Route.origin_airport_id == airport_id).first()
    routes_as_destination = db.query(Route).filter(Route.destination_airport_id == airport_id).first()
    
    if routes_as_origin or routes_as_destination:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Cannot delete airport that is referenced by routes"
        )
    
    try:
        repository.delete(db, airport_id, Airport)
        db.commit()
        return {"message": "Airport deleted successfully"}
    except Exception:
        db.rollback()
        raise


def create_route(db: Session, data: RouteCreate) -> Route:
    origin = repository.get_by_id(db, data.origin_airport_id, Airport)
    if not origin:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Origin airport with id {data.origin_airport_id} not found"
        )
    
    destination = repository.get_by_id(db, data.destination_airport_id, Airport)
    if not destination:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Destination airport with id {data.destination_airport_id} not found"
        )
    
    if data.origin_airport_id == data.destination_airport_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Route origin and destination airports must be different"
        )
    
    existing_routes = repository.get_routes_by_airports(
        db,
        data.origin_airport_id,
        data.destination_airport_id
    )
    if existing_routes:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Route between these airports already exists"
        )
    
    try:
        route = repository.create(db, data.model_dump(), Route)
        db.commit()
        db.refresh(route)
        return route
    except Exception:
        db.rollback()
        raise


def list_routes(
    db: Session,
    limit: int,
    offset: int,
    status_filter: Optional[str] = None,
    origin_airport_id: Optional[str] = None,
    destination_airport_id: Optional[str] = None
) -> List[Route]:
    return repository.list_all(
        db,
        Route,
        limit=limit,
        offset=offset,
        status=status_filter,
        origin_airport_id=origin_airport_id,
        destination_airport_id=destination_airport_id
    )


def get_route(db: Session, route_id: str) -> Route:
    route = repository.get_by_id(db, route_id, Route)
    if not route:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Route with id {route_id} not found"
        )
    return route


def get_route_details(db: Session, route_id: str) -> Route:
    route = repository.get_route_with_details(db, route_id)
    if not route:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Route with id {route_id} not found"
        )
    return route


def update_route(db: Session, route_id: str, data: RouteUpdate) -> Route:
    update_data = data.model_dump(exclude_unset=True)
    
    if "origin_airport_id" in update_data:
        origin = repository.get_by_id(db, update_data["origin_airport_id"], Airport)
        if not origin:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Origin airport with id {update_data['origin_airport_id']} not found"
            )
    
    if "destination_airport_id" in update_data:
        destination = repository.get_by_id(db, update_data["destination_airport_id"], Airport)
        if not destination:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Destination airport with id {update_data['destination_airport_id']} not found"
            )
    
    route = repository.get_by_id(db, route_id, Route)
    if not route:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Route with id {route_id} not found"
        )
    
    new_origin = update_data.get("origin_airport_id", route.origin_airport_id)
    new_destination = update_data.get("destination_airport_id", route.destination_airport_id)
    
    if new_origin == new_destination:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Route origin and destination airports must be different"
        )
    
    if "origin_airport_id" in update_data or "destination_airport_id" in update_data:
        existing_routes = repository.get_routes_by_airports(db, new_origin, new_destination)
        if existing_routes and any(r.id != route_id for r in existing_routes):
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="Route between these airports already exists"
            )
    
    try:
        route = repository.update(db, route_id, update_data, Route)
        db.commit()
        db.refresh(route)
        return route
    except HTTPException:
        db.rollback()
        raise
    except Exception:
        db.rollback()
        raise


def delete_route(db: Session, route_id: str) -> dict:
    route = repository.get_by_id(db, route_id, Route)
    if not route:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Route with id {route_id} not found"
        )
    
    from flight_management.models import Flight
    flights = db.query(Flight).filter(Flight.route_id == route_id).first()
    if flights:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Cannot delete route that has associated flights"
        )
    
    try:
        repository.delete(db, route_id, Route)
        db.commit()
        return {"message": "Route deleted successfully"}
    except Exception:
        db.rollback()
        raise