"""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 net, 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, context=net.context()) 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")
