"""图片下载与 base64 编码（vault extract）。"""

from __future__ import annotations

import base64
import mimetypes
from typing import Any
from urllib.parse import urlparse
from urllib.request import Request, urlopen


def max_image_bytes(config: dict[str, Any] | None = None) -> int:
    vault = (config or {}).get("vault") or {}
    extract = vault.get("extract") if isinstance(vault, dict) else {}
    if isinstance(extract, dict) and extract.get("max_image_bytes"):
        return int(extract["max_image_bytes"])
    return 8_388_608


def guess_mime(url: str | None, content_type: str | None) -> str:
    if content_type:
        mime = content_type.split(";")[0].strip().lower()
        if mime.startswith("image/"):
            return mime
    if url:
        guessed, _ = mimetypes.guess_type(url)
        if guessed and guessed.startswith("image/"):
            return guessed
    return "image/png"


def fetch_image_b64(url: str, *, max_bytes: int, opener=None) -> tuple[str, str]:
    req = Request(url, headers={"User-Agent": "NianNianBot/1.0"})
    open_fn = opener or urlopen
    with open_fn(req, timeout=30) as resp:
        content_type = resp.headers.get("Content-Type") if hasattr(resp, "headers") else None
        raw = resp.read(max_bytes + 1)
    if len(raw) > max_bytes:
        raise ValueError("image_too_large")
    mime = guess_mime(url, content_type)
    return base64.b64encode(raw).decode("ascii"), mime


def load_image_b64(payload: dict[str, Any], *, max_bytes: int, fetch_fn=None) -> tuple[str, str]:
    if payload.get("image_b64"):
        raw = base64.b64decode(str(payload["image_b64"]))
        if len(raw) > max_bytes:
            raise ValueError("image_too_large")
        mime = str(payload.get("mime") or "image/png")
        return str(payload["image_b64"]), mime
    url = str(payload.get("url") or "").strip()
    if not url:
        raise ValueError("image_url_or_b64_required")
    parsed = urlparse(url)
    if parsed.scheme not in ("http", "https"):
        raise ValueError("invalid_image_url")
    fetch = fetch_fn or fetch_image_b64
    return fetch(url, max_bytes=max_bytes)
