"""Router for stock adjustment (sounding) endpoints."""

from datetime import date
from decimal import Decimal

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.operational import StatusLaporan
from app.models.user import User
from app.schemas.stock import (
    StockAdjustmentCreate,
    StockAdjustmentDetailResponse,
    StockAdjustmentResponse,
    StockAdjustmentUpdate,
    StockInitResponse,
    ReviewRequest,
    UnlockRequest,
)
from app.services import stock_service

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


def _service_error(e: ValueError | PermissionError) -> HTTPException:
    """Convert service-layer ValueError → 400 and PermissionError → 403."""
    if isinstance(e, PermissionError):
        return HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e))
    return HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))


@router.get("", response_model=dict)
async def list_adjustments(
    spbu_id: int,
    tanggal: date | None = Query(default=None),
    shift_id: int | None = Query(default=None),
    adj_status: StatusLaporan | None = Query(default=None, alias="status"),
    skip: int = Query(default=0, ge=0),
    limit: int = Query(default=20, ge=1, le=100),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    List stock adjustments for an SPBU with optional filters.
    Requires: stock:view permission.
    """
    try:
        adj_list, total = await stock_service.list_adjustments(
            db, spbu_id, tanggal, shift_id, adj_status, skip, limit
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {
        "data": [a.model_dump() for a in adj_list],
        "meta": {"total": total, "page": skip // limit + 1, "per_page": limit},
    }


@router.get("/stock-init", response_model=dict)
async def get_stock_init(
    spbu_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Return pre-fill data (last known volume/dipstick) for all active tanks of an SPBU.
    Also returns can_approve flag so the frontend can show "Simpan & Approve" vs "Simpan & Submit".
    Requires: stock:view permission.
    """
    try:
        rows = await stock_service.get_stock_init(db, spbu_id)
        can_approve = await stock_service.check_user_can_approve(db, current_user, spbu_id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": [r.model_dump() for r in rows], "can_approve": can_approve}


@router.get("/calculate-volume", response_model=dict)
async def calculate_volume(
    spbu_id: int,
    tangki_id: int = Query(...),
    height_mm: Decimal = Query(...),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Calculate volume (litres) from a dipstick height (mm) using the tank's calibration table.
    Returns error detail if height is out of calibration range.
    """
    try:
        volume = await stock_service.calculate_volume_for_tank(db, spbu_id, tangki_id, height_mm)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": {"volume_liter": str(volume)}}


@router.post("", response_model=dict, status_code=status.HTTP_201_CREATED)
async def create_adjustment(
    spbu_id: int,
    data: StockAdjustmentCreate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
    force_draft: bool = Query(False, description="Dev only: skip auto-approve even if user has approve permission"),
) -> dict:
    """
    Create a new stock adjustment (starts in DRAFT status).
    Requires: stock:create permission.
    """
    try:
        adj = await stock_service.create_adjustment(
            db, spbu_id, data.shift_id, data.tanggal, data.items, current_user,
            force_draft=force_draft,
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    action = "dibuat & disetujui" if adj.status == "approved" else "disimpan sebagai draft"
    return {"data": adj.model_dump(), "message": f"Stock adjustment berhasil {action}"}


@router.get("/{adj_id}", response_model=dict)
async def get_adjustment_detail(
    spbu_id: int,
    adj_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Return full detail for a single stock adjustment including all tank rows.
    Requires: stock:view permission.
    """
    try:
        adj = await stock_service.get_detail(db, spbu_id, adj_id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": adj.model_dump()}


@router.patch("/{adj_id}", response_model=dict)
async def update_adjustment(
    spbu_id: int,
    adj_id: int,
    data: StockAdjustmentUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Replace tank rows on a DRAFT stock adjustment.
    Requires: stock:edit permission.
    """
    try:
        adj = await stock_service.update_adjustment(db, spbu_id, adj_id, data.items)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": adj.model_dump(), "message": "Stock adjustment berhasil diupdate"}


@router.patch("/{adj_id}/submit", response_model=dict)
async def submit_adjustment(
    spbu_id: int,
    adj_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Submit a DRAFT stock adjustment for review.
    Validates all active tanks have digital readings before allowing submit.
    Requires: stock:create permission.
    """
    try:
        adj = await stock_service.submit_adjustment(db, spbu_id, adj_id, current_user.id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": adj.model_dump(), "message": "Stock adjustment berhasil di-submit"}


@router.patch("/{adj_id}/recall", response_model=dict)
async def recall_adjustment(
    spbu_id: int,
    adj_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Recall a SUBMITTED stock adjustment back to DRAFT (operator only).
    Only valid while status is still SUBMITTED (admin has not acted yet).
    Requires: stock:create permission.
    """
    try:
        adj = await stock_service.recall_adjustment(db, spbu_id, adj_id, current_user.id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": adj.model_dump(), "message": "Stock adjustment berhasil ditarik kembali ke Draft"}


@router.patch("/{adj_id}/review", response_model=dict)
async def review_adjustment(
    spbu_id: int,
    adj_id: int,
    data: ReviewRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Approve or reject a SUBMITTED stock adjustment.
    Requires: stock:approve permission.
    """
    try:
        adj = await stock_service.review_adjustment(
            db, spbu_id, adj_id, current_user.id, data.action, data.catatan
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    action_label = "disetujui" if data.action == "approve" else "ditolak"
    return {"data": adj.model_dump(), "message": f"Stock adjustment berhasil {action_label}"}


@router.patch("/{adj_id}/unlock", response_model=dict)
async def unlock_adjustment(
    spbu_id: int,
    adj_id: int,
    data: UnlockRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Unlock an APPROVED or LOCKED stock adjustment back to DRAFT, recording the reason.
    Requires: stock:approve permission.
    """
    try:
        adj = await stock_service.unlock_adjustment(
            db, spbu_id, adj_id, current_user.id, data.alasan
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": adj.model_dump(), "message": "Stock adjustment berhasil di-unlock"}


@router.post("/{adj_id}/items/{item_id}/fotos", response_model=dict, status_code=status.HTTP_201_CREATED)
async def upload_item_foto(
    spbu_id: int,
    adj_id: int,
    item_id: int,
    tipe: str = Query(..., description="Tipe foto: 'manual' atau 'digital'"),
    file: UploadFile = File(...),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Upload photo evidence for a tank sounding item (manual stick or digital display).
    Requires: stock:create permission.
    """
    file_bytes = await file.read()
    try:
        adj = await stock_service.upload_item_foto(
            db, spbu_id, adj_id, item_id, tipe, file_bytes, file.filename or "foto.jpg"
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": adj.model_dump(), "message": "Foto berhasil diupload"}


@router.delete("/{adj_id}/items/{item_id}/fotos/{foto_id}", response_model=dict)
async def delete_item_foto(
    spbu_id: int,
    adj_id: int,
    item_id: int,
    foto_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Delete a photo from a tank sounding item.
    Requires: stock:edit permission.
    """
    try:
        adj = await stock_service.delete_item_foto(
            db, spbu_id, adj_id, item_id, foto_id
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": adj.model_dump(), "message": "Foto berhasil dihapus"}


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

    try:
        adj = await stock_service.get_detail(db, spbu_id, adj_id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    if adj.status not in ("approved",):
        raise HTTPException(400, detail="BAST hanya tersedia untuk stock adjustment 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(adj.tanggal)),
        ("Shift", adj.shift_nama or "-"),
        ("Jumlah Tangki", str(len(adj.items))),
    ]
    pdf_bytes = generate_bast(
        modul="Stock Adjustment", record_id=adj_id,
        spbu_name=spbu_name, tanggal=str(adj.tanggal),
        submitter_name=adj.submitted_by_name or "-",
        submitter_at=adj.submitted_at,
        approver_name=adj.reviewed_by_name or "-",
        approver_at=adj.reviewed_at,
        details=details,
    )
    filename = f"BAST-StockAdjustment-{spbu_id}-{adj.tanggal}-shift{adj.shift_id}.pdf"
    return FastAPIResponse(content=pdf_bytes, media_type="application/pdf",
                    headers={"Content-Disposition": f'attachment; filename="{filename}"'})
