"""Téléversement, analyse et déclinaisons des médias (remplace App\\Support\\ImageVariants)."""

import mimetypes
import os
from io import BytesIO

from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from PIL import Image, UnidentifiedImageError

from .models import DOCUMENT_EXTENSIONS, IMAGE_EXTENSIONS, MediaAsset, variant_name

_PIL_FORMATS = {"jpg": "JPEG", "jpeg": "JPEG", "png": "PNG", "webp": "WEBP"}


def validate_upload(uploaded, images_only=False):
    ext = os.path.splitext(uploaded.name)[1].lstrip(".").lower()
    allowed = IMAGE_EXTENSIONS if images_only else IMAGE_EXTENSIONS | DOCUMENT_EXTENSIONS
    if ext not in allowed:
        raise ValidationError(
            "Format non autorisé (%(ext)s). Formats acceptés : %(ok)s.",
            params={"ext": ext or "?", "ok": ", ".join(sorted(allowed))},
        )
    conf = settings.LABELINFO
    limit_mb = conf["MAX_IMAGE_SIZE_MB"] if ext in IMAGE_EXTENSIONS else conf["MAX_DOCUMENT_SIZE_MB"]
    if uploaded.size > limit_mb * 1024 * 1024:
        raise ValidationError("Fichier trop volumineux (maximum %(mb)s Mo).", params={"mb": limit_mb})
    if ext in IMAGE_EXTENSIONS:
        try:
            pos = uploaded.tell()
            Image.open(uploaded).verify()
            uploaded.seek(pos)
        except (UnidentifiedImageError, OSError):
            raise ValidationError("Ce fichier n'est pas une image valide.")
    return ext


def create_asset(uploaded, user=None, collection=MediaAsset.Collection.DIVERS, images_only=False, **meta):
    validate_upload(uploaded, images_only=images_only)
    asset = MediaAsset(uploade_par=user, collection=collection, **meta)
    if not asset.titre:
        asset.titre = os.path.splitext(os.path.basename(uploaded.name))[0].replace("-", " ").replace("_", " ")[:255]
    asset.fichier.save(uploaded.name, uploaded, save=False)
    analyse(asset)
    asset.save()
    generate_variants(asset)
    return asset


def analyse(asset):
    """Renseigne type, MIME, taille et dimensions à partir du fichier stocké."""
    name = asset.fichier.name
    ext = asset.extension
    asset.mime = mimetypes.guess_type(name)[0] or "application/octet-stream"
    try:
        asset.taille = default_storage.size(name)
    except OSError:
        asset.taille = 0
    if ext in IMAGE_EXTENSIONS:
        asset.type = MediaAsset.Kind.IMAGE
        try:
            with default_storage.open(name, "rb") as fh, Image.open(fh) as img:
                asset.largeur, asset.hauteur = img.size
        except (OSError, UnidentifiedImageError):
            pass
    elif ext in DOCUMENT_EXTENSIONS:
        asset.type = MediaAsset.Kind.DOCUMENT
    else:
        asset.type = MediaAsset.Kind.OTHER


def generate_variants(asset, overwrite=False):
    """Crée les versions réduites sm/md/lg (sans agrandissement, GIF animés ignorés)."""
    fmt = _PIL_FORMATS.get(asset.extension)
    if not asset.is_image or not fmt:
        return {}
    out = {}
    try:
        with default_storage.open(asset.fichier.name, "rb") as fh, Image.open(fh) as src:
            src.load()
            for key, width in settings.LABELINFO["IMAGE_VARIANTS"].items():
                if src.width <= width:
                    continue
                name = variant_name(asset.fichier.name, key)
                if default_storage.exists(name) and not overwrite:
                    out[key] = name
                    continue
                img = src.copy()
                img.thumbnail((width, 10_000), Image.LANCZOS)
                if fmt == "JPEG" and img.mode not in ("RGB", "L"):
                    img = img.convert("RGB")
                buf = BytesIO()
                img.save(buf, fmt, quality=82, optimize=True)
                if default_storage.exists(name):
                    default_storage.delete(name)
                out[key] = default_storage.save(name, ContentFile(buf.getvalue()))
    except (OSError, UnidentifiedImageError):
        return {}
    if out != asset.variantes:
        asset.variantes = out
        MediaAsset.objects.filter(pk=asset.pk).update(variantes=out)
    return out


def delete_files(asset):
    names = [asset.fichier.name, *asset.variantes.values()]
    names += [variant_name(asset.fichier.name, k) for k in settings.LABELINFO["IMAGE_VARIANTS"]]
    for name in set(filter(None, names)):
        if default_storage.exists(name):
            default_storage.delete(name)


def register_existing_file(name, user=None, collection=MediaAsset.Collection.DIVERS, **meta):
    """Référence un fichier déjà présent dans le stockage (import Laravel)."""
    asset = MediaAsset.objects.filter(fichier=name).first()
    if asset:
        return asset, False
    asset = MediaAsset(fichier=name, uploade_par=user, collection=collection, **meta)
    if not asset.titre:
        asset.titre = os.path.splitext(os.path.basename(name))[0][:255]
    analyse(asset)
    asset.save()
    if default_storage.exists(name):
        generate_variants(asset)
    return asset, True
