"""Core media providers — licensed, keyed APIs only (Tenor, Pexels,
Pixabay, Freesound). They register through the same media_provider registry
as plugin providers and report ready=False until the user supplies a key
(presets domain "apikeys", or the matching environment variable).

Anything keyless/scraped lives in external plugins, never here.
"""
import hashlib
import json
import os
import re
import urllib.parse
import urllib.request

from clipshelf import presets
from clipshelf.config import ASSETS, SOUNDS

APIKEY_DOMAIN_DEFAULTS = {"tenor": "", "pexels": "", "pixabay": "", "freesound": ""}
APIKEY_DOMAIN_SCHEMA = {k: ("str", 128) for k in APIKEY_DOMAIN_DEFAULTS}
_ENV = {"tenor": "TENOR_API_KEY", "pexels": "PEXELS_API_KEY",
        "pixabay": "PIXABAY_API_KEY", "freesound": "FREESOUND_API_KEY"}


def register_apikeys_domain():
    presets.register(presets.PresetDomain(
        "apikeys", APIKEY_DOMAIN_DEFAULTS, APIKEY_DOMAIN_SCHEMA))


def _key(name: str) -> str:
    try:
        v = presets.domain("apikeys").get("keys").get(name) or ""
    except Exception:
        v = ""
    return v or os.getenv(_ENV[name], "")


def _fetch(url: str, headers: dict = None, cap: int = 30_000_000) -> bytes:
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (cut-editor)",
                                               **(headers or {})})
    with urllib.request.urlopen(req, timeout=30) as r:
        return r.read(cap)


def _fetch_json(url: str, headers: dict = None) -> dict:
    return json.loads(_fetch(url, headers).decode("utf-8", "replace"))


def save_image_bytes(raw: bytes, nice: str = "") -> dict:
    """Shared image-import: content-hashed asset + library note."""
    from clipshelf.routers.editor_common import lib_note
    if len(raw) < 100:
        raise ValueError("empty image")
    ext = {b"\x89PNG": ".png", b"GIF8": ".gif", b"RIFF": ".webp"}.get(raw[:4], ".jpg")
    name = hashlib.sha1(raw).hexdigest()[:20] + ext
    p = ASSETS / name
    if not p.exists():
        p.write_bytes(raw)
    lib_note("assets", name, name=(nice.strip()[:80] or None))
    return {"url": f"/api/editor/assets/{name}"}


def save_sound_bytes(raw: bytes, name: str, ext: str = ".mp3") -> dict:
    """Shared sound-import into the soundboard, collision-suffixed."""
    from clipshelf.routers.editor_common import slugify
    base = slugify(name or "sound") or "sound"
    p = SOUNDS / f"{base}{ext}"
    n = 1
    while p.exists():
        n += 1
        p = SOUNDS / f"{base}-{n}{ext}"
    p.write_bytes(raw)
    return {"name": p.stem.replace("_", " "), "url": f"/api/editor/sounds/{p.name}"}


class _Keyed:
    KEY = ""

    def ready(self) -> bool:
        return bool(_key(self.KEY))

    def _require(self) -> str:
        k = _key(self.KEY)
        if not k:
            raise ValueError(f"no {self.LABEL} API key — set it in settings (apikeys.{self.KEY})")
        return k


class TenorProvider(_Keyed):
    ID = "tenor"
    KIND = "gif"
    LABEL = "Tenor"
    KEY = "tenor"

    def search(self, q, params=None):
        key = self._require()
        u = ("https://tenor.googleapis.com/v2/search?q=" + urllib.parse.quote(q)
             + "&key=" + key + "&limit=18&media_filter=tinygif,gif")
        data = _fetch_json(u)
        return {"results": [{
            "preview": r.get("media_formats", {}).get("tinygif", {}).get("url", ""),
            "gif": r.get("media_formats", {}).get("gif", {}).get("url", ""),
            "title": r.get("content_description", "gif"),
        } for r in data.get("results", [])]}

    def import_item(self, item):
        from clipshelf.routers.editor_assets import gif_bytes_to_webm
        url = str(item.get("gif") or item.get("url") or "")
        if not re.match(r"^https?://", url):
            raise ValueError("bad gif url")
        return {"url": gif_bytes_to_webm(_fetch(url, cap=80_000_000),
                                         str(item.get("title") or ""))}


class PexelsProvider(_Keyed):
    ID = "pexels"
    KIND = "image"
    LABEL = "Pexels"
    KEY = "pexels"

    def search(self, q, params=None):
        key = self._require()
        data = _fetch_json("https://api.pexels.com/v1/search?per_page=24&query="
                           + urllib.parse.quote(q), {"Authorization": key})
        return {"results": [{"url": p["src"]["large2x"], "thumb": p["src"]["medium"],
                             "name": p.get("photographer", "")}
                            for p in data.get("photos", [])]}

    def import_item(self, item):
        return save_image_bytes(_fetch(str(item.get("url") or ""), cap=25_000_000),
                                str(item.get("name") or ""))


class PixabayProvider(_Keyed):
    ID = "pixabay"
    KIND = "image"
    LABEL = "Pixabay"
    KEY = "pixabay"

    def search(self, q, params=None):
        key = self._require()
        u = ("https://pixabay.com/api/?per_page=24&key=" + key
             + "&q=" + urllib.parse.quote(q))
        if (params or {}).get("transparent"):
            u += "&colors=transparent"
        data = _fetch_json(u)
        return {"results": [{"url": h.get("largeImageURL") or h.get("webformatURL"),
                             "thumb": h.get("previewURL"),
                             "name": h.get("tags", "")}
                            for h in data.get("hits", [])]}

    def import_item(self, item):
        return save_image_bytes(_fetch(str(item.get("url") or ""), cap=25_000_000),
                                str(item.get("name") or ""))


class FreesoundProvider(_Keyed):
    ID = "freesound"
    KIND = "sound"
    LABEL = "Freesound"
    KEY = "freesound"

    def search(self, q, params=None):
        key = self._require()
        data = _fetch_json(
            "https://freesound.org/apiv2/search/text/?fields=name,previews,duration"
            "&page_size=20&query=" + urllib.parse.quote(q),
            {"Authorization": f"Token {key}"})
        out = []
        for r in data.get("results", []):
            prev = (r.get("previews") or {}).get("preview-hq-mp3", "")
            if prev:
                out.append({"name": r.get("name", "sound")[:60], "url": prev})
        return {"results": out}

    def import_item(self, item):
        url = str(item.get("url") or "")
        if not re.match(r"^https?://", url):
            raise ValueError("bad url")
        return save_sound_bytes(_fetch(url), str(item.get("name") or "sound"))


def register_all(registry):
    for p in (TenorProvider(), PexelsProvider(), PixabayProvider(),
              FreesoundProvider()):
        registry.register_media_provider(p, source="core")
