"""Shared TMDB API helpers for the gallery builder scripts.

Consolidates the tmdb_get/tmdb_person_images/tmdb_person_* helpers that were
byte-for-byte identical in make_gallery.py and make_jellyfin_gallery.py.
"""

import requests

TMDB_BASE = "https://api.themoviedb.org/3"
TMDB_IMG = "https://image.tmdb.org/t/p/original"


def tmdb_get(path: str, token: str, **params) -> dict:
    url = TMDB_BASE + path
    # TMDB accepts two credential styles. A v4 "API Read Access Token" is a JWT,
    # which always starts with "eyJ" (base64 of '{"...'); it goes in an
    # Authorization: Bearer header. A v3 API key is an opaque hex string and goes
    # in the api_key query param instead. We auto-detect by that "eyJ" prefix.
    if token.startswith("eyJ"):
        headers = {"Authorization": f"Bearer {token}", "Accept": "application/json"}
        r = requests.get(url, params=params, headers=headers, timeout=10)
    else:
        params["api_key"] = token
        r = requests.get(url, params=params, headers={"Accept": "application/json"}, timeout=10)
    r.raise_for_status()
    return r.json()


def tmdb_person_images(tmdb_person_id: str, token: str) -> list[str]:
    images_data = tmdb_get(f"/person/{tmdb_person_id}/images", token)
    return [TMDB_IMG + p["file_path"] for p in images_data.get("profiles", []) if p.get("file_path")]


def tmdb_person_for_imdb(imdb_id: str, token: str) -> tuple[str | None, list[str]]:
    """Return (tmdb_person_id, profile_image_urls) for the TMDB person matching this IMDB person id."""
    data = tmdb_get(f"/find/{imdb_id}", token, external_source="imdb_id")
    people = data.get("person_results", [])
    if not people:
        return None, []
    tmdb_person_id = str(people[0]["id"])
    return tmdb_person_id, tmdb_person_images(tmdb_person_id, token)


def tmdb_person_by_name(name: str, token: str) -> tuple[str | None, list[str]]:
    """Return (tmdb_person_id, profile_image_urls) for the best name match on TMDB.

    Used when Jellyfin has no IMDB ProviderId for this person (the common case —
    Jellyfin rarely populates ProviderIds on Person items), so /find/{imdb_id}
    isn't an option. /search/person is sorted by popularity; take the top hit.
    """
    data = tmdb_get("/search/person", token, query=name)
    people = data.get("results", [])
    if not people:
        return None, []
    tmdb_person_id = str(people[0]["id"])
    return tmdb_person_id, tmdb_person_images(tmdb_person_id, token)


def tmdb_id_from_imdb(imdb_id: str, token: str) -> int:
    data = tmdb_get(f"/find/{imdb_id}", token, external_source="imdb_id")
    results = data.get("movie_results", [])
    if not results:
        raise ValueError(f"No TMDB movie found for IMDB ID {imdb_id}")
    return results[0]["id"]


def tmdb_tv_id_from_imdb(imdb_id: str, token: str) -> int | None:
    """Return the TMDB TV series id for an IMDB series id, or None if not found."""
    data = tmdb_get(f"/find/{imdb_id}", token, external_source="imdb_id")
    results = data.get("tv_results", [])
    return results[0]["id"] if results else None


def tmdb_episode_cast(tv_id: int | str, season: int, episode: int, token: str) -> list[dict]:
    """Return the per-episode cast for one episode (cast + guest_stars).

    Uses /tv/{id}/season/{s}/episode/{e}/credits, which is the episode-level
    roster — strictly tighter than the whole-series cast Jellyfin exposes.
    Each dict carries at least "id" (TMDB person id) and "name".
    """
    data = tmdb_get(f"/tv/{tv_id}/season/{season}/episode/{episode}/credits", token)
    # De-dupe by TMDB person id — a name can appear in both lists.
    by_id: dict = {}
    for person in data.get("cast", []) + data.get("guest_stars", []):
        pid = person.get("id")
        if pid is not None and pid not in by_id:
            by_id[pid] = person
    return list(by_id.values())
