"""Router for expenses endpoints."""

from datetime import date

from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.dependencies import get_current_user, get_spbu_access
from app.models.user import User
from app.schemas.expenses import ExpenseCreate, ExpenseUpdate, KategoriCreate, ReviewRequest, UnlockRequest
from app.services import expense_service

router = APIRouter(prefix="/spbus/{spbu_id}/expenses", tags=["expenses"])


def _err(e: Exception) -> HTTPException:
    if isinstance(e, PermissionError):
        return HTTPException(status_code=403, detail=str(e))
    return HTTPException(status_code=400, detail=str(e))


# ── Kategori ──────────────────────────────────────────────────────────────────

@router.get("/kategori", response_model=dict)
async def list_kategori(
    spbu_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    rows = await expense_service.list_kategori(db, spbu_id)
    return {"data": [r.model_dump() for r in rows]}


@router.post("/kategori", response_model=dict, status_code=201)
async def create_kategori(
    spbu_id: int,
    body: KategoriCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        kat = await expense_service.create_kategori(db, spbu_id, body.nama, body.urutan)
    except Exception as e:
        raise _err(e)
    return {"data": kat.model_dump(), "message": "Kategori berhasil ditambahkan"}


# ── Expenses ──────────────────────────────────────────────────────────────────

@router.get("", response_model=dict)
async def list_expenses(
    spbu_id: int,
    tanggal: date | None = Query(default=None),
    tanggal_from: date | None = Query(default=None),
    tanggal_to: date | None = Query(default=None),
    laporan_shift_id: int | None = Query(default=None),
    kategori_id: int | None = Query(default=None),
    skip: int = Query(default=0, ge=0),
    limit: int = Query(default=50, ge=1, le=100),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    rows, total = await expense_service.list_expenses(
        db, spbu_id, tanggal, tanggal_from, tanggal_to, laporan_shift_id, kategori_id, skip, limit
    )
    return {
        "data": [r.model_dump() for r in rows],
        "meta": {"total": total, "skip": skip, "limit": limit},
    }


@router.post("", response_model=dict, status_code=201)
async def create_expense(
    spbu_id: int,
    body: ExpenseCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        expense = await expense_service.create_expense(db, spbu_id, current_user.id, body)
    except Exception as e:
        raise _err(e)
    return {"data": expense.model_dump(), "message": "Expense berhasil disimpan"}


@router.get("/{expense_id}", response_model=dict)
async def get_expense(
    spbu_id: int,
    expense_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        expense = await expense_service.get_expense(db, spbu_id, expense_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    return {"data": expense.model_dump()}


@router.patch("/{expense_id}", response_model=dict)
async def update_expense(
    spbu_id: int,
    expense_id: int,
    body: ExpenseUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        expense = await expense_service.update_expense(db, spbu_id, expense_id, body)
    except Exception as e:
        raise _err(e)
    return {"data": expense.model_dump(), "message": "Expense berhasil diupdate"}


@router.delete("/{expense_id}", response_model=dict)
async def delete_expense(
    spbu_id: int,
    expense_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        await expense_service.delete_expense(db, spbu_id, expense_id)
    except Exception as e:
        raise _err(e)
    return {"message": "Expense berhasil dihapus"}


@router.post("/{expense_id}/submit", response_model=dict)
async def submit_expense(
    spbu_id: int, expense_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        expense = await expense_service.submit_expense(db, spbu_id, expense_id, current_user.id)
    except Exception as e:
        raise _err(e)
    return {"data": expense.model_dump(), "message": "Expense berhasil di-submit"}


@router.post("/{expense_id}/recall", response_model=dict)
async def recall_expense(
    spbu_id: int, expense_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        expense = await expense_service.recall_expense(db, spbu_id, expense_id, current_user.id)
    except Exception as e:
        raise _err(e)
    return {"data": expense.model_dump(), "message": "Expense berhasil di-recall"}


@router.post("/{expense_id}/review", response_model=dict)
async def review_expense(
    spbu_id: int, expense_id: int, body: ReviewRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        expense = await expense_service.review_expense(db, spbu_id, expense_id, current_user, body.action, body.catatan)
    except Exception as e:
        raise _err(e)
    label = "diapprove" if body.action == "approve" else "ditolak"
    return {"data": expense.model_dump(), "message": f"Expense berhasil {label}"}


@router.post("/{expense_id}/unlock", response_model=dict)
async def unlock_expense(
    spbu_id: int, expense_id: int, body: UnlockRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    try:
        expense = await expense_service.unlock_expense(db, spbu_id, expense_id, current_user, body.alasan)
    except Exception as e:
        raise _err(e)
    return {"data": expense.model_dump(), "message": "Expense berhasil di-unlock"}


@router.post("/{expense_id}/bukti", response_model=dict, status_code=201)
async def upload_bukti(
    spbu_id: int,
    expense_id: int,
    file: UploadFile = File(...),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    file_bytes = await file.read()
    if len(file_bytes) > 10 * 1024 * 1024:
        raise HTTPException(status_code=400, detail="File terlalu besar (max 10 MB)")
    try:
        expense = await expense_service.upload_bukti(
            db, spbu_id, expense_id, file_bytes, file.filename or "bukti"
        )
    except Exception as e:
        raise _err(e)
    return {"data": expense.model_dump(), "message": "Bukti berhasil diupload"}


@router.get("/{expense_id}/bast-pdf")
async def download_bast_expense(
    spbu_id: int,
    expense_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    """Download BAST PDF for an approved expense. Requires: expenses:view permission."""
    from fastapi.responses import Response as FastAPIResponse
    from app.utils.bast_pdf import generate_bast

    try:
        expense = await expense_service.get_expense(db, spbu_id, expense_id)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    if expense.status != "approved":
        raise HTTPException(400, detail="BAST hanya tersedia untuk expense yang sudah Approved")
    from app.models.spbu import Spbu
    from sqlalchemy import select as sa_select
    spbu = (await db.execute(sa_select(Spbu).where(Spbu.id == spbu_id))).scalar_one_or_none()
    spbu_name = spbu.name if spbu else f"SPBU {spbu_id}"
    details = [
        ("Tanggal", str(expense.tanggal)),
        ("Kategori", expense.kategori_nama),
        ("Keterangan", expense.keterangan or "-"),
        ("Jumlah", f"Rp {expense.jumlah:,.0f}"),
    ]
    pdf_bytes = generate_bast(
        modul="Expenses", record_id=expense_id,
        spbu_name=spbu_name, tanggal=str(expense.tanggal),
        submitter_name=expense.submitted_by_name or "-",
        submitter_at=expense.submitted_at,
        approver_name=expense.reviewed_by_name or "-",
        approver_at=expense.reviewed_at,
        details=details,
    )
    filename = f"BAST-Expense-{spbu_id}-{expense.tanggal}-{expense_id}.pdf"
    return FastAPIResponse(content=pdf_bytes, media_type="application/pdf",
                    headers={"Content-Disposition": f'attachment; filename="{filename}"'})
