from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import List, Optional
from datetime import datetime
from . import repository
from .schema import CancellationCreate, CancellationUpdate, CancellationDetailsResponse
from booking_management import repository as booking_repo
from user_management import repository as user_repo


def _get_or_raise(db: Session, cancellation_id: str) -> "Cancellation":
    cancellation = repository.get_by_id(db, cancellation_id)
    if not cancellation:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Cancellation with id {cancellation_id} not found"
        )
    return cancellation


def create_cancellation(db: Session, data: CancellationCreate):
    booking = booking_repo.get_by_id(db, data.booking_id)
    if not booking:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Booking with id {data.booking_id} not found"
        )
    
    user = user_repo.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"User with id {data.user_id} not found"
        )
    
    if booking.status == "cancelled":
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Booking is already cancelled"
        )
    
    if data.refund_amount > booking.total_amount:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Refund amount cannot exceed booking total amount"
        )
    
    cancellation_data = data.model_dump()
    cancellation = repository.create(db, cancellation_data)
    
    db.commit()
    db.refresh(cancellation)
    return cancellation


def get_cancellation(db: Session, cancellation_id: str):
    return _get_or_raise(db, cancellation_id)


def list_cancellations(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    user_id: Optional[str] = None,
    booking_id: Optional[str] = None,
    refund_status: Optional[str] = None
):
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    if booking_id:
        filters["booking_id"] = booking_id
    if refund_status:
        filters["refund_status"] = refund_status
    
    return repository.list_all(db, limit, offset, **filters)


def update_cancellation(db: Session, cancellation_id: str, data: CancellationUpdate):
    update_data = data.model_dump(exclude_unset=True)
    
    if not update_data:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No fields to update"
        )
    
    if "booking_id" in update_data:
        booking = booking_repo.get_by_id(db, update_data["booking_id"])
        if not booking:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Booking with id {update_data['booking_id']} not found"
            )
    
    if "user_id" in update_data:
        user = user_repo.get_by_id(db, update_data["user_id"])
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"User with id {update_data['user_id']} not found"
            )
    
    cancellation = repository.update(db, cancellation_id, update_data)
    if not cancellation:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Cancellation with id {cancellation_id} not found"
        )
    
    db.commit()
    db.refresh(cancellation)
    return cancellation


def delete_cancellation(db: Session, cancellation_id: str):
    success = repository.delete(db, cancellation_id)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Cancellation with id {cancellation_id} not found"
        )
    
    db.commit()
    return {"message": "Cancellation deleted successfully"}


def get_cancellation_details(db: Session, cancellation_id: str) -> CancellationDetailsResponse:
    cancellation = repository.get_with_details(db, cancellation_id)
    if not cancellation:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Cancellation with id {cancellation_id} not found"
        )
    
    response_data = {
        "id": cancellation.id,
        "booking_id": cancellation.booking_id,
        "user_id": cancellation.user_id,
        "cancellation_datetime": cancellation.cancellation_datetime,
        "reason": cancellation.reason,
        "refund_amount": cancellation.refund_amount,
        "refund_status": cancellation.refund_status,
        "processed_at": cancellation.processed_at,
        "created_at": cancellation.created_at,
        "updated_at": cancellation.updated_at,
        "booking_reference": cancellation.booking.booking_reference,
        "schedule_departure_datetime": cancellation.booking.schedule.departure_datetime,
        "bus_number": cancellation.booking.schedule.bus.bus_number,
        "origin_city": cancellation.booking.schedule.route.origin_city,
        "destination_city": cancellation.booking.schedule.route.destination_city,
        "user_email": cancellation.user.email,
        "user_first_name": cancellation.user.first_name,
        "user_last_name": cancellation.user.last_name,
    }
    
    return CancellationDetailsResponse(**response_data)