from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from . import repository
from .schema import DocumentCreate, DocumentUpdate, DocumentResponse, DocumentDetailResponse
from user_management import repository as user_repo
from booking_management import repository as booking_repo
from user_management.models import Customer

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

def create_document(db: Session, data: DocumentCreate) -> DocumentResponse:
    # Validate uploader exists
    uploader = user_repo.get_by_id(db, data.uploaded_by)
    if not uploader:
        raise HTTPException(status_code=400, detail="Uploader user not found")
    
    # Validate booking if provided
    if data.booking_id:
        booking = booking_repo.get_by_id(db, data.booking_id)
        if not booking:
            raise HTTPException(status_code=400, detail="Booking not found")
    
    # Validate customer if provided
    if data.customer_id:
        customer = db.query(Customer).filter(Customer.id == data.customer_id).first()
        if not customer:
            raise HTTPException(status_code=400, detail="Customer not found")
    
    # At least one of booking_id or customer_id must be provided
    if not data.booking_id and not data.customer_id:
        raise HTTPException(
            status_code=400,
            detail="Either booking_id or customer_id must be provided"
        )
    
    try:
        document = repository.create(db, data.model_dump())
        db.commit()
        db.refresh(document)
        return DocumentResponse.model_validate(document)
    except Exception:
        db.rollback()
        raise

def get_document(db: Session, document_id: str) -> DocumentResponse:
    document = _get_or_raise(db, document_id, repository)
    return DocumentResponse.model_validate(document)

def get_document_details(db: Session, document_id: str) -> DocumentDetailResponse:
    document = repository.get_with_details(db, document_id)
    if not document:
        raise HTTPException(status_code=404, detail="Document not found")
    
    response_data = DocumentDetailResponse.model_validate(document)
    
    # Add computed fields
    if document.uploader:
        response_data.uploader_name = f"{document.uploader.first_name} {document.uploader.last_name}"
    
    if document.booking:
        response_data.booking_number = document.booking.booking_number
    
    if document.customer and document.customer.user:
        response_data.customer_name = f"{document.customer.user.first_name} {document.customer.user.last_name}"
    
    return response_data

def list_documents(
    db: Session,
    limit: int,
    offset: int,
    booking_id: Optional[str] = None,
    customer_id: Optional[str] = None,
    document_type: Optional[str] = None,
    uploaded_by: Optional[str] = None
) -> List[DocumentResponse]:
    documents = repository.list_all(
        db,
        limit=limit,
        offset=offset,
        booking_id=booking_id,
        customer_id=customer_id,
        document_type=document_type,
        uploaded_by=uploaded_by
    )
    return [DocumentResponse.model_validate(doc) for doc in documents]

def update_document(db: Session, document_id: str, data: DocumentUpdate) -> DocumentResponse:
    document = _get_or_raise(db, document_id, repository)
    
    update_data = data.model_dump(exclude_unset=True)
    
    # Validate booking if being updated
    if "booking_id" in update_data and update_data["booking_id"]:
        booking = booking_repo.get_by_id(db, update_data["booking_id"])
        if not booking:
            raise HTTPException(status_code=400, detail="Booking not found")
    
    # Validate customer if being updated
    if "customer_id" in update_data and update_data["customer_id"]:
        customer = db.query(Customer).filter(Customer.id == update_data["customer_id"]).first()
        if not customer:
            raise HTTPException(status_code=400, detail="Customer not found")
    
    try:
        updated_document = repository.update(db, document_id, update_data)
        db.commit()
        db.refresh(updated_document)
        return DocumentResponse.model_validate(updated_document)
    except Exception:
        db.rollback()
        raise

def delete_document(db: Session, document_id: str) -> dict:
    document = _get_or_raise(db, document_id, repository)
    
    try:
        repository.delete(db, document_id)
        db.commit()
        return {"message": "Document deleted successfully"}
    except Exception:
        db.rollback()
        raise

def get_booking_documents(db: Session, booking_id: str) -> List[DocumentResponse]:
    # Validate booking exists
    booking = booking_repo.get_by_id(db, booking_id)
    if not booking:
        raise HTTPException(status_code=404, detail="Booking not found")
    
    documents = repository.list_all(db, limit=100, offset=0, booking_id=booking_id)
    return [DocumentResponse.model_validate(doc) for doc in documents]

def get_customer_documents(db: Session, customer_id: str) -> List[DocumentResponse]:
    # Validate customer exists
    customer = db.query(Customer).filter(Customer.id == customer_id).first()
    if not customer:
        raise HTTPException(status_code=404, detail="Customer not found")
    
    documents = repository.list_all(db, limit=100, offset=0, customer_id=customer_id)
    return [DocumentResponse.model_validate(doc) for doc in documents]