"""Router for penjualan / laporan shift endpoints."""

from datetime import date

from fastapi import APIRouter, Depends, File, Form, 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.operational import (
    LaporanShiftCreate,
    LaporanShiftDetailResponse,
    LaporanShiftResponse,
    LaporanShiftUpdate,
    ReviewRequest,
    TellerInitResponse,
    UnlockRequest,
)
from app.services import operational_service

router = APIRouter(
    prefix="/spbus/{spbu_id}/laporan-shift",
    tags=["penjualan"],
)


def _service_error(e: ValueError | PermissionError) -> HTTPException:
    """Convert service-layer ValueError → 404/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_laporan(
    spbu_id: int,
    tanggal: date | None = Query(default=None),
    shift_id: int | None = Query(default=None),
    laporan_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 laporan shift for an SPBU with optional filters.
    Requires: penjualan:view permission (currently enforced by authentication).
    """
    try:
        laporan_list, total = await operational_service.list_laporan(
            db, spbu_id, tanggal, shift_id, laporan_status, skip, limit
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {
        "data": [l.model_dump() for l in laporan_list],
        "meta": {"total": total, "page": skip // limit + 1, "per_page": limit},
    }


@router.get("/teller-init", response_model=dict)
async def get_teller_init(
    spbu_id: int,
    tanggal: date = Query(..., description="Tanggal laporan yang akan dibuat (YYYY-MM-DD)"),
    shift_id: int = Query(..., description="Shift ID laporan yang akan dibuat"),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Return pre-filled teller_awal values from the previous shift's submitted laporan.
    Also returns can_approve flag so the frontend can show "Save & Approve" vs "Save & Submit".
    Requires: penjualan:view permission.
    """
    try:
        rows = await operational_service.get_teller_init(db, spbu_id, tanggal, shift_id)
        can_approve = await operational_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.post("/extract-from-image", response_model=dict)
async def extract_from_image(
    spbu_id: int,
    file: UploadFile = File(...),
    old_foto_url: str | None = Form(None),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Upload foto laporan shift → Gemini Vision ekstrak data → return structured JSON.
    Foto juga di-upload ke Google Drive sebagai arsip.
    Jika old_foto_url disertakan, file lama akan dihapus (replace, bukan append).
    """
    from app.utils.file_upload import save_upload, UploadContext, delete_file
    from app.utils.gemini_vision import extract_from_image as gemini_extract

    ALLOWED = {"image/jpeg", "image/jpg", "image/png", "image/webp"}
    if file.content_type not in ALLOWED:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Format file tidak didukung. Gunakan JPG, PNG, atau WEBP.",
        )

    image_bytes = await file.read()

    # Upload ke Google Drive / lokal
    from datetime import date as _date
    from app.utils.file_upload import get_spbu_code
    today = _date.today()
    spbu_code = await get_spbu_code(db, spbu_id)
    ctx = UploadContext(spbu_code, "laporan", today)
    new_url = await save_upload(image_bytes, file.filename or "laporan.jpg", ctx)

    # Delete old file now that new one is saved (non-blocking, errors swallowed)
    await delete_file(old_foto_url)

    # Ekstrak data via Gemini Vision — always return foto_url even if AI fails
    extract_error: str | None = None
    extracted = None
    try:
        extracted = await gemini_extract(image_bytes, file.content_type)
    except Exception as e:
        extract_error = f"Gagal membaca gambar: {str(e)}"

    return {
        "data": extracted,
        "foto_url": new_url,
        "extract_error": extract_error,
    }


@router.post("", response_model=dict, status_code=status.HTTP_201_CREATED)
async def create_laporan(
    spbu_id: int,
    data: LaporanShiftCreate,
    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 laporan shift (starts in DRAFT status).
    Requires: penjualan:create permission.
    """
    try:
        laporan = await operational_service.create_laporan(
            db, spbu_id, data.shift_id, data.tanggal, data.nozzles, current_user,
            source_foto_url=data.source_foto_url,
            force_draft=force_draft,
            kas=data.kas,
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    action = "dibuat & disetujui" if laporan.status == "approved" else "disimpan sebagai draft"
    return {"data": laporan.model_dump(), "message": f"Laporan berhasil {action}"}


@router.get("/{laporan_id}", response_model=dict)
async def get_laporan_detail(
    spbu_id: int,
    laporan_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Return full detail for a single laporan shift including all nozzle rows.
    Requires: penjualan:view permission.
    """
    try:
        laporan = await operational_service.get_laporan_detail(db, spbu_id, laporan_id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": laporan.model_dump()}


@router.patch("/{laporan_id}", response_model=dict)
async def update_laporan(
    spbu_id: int,
    laporan_id: int,
    data: LaporanShiftUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Replace nozzle rows on a DRAFT laporan.
    Requires: penjualan:edit permission.
    """
    try:
        laporan = await operational_service.update_laporan(
            db, spbu_id, laporan_id, data.nozzles,
            source_foto_url=data.source_foto_url,
            kas=data.kas,
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": laporan.model_dump(), "message": "Laporan berhasil diupdate"}


@router.patch("/{laporan_id}/submit", response_model=dict)
async def submit_laporan(
    spbu_id: int,
    laporan_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Submit a DRAFT laporan for review.
    Requires: penjualan:create permission.
    """
    try:
        laporan = await operational_service.submit_laporan(
            db, spbu_id, laporan_id, current_user.id
        )
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    return {"data": laporan.model_dump(), "message": "Laporan berhasil di-submit"}


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


@router.patch("/{laporan_id}/review", response_model=dict)
async def review_laporan(
    spbu_id: int,
    laporan_id: int,
    data: ReviewRequest,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """
    Approve or reject a SUBMITTED laporan.
    Requires: penjualan:approve permission.
    """
    try:
        laporan = await operational_service.review_laporan(
            db, spbu_id, laporan_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": laporan.model_dump(), "message": f"Laporan berhasil {action_label}"}


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


@router.delete("/{laporan_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_laporan(
    spbu_id: int,
    laporan_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> None:
    """
    Hard-delete a DRAFT laporan shift (and its nozzle rows via cascade).
    Only DRAFT status can be deleted. Requires: penjualan:delete permission.
    """
    try:
        await operational_service.delete_laporan(db, spbu_id, laporan_id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)


@router.post("/{laporan_id}/import-csv", response_model=dict)
async def import_pos_csv(
    spbu_id: int,
    laporan_id: int,
    file: UploadFile = File(...),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """Upload a POS CSV file and import per-transaction data."""
    if not file.filename or not file.filename.endswith(".csv"):
        raise HTTPException(status_code=400, detail="File harus berformat CSV")

    content = await file.read()
    csv_text = content.decode("utf-8-sig")

    from app.services import pos_import_service
    try:
        result = await pos_import_service.import_pos_csv(
            db, spbu_id, laporan_id, csv_text, current_user.id
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    return {"data": result, "message": f"{result['inserted']} transaksi berhasil diimpor"}


@router.get("/{laporan_id}/ba-pdf")
async def download_ba_laporan(
    spbu_id: int,
    laporan_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
):
    """Download Berita Acara PDF for an approved laporan shift. Requires: penjualan:view permission."""
    from fastapi.responses import Response as FastAPIResponse
    from app.utils.ba_pdf import generate_ba_penjualan
    from app.models.spbu import Spbu
    from sqlalchemy import select as sa_select

    try:
        laporan = await operational_service.get_laporan_detail(db, spbu_id, laporan_id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)

    if laporan.status not in ("approved", "locked"):
        raise HTTPException(400, detail="BA hanya tersedia untuk laporan yang sudah Approved")

    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}"
    spbu_code = spbu.nomor_pertamina if spbu else str(spbu_id)

    kas = {
        "kas_100k":           laporan.kas_100k,
        "kas_50k":            laporan.kas_50k,
        "kas_20k":            laporan.kas_20k,
        "kas_10k":            laporan.kas_10k,
        "kas_5k":             laporan.kas_5k,
        "kas_2k":             laporan.kas_2k,
        "kas_1k":             laporan.kas_1k,
        "kas_logam":          laporan.kas_logam,
        "pembayaran_kartu":   laporan.pembayaran_kartu,
        "pembayaran_qr":      laporan.pembayaran_qr,
        "pembayaran_instansi": laporan.pembayaran_instansi,
    }
    nozzle_dicts = [n.model_dump() for n in laporan.nozzles]

    pdf_bytes = generate_ba_penjualan(
        spbu_name=spbu_name,
        spbu_code=spbu_code,
        tanggal=str(laporan.tanggal),
        shift_nama=laporan.shift_nama or "-",
        approver_name=laporan.reviewed_by_name or "-",
        nozzles=nozzle_dicts,
        total_volume=laporan.total_volume,
        total_nilai=laporan.total_nilai,
        kas=kas,
    )

    # Build clean filename: BA-Penjualan-{spbu_code}-{tanggal}-{shift_nama}.pdf
    safe_shift = (laporan.shift_nama or "shift").replace(" ", "_").replace("/", "-")
    safe_code = spbu_code.replace(" ", "_")
    filename = f"BA-Penjualan-{safe_code}-{laporan.tanggal}-{safe_shift}.pdf"

    return FastAPIResponse(
        content=pdf_bytes,
        media_type="application/pdf",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )


@router.get("/{laporan_id}/activity-log", response_model=dict)
async def get_activity_log(
    spbu_id: int,
    laporan_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_spbu_access),
) -> dict:
    """Return full audit trail for a laporan shift (all submit/recall/approve/unlock events)."""
    from sqlalchemy import select as sa_select
    from app.models.audit import AuditLog
    from sqlalchemy.orm import selectinload

    result = await db.execute(
        sa_select(AuditLog)
        .options(selectinload(AuditLog.user))
        .where(
            AuditLog.modul == "penjualan",
            AuditLog.object_id == laporan_id,
            AuditLog.spbu_id == spbu_id,
        )
        .order_by(AuditLog.created_at.asc())
    )
    rows = result.scalars().all()
    return {
        "data": [
            {
                "aksi": r.aksi,
                "user_name": r.user.name if r.user else None,
                "created_at": r.created_at.isoformat() if r.created_at else None,
                "detail": r.detail,
            }
            for r in rows
        ]
    }

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

    try:
        laporan = await operational_service.get_laporan_detail(db, spbu_id, laporan_id)
    except (ValueError, PermissionError) as e:
        raise _service_error(e)
    if laporan.status not in ("approved", "locked"):
        raise HTTPException(400, detail="BAST hanya tersedia untuk laporan 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(laporan.tanggal)),
        ("Shift", laporan.shift_nama or "-"),
        ("Total Volume", f"{laporan.total_volume} L"),
        ("Total Nilai", f"Rp {laporan.total_nilai:,.0f}"),
    ]
    pdf_bytes = generate_bast(
        modul="Penjualan", record_id=laporan_id,
        spbu_name=spbu_name, tanggal=str(laporan.tanggal),
        submitter_name=laporan.submitted_by_name or "-",
        submitter_at=laporan.submitted_at,
        approver_name=laporan.reviewed_by_name or "-",
        approver_at=laporan.reviewed_at,
        details=details,
    )
    filename = f"BAST-Penjualan-{spbu_id}-{laporan.tanggal}-shift{laporan.shift_id}.pdf"
    return FastAPIResponse(content=pdf_bytes, media_type="application/pdf",
                    headers={"Content-Disposition": f'attachment; filename="{filename}"'})
