from __future__ import annotations

from fastapi import APIRouter, File, Form, HTTPException, UploadFile
from fastapi.responses import FileResponse, JSONResponse
from pydantic import EmailStr

from app.config import settings
from app.services import documents as document_service
from app.services.document_access import authorize_document_read, authorize_upload_client
from app.services.duplicate_detection import DuplicateUploadError
from app.store import DuplicateStatusRequest, UploadDocumentRequest, store

router = APIRouter(prefix="/v1", tags=["documents"])


@router.post("/documents/upload")
async def upload_document(
    platform: str = Form(...),
    email: EmailStr = Form(...),
    file: UploadFile = File(...),
    clientId: str | None = Form(None),
    actor: str = Form("User"),
    forceDuplicate: bool = Form(False),
):
    """Upload a document for a client. Client users cannot choose another client."""
    client_id = authorize_upload_client(
        platform=platform,
        email=str(email),
        requested_client_id=clientId,
    )

    try:
        storage_key, mime_type, file_size, file_hash = await document_service.save_document_file(
            file,
            client_id=client_id,
        )
    except ValueError as exc:
        msg = str(exc)
        if "MB" in msg or "smaller" in msg.lower():
            raise HTTPException(
                status_code=400,
                detail=(
                    f"This file is too large. "
                    f"Please upload a file smaller than {settings.max_document_size_mb} MB."
                ),
            ) from exc
        raise HTTPException(
            status_code=400,
            detail="This file type isn't supported. Please upload a PDF, JPG, PNG, or WEBP file.",
        ) from exc

    original_name = (file.filename or "document").strip() or "document"

    try:
        return store.upload_document(
            UploadDocumentRequest(
                clientId=client_id,
                fileName=original_name,
                actor=actor,
                mimeType=mime_type,
                fileSize=file_size,
                storageKey=storage_key,
                uploadedBy=actor,
                fileHash=file_hash,
                forceDuplicate=forceDuplicate,
            )
        )
    except DuplicateUploadError as exc:
        document_service.delete_document_file(storage_key)
        existing = exc.existing
        return JSONResponse(
            status_code=409,
            content={
                "detail": "duplicate",
                "message": "An identical document has already been uploaded.",
                "existing": {
                    "id": existing.id,
                    "file": existing.file,
                    "uploadedAt": existing.uploadedAt,
                    "status": existing.status,
                },
            },
        )
    except Exception as exc:
        document_service.delete_document_file(storage_key)
        raise HTTPException(
            status_code=500,
            detail="We couldn't upload this document. Please try again.",
        ) from exc


@router.post("/invoices/{invoice_id}/duplicate-status")
def set_duplicate_status(invoice_id: str, payload: DuplicateStatusRequest):
    from app.services.ledger_auth import require_firm

    actor = require_firm(platform=payload.platform, email=payload.email, actor=payload.actor)
    try:
        return store.set_duplicate_status(invoice_id, status=payload.status, actor=actor)
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Document not found") from exc
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc


@router.get("/documents/{invoice_id}/file")
def download_document(
    invoice_id: str,
    platform: str,
    email: EmailStr,
):
    """Serve the original uploaded document to authorized users only."""
    try:
        invoice = store._find_invoice(invoice_id)  # noqa: SLF001
    except KeyError as exc:
        raise HTTPException(status_code=404, detail="Document not found") from exc

    authorize_document_read(platform=platform, email=str(email), invoice=invoice)

    path = document_service.resolve_document_path(invoice.storageKey)
    if not path:
        raise HTTPException(status_code=404, detail="File not found")

    media = invoice.mimeType or "application/octet-stream"
    return FileResponse(
        path,
        media_type=media,
        filename=invoice.file,
        headers={"Cache-Control": "private, no-store"},
    )
