from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
from sqlalchemy.orm import Session

from app.api.deps import AuthSession, require_client_module_access
from app.db.session import get_db
from app.schemas.mix_calculator import (
    MixCalculatorOptionsRead,
    MixCalculatorPreviewRead,
    MixCalculatorSessionCreate,
    MixCalculatorSessionRead,
    MixCalculatorSessionSummaryRead,
    MixCalculatorSessionUpdate,
)
from app.services.mix_calculator_service import (
    build_mix_calculator_options,
    can_view_all_mix_calculator_sessions,
    calculate_mix_calculator_preview,
    create_mix_calculator_session,
    get_mix_calculator_session,
    list_mix_calculator_sessions,
    serialize_mix_calculator_session,
    update_mix_calculator_session,
)
from app.services.mix_calculator_pdf import MixCalculatorPdfUnavailableError, build_mix_calculator_pdf
from app.services.mix_calculator_filenames import mix_calculator_pdf_filename, mix_calculator_preview_pdf_filename

router = APIRouter(prefix="/api/mix-calculator", tags=["mix-calculator"])


@router.get("/options", response_model=MixCalculatorOptionsRead)
def mix_calculator_options(
    session: AuthSession = Depends(require_client_module_access("mix_calculator")),
    db: Session = Depends(get_db),
):
    return build_mix_calculator_options(db, tenant_id=session.tenant_id or "")


@router.get("", response_model=list[MixCalculatorSessionSummaryRead])
def mix_calculator_sessions(
    limit: int = Query(default=100, ge=1, le=200),
    session: AuthSession = Depends(require_client_module_access("mix_calculator")),
    db: Session = Depends(get_db),
):
    return list_mix_calculator_sessions(db, auth_session=session, limit=limit)


@router.post("/preview", response_model=MixCalculatorPreviewRead)
def preview_mix_calculator_session(
    payload: MixCalculatorSessionCreate,
    session: AuthSession = Depends(require_client_module_access("mix_calculator", "edit")),
    db: Session = Depends(get_db),
):
    try:
        return calculate_mix_calculator_preview(db, tenant_id=session.tenant_id or "", payload=payload)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc


@router.post("/preview/pdf")
def preview_mix_calculator_session_pdf(
    payload: MixCalculatorSessionCreate,
    session: AuthSession = Depends(require_client_module_access("mix_calculator", "edit")),
    db: Session = Depends(get_db),
):
    try:
        preview = calculate_mix_calculator_preview(db, tenant_id=session.tenant_id or "", payload=payload)
        pdf_bytes = build_mix_calculator_pdf(preview)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
    except MixCalculatorPdfUnavailableError as exc:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=str(exc)) from exc

    filename = mix_calculator_preview_pdf_filename(MixCalculatorPreviewRead.model_validate(preview))
    return Response(
        content=pdf_bytes,
        media_type="application/pdf",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )


@router.post("", response_model=MixCalculatorSessionRead, status_code=status.HTTP_201_CREATED)
def create_saved_mix_calculator_session(
    payload: MixCalculatorSessionCreate,
    session: AuthSession = Depends(require_client_module_access("mix_calculator", "edit")),
    db: Session = Depends(get_db),
):
    try:
        return create_mix_calculator_session(db, auth_session=session, payload=payload)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc


@router.get("/{session_id}", response_model=MixCalculatorSessionRead)
def read_mix_calculator_session(
    session_id: int,
    session: AuthSession = Depends(require_client_module_access("mix_calculator")),
    db: Session = Depends(get_db),
):
    session_record = get_mix_calculator_session(db, auth_session=session, session_id=session_id)
    if session_record is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Mix calculator session not found")
    return serialize_mix_calculator_session(session_record, session)


@router.get("/{session_id}/pdf")
def download_mix_calculator_session_pdf(
    session_id: int,
    session: AuthSession = Depends(require_client_module_access("mix_calculator")),
    db: Session = Depends(get_db),
):
    session_record = get_mix_calculator_session(db, auth_session=session, session_id=session_id)
    if session_record is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Mix calculator session not found")

    try:
        pdf_bytes = build_mix_calculator_pdf(session_record)
    except MixCalculatorPdfUnavailableError as exc:
        raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=str(exc)) from exc
    filename = mix_calculator_pdf_filename(session_record)
    return Response(
        content=pdf_bytes,
        media_type="application/pdf",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )


@router.patch("/{session_id}", response_model=MixCalculatorSessionRead)
def patch_mix_calculator_session(
    session_id: int,
    payload: MixCalculatorSessionUpdate,
    session: AuthSession = Depends(require_client_module_access("mix_calculator", "edit")),
    db: Session = Depends(get_db),
):
    session_record = get_mix_calculator_session(db, auth_session=session, session_id=session_id)
    if session_record is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Mix calculator session not found")
    if not can_view_all_mix_calculator_sessions(session) and session_record.prepared_by_user_id != session.user_id:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="You can only edit your own mix calculator sessions")
    try:
        return update_mix_calculator_session(db, auth_session=session, session_record=session_record, payload=payload)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
