from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from . import repository
from .models import Hotel, Hotelamenity, Roomtype, Transportation
from .schema import (
    HotelCreate,
    HotelUpdate,
    HotelamenityCreate,
    HotelamenityUpdate,
    RoomtypeCreate,
    RoomtypeUpdate,
    TransportationCreate,
    TransportationUpdate,
)
from user_management import repository as user_repo
from tour_catalog import repository as catalog_repo


def _get_or_raise(db: Session, entity_id: str, model_class, entity_name: str):
    obj = repository.get_by_id(db, entity_id, model_class)
    if not obj:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"{entity_name} with id {entity_id} not found",
        )
    return obj


def create_hotel(db: Session, data: HotelCreate) -> Hotel:
    from user_management.models import Serviceprovider

    service_provider = repository.get_by_id(db, data.service_provider_id, Serviceprovider)
    if not service_provider:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Service provider with id {data.service_provider_id} not found",
        )

    try:
        hotel = repository.create(db, Hotel, data.model_dump())
        db.commit()
        db.refresh(hotel)
        return hotel
    except Exception:
        db.rollback()
        raise


def get_hotel(db: Session, hotel_id: str) -> Hotel:
    return _get_or_raise(db, hotel_id, Hotel, "Hotel")


def list_hotels(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    service_provider_id: Optional[str] = None,
    is_active: Optional[bool] = None,
) -> List[Hotel]:
    filters = {}
    if service_provider_id is not None:
        filters["service_provider_id"] = service_provider_id
    if is_active is not None:
        filters["is_active"] = is_active
    return repository.list_all(db, Hotel, limit, offset, **filters)


def update_hotel(db: Session, hotel_id: str, data: HotelUpdate) -> Hotel:
    update_data = data.model_dump(exclude_unset=True)

    if "service_provider_id" in update_data:
        from user_management.models import Serviceprovider

        service_provider = repository.get_by_id(db, update_data["service_provider_id"], Serviceprovider)
        if not service_provider:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Service provider with id {update_data['service_provider_id']} not found",
            )

    try:
        hotel = repository.update(db, hotel_id, Hotel, update_data)
        if not hotel:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Hotel with id {hotel_id} not found",
            )
        db.commit()
        db.refresh(hotel)
        return hotel
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise


def delete_hotel(db: Session, hotel_id: str) -> bool:
    try:
        result = repository.delete(db, hotel_id, Hotel)
        if not result:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Hotel with id {hotel_id} not found",
            )
        db.commit()
        return True
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise


def get_hotel_with_details(db: Session, hotel_id: str):
    hotel = repository.get_hotel_with_details(db, hotel_id)
    if not hotel:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Hotel with id {hotel_id} not found",
        )
    return hotel


def create_hotel_amenity(db: Session, data: HotelamenityCreate) -> Hotelamenity:
    hotel = repository.get_by_id(db, data.hotel_id, Hotel)
    if not hotel:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Hotel with id {data.hotel_id} not found",
        )

    from tour_catalog.models import Amenity

    amenity = repository.get_by_id(db, data.amenity_id, Amenity)
    if not amenity:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Amenity with id {data.amenity_id} not found",
        )

    existing = (
        db.query(Hotelamenity)
        .filter(
            Hotelamenity.hotel_id == data.hotel_id,
            Hotelamenity.amenity_id == data.amenity_id,
        )
        .first()
    )
    if existing:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="This amenity is already associated with the hotel",
        )

    try:
        hotel_amenity = repository.create(db, Hotelamenity, data.model_dump())
        db.commit()
        db.refresh(hotel_amenity)
        return hotel_amenity
    except Exception:
        db.rollback()
        raise


def get_hotel_amenity(db: Session, hotel_amenity_id: str) -> Hotelamenity:
    return _get_or_raise(db, hotel_amenity_id, Hotelamenity, "Hotel amenity")


def list_hotel_amenities(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    hotel_id: Optional[str] = None,
) -> List[Hotelamenity]:
    filters = {}
    if hotel_id is not None:
        filters["hotel_id"] = hotel_id
    return repository.list_all(db, Hotelamenity, limit, offset, **filters)


def delete_hotel_amenity(db: Session, hotel_amenity_id: str) -> bool:
    try:
        result = repository.delete(db, hotel_amenity_id, Hotelamenity)
        if not result:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Hotel amenity with id {hotel_amenity_id} not found",
            )
        db.commit()
        return True
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise


def create_room_type(db: Session, data: RoomtypeCreate) -> Roomtype:
    hotel = repository.get_by_id(db, data.hotel_id, Hotel)
    if not hotel:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Hotel with id {data.hotel_id} not found",
        )

    try:
        room_type = repository.create(db, Roomtype, data.model_dump())
        db.commit()
        db.refresh(room_type)
        return room_type
    except Exception:
        db.rollback()
        raise


def get_room_type(db: Session, room_type_id: str) -> Roomtype:
    return _get_or_raise(db, room_type_id, Roomtype, "Room type")


def list_room_types(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    hotel_id: Optional[str] = None,
) -> List[Roomtype]:
    filters = {}
    if hotel_id is not None:
        filters["hotel_id"] = hotel_id
    return repository.list_all(db, Roomtype, limit, offset, **filters)


def update_room_type(db: Session, room_type_id: str, data: RoomtypeUpdate) -> Roomtype:
    update_data = data.model_dump(exclude_unset=True)

    if "hotel_id" in update_data:
        hotel = repository.get_by_id(db, update_data["hotel_id"], Hotel)
        if not hotel:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Hotel with id {update_data['hotel_id']} not found",
            )

    try:
        room_type = repository.update(db, room_type_id, Roomtype, update_data)
        if not room_type:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Room type with id {room_type_id} not found",
            )
        db.commit()
        db.refresh(room_type)
        return room_type
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise


def delete_room_type(db: Session, room_type_id: str) -> bool:
    try:
        result = repository.delete(db, room_type_id, Roomtype)
        if not result:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Room type with id {room_type_id} not found",
            )
        db.commit()
        return True
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise


def create_transportation(db: Session, data: TransportationCreate) -> Transportation:
    from user_management.models import Serviceprovider

    service_provider = repository.get_by_id(db, data.service_provider_id, Serviceprovider)
    if not service_provider:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Service provider with id {data.service_provider_id} not found",
        )

    try:
        transportation = repository.create(db, Transportation, data.model_dump())
        db.commit()
        db.refresh(transportation)
        return transportation
    except Exception:
        db.rollback()
        raise


def get_transportation(db: Session, transportation_id: str) -> Transportation:
    return _get_or_raise(db, transportation_id, Transportation, "Transportation")


def list_transportations(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    service_provider_id: Optional[str] = None,
    transport_type: Optional[str] = None,
    is_active: Optional[bool] = None,
) -> List[Transportation]:
    filters = {}
    if service_provider_id is not None:
        filters["service_provider_id"] = service_provider_id
    if transport_type is not None:
        filters["transport_type"] = transport_type
    if is_active is not None:
        filters["is_active"] = is_active
    return repository.list_all(db, Transportation, limit, offset, **filters)


def update_transportation(db: Session, transportation_id: str, data: TransportationUpdate) -> Transportation:
    update_data = data.model_dump(exclude_unset=True)

    if "service_provider_id" in update_data:
        from user_management.models import Serviceprovider

        service_provider = repository.get_by_id(db, update_data["service_provider_id"], Serviceprovider)
        if not service_provider:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Service provider with id {update_data['service_provider_id']} not found",
            )

    try:
        transportation = repository.update(db, transportation_id, Transportation, update_data)
        if not transportation:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Transportation with id {transportation_id} not found",
            )
        db.commit()
        db.refresh(transportation)
        return transportation
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise


def delete_transportation(db: Session, transportation_id: str) -> bool:
    try:
        result = repository.delete(db, transportation_id, Transportation)
        if not result:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Transportation with id {transportation_id} not found",
            )
        db.commit()
        return True
    except HTTPException:
        raise
    except Exception:
        db.rollback()
        raise