from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import List, Optional
from datetime import datetime, timedelta
from . import repository
from .schema import (
    AircraftCreate, AircraftUpdate, AircraftResponse, AircraftDetailResponse,
    SeatCreate, SeatUpdate, SeatResponse
)
from flight_management.models import Flight


def _get_or_raise(db: Session, entity_id: str, repo_module) -> any:
    entity = repo_module.get_by_id(db, entity_id)
    if not entity:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Entity with id {entity_id} not found")
    return entity


def create_aircraft(db: Session, data: AircraftCreate) -> AircraftResponse:
    existing = repository.get_by_registration_number(db, data.registration_number)
    if existing:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Aircraft with registration number {data.registration_number} already exists"
        )
    
    try:
        aircraft = repository.create(db, data.model_dump())
        db.commit()
        db.refresh(aircraft)
        return AircraftResponse.model_validate(aircraft)
    except Exception:
        db.rollback()
        raise


def list_aircraft(
    db: Session,
    limit: int,
    offset: int,
    status: Optional[str] = None,
    search: Optional[str] = None
) -> List[AircraftResponse]:
    filters = {}
    if status:
        filters["status"] = status
    if search:
        filters["search"] = search
    
    aircraft_list = repository.list_all(db, limit, offset, **filters)
    return [AircraftResponse.model_validate(a) for a in aircraft_list]


def get_aircraft(db: Session, aircraft_id: str) -> AircraftResponse:
    aircraft = _get_or_raise(db, aircraft_id, repository)
    return AircraftResponse.model_validate(aircraft)


def update_aircraft(db: Session, aircraft_id: str, data: AircraftUpdate) -> AircraftResponse:
    aircraft = _get_or_raise(db, aircraft_id, repository)
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "registration_number" in update_data:
        existing = repository.get_by_registration_number(db, update_data["registration_number"])
        if existing and existing.id != aircraft_id:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f"Aircraft with registration number {update_data['registration_number']} already exists"
            )
    
    try:
        updated_aircraft = repository.update(db, aircraft_id, update_data)
        db.commit()
        db.refresh(updated_aircraft)
        return AircraftResponse.model_validate(updated_aircraft)
    except Exception:
        db.rollback()
        raise


def delete_aircraft(db: Session, aircraft_id: str) -> dict:
    aircraft = _get_or_raise(db, aircraft_id, repository)
    
    future_flights = db.query(Flight).filter(
        Flight.aircraft_id == aircraft_id,
        Flight.scheduled_departure > datetime.utcnow()
    ).first()
    
    if future_flights:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Aircraft cannot be deleted because it has future flight assignments"
        )
    
    try:
        repository.delete(db, aircraft_id)
        db.commit()
        return {"message": "Aircraft deleted successfully"}
    except Exception:
        db.rollback()
        raise


def get_aircraft_details(db: Session, aircraft_id: str) -> AircraftDetailResponse:
    aircraft = repository.get_with_details(db, aircraft_id)
    if not aircraft:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Aircraft with id {aircraft_id} not found")
    
    return AircraftDetailResponse.model_validate(aircraft)


def check_aircraft_availability(db: Session, aircraft_id: str, start_date: datetime, end_date: datetime) -> dict:
    aircraft = _get_or_raise(db, aircraft_id, repository)
    
    if aircraft.status != "Active":
        return {
            "available": False,
            "reason": f"Aircraft is currently in {aircraft.status} status",
            "conflicting_flights": []
        }
    
    conflicting_flights = db.query(Flight).filter(
        Flight.aircraft_id == aircraft_id,
        Flight.scheduled_departure <= end_date,
        Flight.scheduled_arrival >= start_date,
        Flight.status.notin_(["Cancelled", "Arrived"])
    ).all()
    
    if conflicting_flights:
        return {
            "available": False,
            "reason": "Aircraft has conflicting flight assignments",
            "conflicting_flights": [
                {
                    "flight_number": f.flight_number,
                    "scheduled_departure": f.scheduled_departure.isoformat(),
                    "scheduled_arrival": f.scheduled_arrival.isoformat(),
                    "status": f.status
                }
                for f in conflicting_flights
            ]
        }
    
    return {
        "available": True,
        "reason": "Aircraft is available for the requested period",
        "conflicting_flights": []
    }


def create_seat(db: Session, data: SeatCreate) -> SeatResponse:
    aircraft = _get_or_raise(db, data.aircraft_id, repository)
    
    existing_seat = repository.get_seat_by_aircraft_and_number(db, data.aircraft_id, data.seat_number)
    if existing_seat:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Seat {data.seat_number} already exists on aircraft {aircraft.registration_number}"
        )
    
    try:
        seat = repository.create_seat(db, data.model_dump())
        db.commit()
        db.refresh(seat)
        return SeatResponse.model_validate(seat)
    except Exception:
        db.rollback()
        raise


def list_seats(
    db: Session,
    limit: int,
    offset: int,
    aircraft_id: Optional[str] = None,
    class_type: Optional[str] = None
) -> List[SeatResponse]:
    filters = {}
    if aircraft_id:
        filters["aircraft_id"] = aircraft_id
    if class_type:
        filters["class_type"] = class_type
    
    seats = repository.list_seats(db, limit, offset, **filters)
    return [SeatResponse.model_validate(s) for s in seats]


def get_seat(db: Session, seat_id: str) -> SeatResponse:
    seat = repository.get_seat_by_id(db, seat_id)
    if not seat:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Seat with id {seat_id} not found")
    return SeatResponse.model_validate(seat)


def update_seat(db: Session, seat_id: str, data: SeatUpdate) -> SeatResponse:
    seat = repository.get_seat_by_id(db, seat_id)
    if not seat:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Seat with id {seat_id} not found")
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "aircraft_id" in update_data:
        aircraft = _get_or_raise(db, update_data["aircraft_id"], repository)
    
    if "seat_number" in update_data or "aircraft_id" in update_data:
        check_aircraft_id = update_data.get("aircraft_id", seat.aircraft_id)
        check_seat_number = update_data.get("seat_number", seat.seat_number)
        
        existing_seat = repository.get_seat_by_aircraft_and_number(db, check_aircraft_id, check_seat_number)
        if existing_seat and existing_seat.id != seat_id:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f"Seat {check_seat_number} already exists on the specified aircraft"
            )
    
    try:
        updated_seat = repository.update_seat(db, seat_id, update_data)
        db.commit()
        db.refresh(updated_seat)
        return SeatResponse.model_validate(updated_seat)
    except Exception:
        db.rollback()
        raise


def delete_seat(db: Session, seat_id: str) -> dict:
    seat = repository.get_seat_by_id(db, seat_id)
    if not seat:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Seat with id {seat_id} not found")
    
    try:
        repository.delete_seat(db, seat_id)
        db.commit()
        return {"message": "Seat deleted successfully"}
    except Exception:
        db.rollback()
        raise