from __future__ import annotations

from io import BytesIO
from pathlib import Path
import warnings

from PIL import Image, ImageOps, UnidentifiedImageError

ALLOWED_INPUT_FORMATS = {"JPEG", "PNG", "WEBP"}
Image.MAX_IMAGE_PIXELS = 40_000_000


class InvalidImage(ValueError):
    pass


def normalise_image(
    content: bytes,
    *,
    max_width: int,
    max_height: int,
    jpeg_quality: int,
) -> tuple[bytes, str, str]:
    """Validate, resize, orient and strip metadata from an uploaded image."""
    if not content:
        raise InvalidImage("Le fichier transmis est vide.")

    try:
        with warnings.catch_warnings():
            warnings.simplefilter("error", Image.DecompressionBombWarning)
            with Image.open(BytesIO(content)) as source:
                if source.format not in ALLOWED_INPUT_FORMATS:
                    raise InvalidImage("Formats acceptés : JPEG, PNG et WebP.")
                source.load()
                image = ImageOps.exif_transpose(source)
    except (UnidentifiedImageError, OSError, ValueError, Image.DecompressionBombError, Image.DecompressionBombWarning) as exc:
        if isinstance(exc, InvalidImage):
            raise
        raise InvalidImage("Le fichier n'est pas une image valide.") from exc

    image.thumbnail((max_width, max_height), Image.Resampling.LANCZOS)

    # Standardise to JPEG. This strips EXIF/GPS metadata and avoids active formats such as SVG.
    if image.mode in {"RGBA", "LA"}:
        background = Image.new("RGB", image.size, "white")
        alpha = image.getchannel("A")
        background.paste(image.convert("RGB"), mask=alpha)
        image = background
    elif image.mode != "RGB":
        image = image.convert("RGB")

    output = BytesIO()
    image.save(
        output,
        format="JPEG",
        quality=max(60, min(jpeg_quality, 95)),
        optimize=True,
        progressive=True,
    )
    return output.getvalue(), ".jpg", "image/jpeg"


def safe_original_name(filename: str | None) -> str:
    return Path(filename or "photo.jpg").name[:255]
