"""Decodes an arbitrary uploaded file (PNG/JPEG/GIF/BMP/WEBP/TIFF/PDF/...)
into a plain RGB PIL image, for the static-image widget (see routers/
api_widgets.py's api_widget_static_upload, app/widgets/static_image.py).
The result is stored (as PNG bytes) rather than the original upload, so
render() never needs to re-run PDF/GIF decoding on every panel refresh --
this module only runs once, at upload time.

PDF decoding uses pypdfium2 (Google's PDFium bindings -- BSD-3-Clause/
Apache-2.0, no copyleft exposure) rather than a GPL/AGPL alternative
like PyMuPDF, per CLAUDE.md's copyleft-dependency convention (a check
that only applies to copyleft/unclear licenses -- this one's plainly
permissive, so no explicit flag was needed here)."""

from __future__ import annotations

import io

import pypdfium2 as pdfium
from fastapi import HTTPException
from PIL import Image, UnidentifiedImageError

MAX_UPLOAD_BYTES = 25 * 1024 * 1024  # generous for a single image/PDF page; stops an accidental huge upload
# ~144 DPI off a PDF's 72-DPI native unit -- comfortably above the panel's
# own 800x480, without ballooning render time/memory on a poster-sized page.
PDF_RENDER_SCALE = 2.0


def decode_upload(data: bytes) -> Image.Image:
    """Raises HTTPException(400) for anything that isn't a recognizable
    image or PDF. Detects PDF by magic bytes, not the client-supplied
    filename/content-type (neither is trustworthy). A PDF renders only
    its first page -- there's no "which page" concept for a single-image
    widget."""
    if len(data) > MAX_UPLOAD_BYTES:
        raise HTTPException(400, f"File is too large (max {MAX_UPLOAD_BYTES // (1024 * 1024)}MB)")
    if data.startswith(b"%PDF-"):
        return _decode_pdf(data)
    try:
        img = Image.open(io.BytesIO(data))
        img.load()
    except UnidentifiedImageError:
        raise HTTPException(400, "Not a recognizable image or PDF file") from None
    return img.convert("RGB")


def _decode_pdf(data: bytes) -> Image.Image:
    try:
        pdf = pdfium.PdfDocument(data)
        if len(pdf) == 0:
            raise HTTPException(400, "PDF has no pages")
        bitmap = pdf[0].render(scale=PDF_RENDER_SCALE)
    except pdfium.PdfiumError as e:
        raise HTTPException(400, f"Could not read PDF: {e}") from e
    return bitmap.to_pil().convert("RGB")
