"""Rondache : placeholder neutre tant que le fichier officiel n'est pas fourni (SVG, PNG ou WebP)."""
from __future__ import annotations

import base64
import io
import os
from pathlib import Path

from defusedxml import ElementTree as SafeET
from PIL import Image

from ..core.config import get_config

PLACEHOLDER = Path(__file__).resolve().parent.parent / "assets" / "branding" / "placeholder.svg"
MAX_BYTES = 2 * 1024 * 1024
SVG_ALLOWED = {
    "svg", "g", "path", "circle", "ellipse", "rect", "line", "polyline", "polygon", "text", "tspan", "defs",
    "lineargradient", "radialgradient", "stop", "clippath", "mask", "use", "title", "desc", "symbol", "image",
    "style", "pattern", "metadata",
}


class BrandingError(ValueError):
    pass


def _files() -> dict[str, Path]:
    d = get_config().branding_dir
    return {"image/svg+xml": d / "rondache.svg", "image/png": d / "rondache.png"}


def current() -> tuple[bytes, str]:
    for mime, path in _files().items():
        if path.is_file():
            return path.read_bytes(), mime
    return PLACEHOLDER.read_bytes(), "image/svg+xml"


def info() -> dict[str, object]:
    for mime, path in _files().items():
        if path.is_file():
            return {"custom": True, "mime": mime, "size": path.stat().st_size}
    return {"custom": False, "mime": "image/svg+xml", "size": PLACEHOLDER.stat().st_size}


def data_uri() -> str:
    data, mime = current()
    return f"data:{mime};base64,{base64.b64encode(data).decode()}"


def _sanitize_svg(data: bytes) -> bytes:
    head = data[:4096].lower()
    if b"<!doctype" in head or b"<!entity" in data.lower():
        raise BrandingError("SVG refusé : DOCTYPE/ENTITY interdits.")
    try:
        root = SafeET.fromstring(data)
    except Exception:
        raise BrandingError("SVG illisible.") from None
    for el in root.iter():
        tag = el.tag.split("}")[-1].lower()
        if tag not in SVG_ALLOWED:
            raise BrandingError(f"SVG refusé : élément <{tag}> non autorisé.")
        if tag == "style" and el.text and ("@import" in el.text or "url(" in el.text.replace(" ", "")):
            raise BrandingError("SVG refusé : feuille de style avec ressource externe.")
        for attr, value in el.attrib.items():
            name = attr.split("}")[-1].lower()
            v = value.strip().lower()
            if name.startswith("on"):
                raise BrandingError("SVG refusé : attribut d'événement (script).")
            if name == "href" and not (v.startswith("#") or v.startswith("data:image/png")
                                       or v.startswith("data:image/jpeg")):
                raise BrandingError("SVG refusé : lien externe.")
            if "javascript:" in v:
                raise BrandingError("SVG refusé : script.")
    return data


def install(data: bytes) -> dict[str, object]:
    if len(data) > MAX_BYTES:
        raise BrandingError("Fichier trop volumineux (2 Mo maximum).")
    d = get_config().branding_dir
    d.mkdir(parents=True, exist_ok=True)
    if data.lstrip()[:5].lower() in {b"<?xml", b"<svg "} or b"<svg" in data[:512].lower():
        clean = _sanitize_svg(data)
        target, mime = d / "rondache.svg", "image/svg+xml"
    elif data[:8] == b"\x89PNG\r\n\x1a\n" or (data[:4] == b"RIFF" and data[8:12] == b"WEBP"):
        Image.MAX_IMAGE_PIXELS = 16_000_000
        try:
            with Image.open(io.BytesIO(data)) as im:
                im.verify()
            with Image.open(io.BytesIO(data)) as im:
                im = im.convert("RGBA")
                im.thumbnail((1200, 1200))
                out = io.BytesIO()
                im.save(out, "PNG", optimize=True)  # réencodage : métadonnées et contenus parasites éliminés
        except BrandingError:
            raise
        except Exception:
            raise BrandingError("Image illisible ou corrompue.") from None
        clean, target, mime = out.getvalue(), d / "rondache.png", "image/png"
    else:
        raise BrandingError("Formats acceptés : SVG, PNG ou WebP.")
    remove()
    tmp = target.with_suffix(".tmp")
    tmp.write_bytes(clean)
    os.chmod(tmp, 0o640)
    tmp.replace(target)
    return {"mime": mime, "size": len(clean)}


def remove() -> None:
    for path in _files().values():
        if path.is_file():
            path.unlink()
