# -*- coding: utf-8 -*-
"""
Menu a seznam „VOD služby ČR“ – novinky ze Streaming Availability API (RapidAPI).
Vykreslení sladěné s resources.lib.tmdb.vod_kodi (labely, kontext, TMDb kde je k dispozici).
"""
from __future__ import annotations

import concurrent.futures
import sys
import time
import urllib.parse
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple

import xbmc
import xbmcgui
import xbmcplugin
import xbmcaddon

from resources.lib.utils.log_utils import get_logger
from resources.lib.utils.ui_utils import pick_icon as ui_pick_icon, add_menu_item as ui_add_item
from resources.lib.vod.streaming_availability_client import (
    fetch_changes,
    is_api_key_configured,
    DEFAULT_COUNTRY,
    DEFAULT_CATALOGS,
)

log = get_logger()

# Čitelné názvy katalogů (API id → menu); musí odpovídat DEFAULT_CATALOGS v streaming_availability_client.
STREAMING_CATALOG_LABELS: Dict[str, str] = {
    "netflix": "Netflix",
    "hbo": "Max (HBO)",
    "disney": "Disney+",
    "apple": "Apple TV+",
    "prime": "Prime Video",
}
_ALLOWED_SA_CATALOGS = frozenset(DEFAULT_CATALOGS)

addon = xbmcaddon.Addon("plugin.video.mmirousek_v3")
addon_handle = int(sys.argv[1])
base_url = sys.argv[0]


def _build_url(query: dict) -> str:
    return base_url + "?" + urllib.parse.urlencode(query)


def _build_url_kwargs(**kwargs):
    return _build_url(kwargs)


def _is_latinish(s: str) -> bool:
    """Kopie logiky z vod_kodi / tmdb_kodi (zkráceno)."""
    if not s:
        return False
    total_letters = latin_letters = strong_nonlatin = 0
    for ch in s:
        code = ord(ch)
        is_latin = (
            (0x0041 <= code <= 0x005A)
            or (0x0061 <= code <= 0x007A)
            or (0x00C0 <= code <= 0x00FF)
            or (0x0100 <= code <= 0x024F)
        )
        is_strong_nonlatin = (
            (0xAC00 <= code <= 0xD7AF)
            or (0x1100 <= code <= 0x11FF)
            or (0x3130 <= code <= 0x318F)
            or (0x4E00 <= code <= 0x9FFF)
            or (0x3400 <= code <= 0x4DBF)
            or (0x30A0 <= code <= 0x30FF)
            or (0x3040 <= code <= 0x309F)
            or (0x0400 <= code <= 0x04FF)
            or (0x0600 <= code <= 0x06FF)
        )
        if ch.isalpha():
            total_letters += 1
            if is_latin:
                latin_letters += 1
            if is_strong_nonlatin:
                strong_nonlatin += 1
    if total_letters == 0:
        return False
    if strong_nonlatin > 0:
        return False
    return (latin_letters / float(total_letters)) >= 0.70


def _poster_url(show: dict) -> str:
    img = (show or {}).get("imageSet") or {}
    vp = img.get("verticalPoster") or {}
    return vp.get("w480") or vp.get("w360") or vp.get("w720") or ""


def _fanart_url(show: dict) -> str:
    img = (show or {}).get("imageSet") or {}
    hb = img.get("horizontalBackdrop") or {}
    u = hb.get("w720") or hb.get("w1080") or ""
    if u:
        return u
    hp = img.get("horizontalPoster") or {}
    return hp.get("w720") or hp.get("w1080") or ""


def _year_text(show: dict, show_kind: str) -> str:
    if show_kind == "series":
        y = show.get("firstAirYear")
    else:
        y = show.get("releaseYear")
    if y is None:
        return ""
    try:
        return str(int(y))
    except (TypeError, ValueError):
        return str(y)


def _rating_float(show: dict) -> float:
    r = show.get("rating")
    try:
        v = float(r)
    except (TypeError, ValueError):
        return 0.0
    # API 0–100 → zobrazení jako „hvězdy“ jako TMDb 0–10
    if v > 10.0:
        return min(v / 10.0, 10.0)
    return v


def _tmdb_id(show: dict) -> Optional[int]:
    """
    Streaming API vrací tmdbId jako řetězec v OpenAPI; často ve tvaru „movie/238“ nebo „tv/12345“,
    ne čisté číslo — viz příklady movieofthenight.com (jinak int() selže a TMDb cs-CZ nejde).
    """
    if not show:
        return None
    raw = show.get("tmdbId")
    if raw is None or raw == "":
        raw = show.get("tmdb_id")
    if raw is None or raw == "":
        return None
    s = str(raw).strip()
    if "/" in s:
        parts = s.split("/", 1)
        if len(parts) == 2:
            prefix, num = parts[0].strip().lower(), parts[1].strip()
            if prefix in ("movie", "tv") and num.isdigit():
                return int(num)
    try:
        return int(s)
    except (TypeError, ValueError):
        return None


def _parse_change_timestamp(ch: dict) -> int:
    ts = ch.get("timestamp")
    if ts is None:
        return 0
    try:
        return int(ts)
    except (TypeError, ValueError):
        return 0


def _vod_date_ddmmyyyy(vod_ts: int) -> str:
    if vod_ts <= 0:
        return ""
    try:
        return datetime.fromtimestamp(vod_ts).strftime("%d.%m.%Y")
    except (ValueError, OSError):
        return ""


def _vod_premiered_iso(vod_ts: int) -> str:
    if vod_ts <= 0:
        return ""
    try:
        return datetime.fromtimestamp(vod_ts).strftime("%Y-%m-%d")
    except (ValueError, OSError):
        return ""


def _batch_tmdb_cs_titles(media_type: str, tmdb_ids: List[int]) -> Dict[int, str]:
    """Paralelní dotazy TMDb cs-CZ (stejný řád max_workers jako vod_kodi)."""
    if not tmdb_ids:
        return {}
    try:
        from resources.lib.tmdb.tmdb_utils import get_tmdb_title_for_language_diag, tmdb_api_keys_try_order
    except Exception as e:
        log.error(f"[VOD SA] TMDb cs-CZ import selhal: {e}")
        return {}

    # Klíče načíst v hlavním vlákně (Kodi + ThreadPool); stejné pořadí jako u get_english_title.
    _api_keys = tmdb_api_keys_try_order()
    log.info(
        f"[VOD SA] TMDb cs-CZ batch: media_type={media_type} tmdb_id_count={len(tmdb_ids)} "
        f"api_key_variants={len(_api_keys)}"
    )
    if not _api_keys:
        log.error(
            "[VOD SA] TMDb cs-CZ: žádný API klíč (nastavení + vestavěný fallback prázdné)"
        )

    def _one(tid: int) -> Tuple[int, str, str]:
        try:
            t, reason = get_tmdb_title_for_language_diag(
                media_type, tid, "cs-CZ", api_keys=_api_keys
            )
            name = (str(t).strip() if t else "")
            return tid, name, (reason or "")
        except Exception as ex:
            return tid, "", "exception:%s" % ex

    out: Dict[int, str] = {}
    failures: List[Tuple[int, str]] = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as ex:
        futures = {ex.submit(_one, tid): tid for tid in tmdb_ids}
        for fut in concurrent.futures.as_completed(futures, timeout=60):
            try:
                tid, name, reason = fut.result(timeout=15)
                if name:
                    out[tid] = name
                elif reason:
                    failures.append((tid, reason))
            except Exception as ex:
                log.error(f"[VOD SA] TMDb cs-CZ future error: {ex}")

    n_hit = len(out)
    n_req = len(tmdb_ids)
    log.info(
        f"[VOD SA] TMDb cs-CZ výsledek: názvy={n_hit}/{n_req} chyb={len(failures)}"
    )
    if failures:
        by_reason: Dict[str, List[int]] = {}
        for tid, rsn in failures:
            by_reason.setdefault(rsn, []).append(tid)
        parts = [f"{rsn}:{len(tids)}" for rsn, tids in sorted(by_reason.items(), key=lambda x: -len(x[1]))[:6]]
        top_reason = max(by_reason.items(), key=lambda x: len(x[1]))
        rsn0, tids0 = top_reason
        sample0 = tids0[:8]
        log.error(
            f"[VOD SA] TMDb cs-CZ diag: úspěch {n_hit}/{n_req} | rozpad {', '.join(parts)} "
            f"| nejčastější [{rsn0}] ukázka tmdb_id={sample0}"
        )
        shown = 0
        for rsn, tids in sorted(by_reason.items(), key=lambda x: -len(x[1])):
            if shown >= 5:
                break
            sample = tids[:5]
            log.info(
                f"[VOD SA] TMDb cs-CZ bez názvu ({rsn}): počet={len(tids)} příklady_tmdb_id={sample}"
            )
            shown += 1
        if len(by_reason) > 5:
            log.info(
                f"[VOD SA] TMDb cs-CZ: další typy chyb celkem {len(by_reason) - 5}"
            )

    return out


def _merge_change_rows(changes: list, shows_map: Any) -> List[Dict[str, Any]]:
    """Sloučí změny podle showId; služby jako unikátní seznam; vod_ts = max(timestamp) ze změn."""
    if not isinstance(shows_map, dict):
        shows_map = {}
    order: List[str] = []
    bucket: Dict[str, Dict[str, Any]] = {}
    for ch in changes or []:
        sid = ch.get("showId")
        if sid is None or sid == "":
            continue
        show = shows_map.get(sid) or shows_map.get(str(sid))
        if not show:
            continue
        sk = str(sid)
        svc = ch.get("service") or {}
        name = (svc.get("name") or svc.get("id") or "").strip()
        ts = _parse_change_timestamp(ch)
        if sk not in bucket:
            bucket[sk] = {"show": show, "services": [], "vod_ts": 0}
            order.append(sk)
        if name and name not in bucket[sk]["services"]:
            bucket[sk]["services"].append(name)
        if ts > 0:
            bucket[sk]["vod_ts"] = max(bucket[sk].get("vod_ts", 0), ts)
    return [bucket[sk] for sk in order if sk in bucket]


def _catalogs_for_list_params(params: dict) -> List[str]:
    """Whitelist sa_catalog → jeden katalog; jinak všechny DEFAULT_CATALOGS."""
    raw = (params.get("sa_catalog") or "").strip().lower()
    if raw and raw in _ALLOWED_SA_CATALOGS:
        return [raw]
    return list(DEFAULT_CATALOGS)


def show_vod_cz_streaming_menu() -> None:
    """Kořen: Filmy / Seriály → mezimenu výběru katalogu."""
    start = time.perf_counter()
    ui_add_item(
        handle=addon_handle,
        build_url_fn=_build_url,
        label="Filmy (Novinky)",
        plot=(
            "Nově přidané filmy na VOD v ČR (Streaming Availability API).\n"
            "Nejdřív zvolte službu nebo „Všechny služby“.\n"
            "Data cachovaná 24 h kvůli měsíční kvótě API."
        ),
        action="tmdb_vod_cz_streaming_catalog_menu",
        art_icon=ui_pick_icon(addon, "movie_popular.png", "DefaultMovies.png", "tmdb"),
        is_folder=True,
        sa_country=DEFAULT_COUNTRY,
        sa_show_type="movie",
    )
    ui_add_item(
        handle=addon_handle,
        build_url_fn=_build_url,
        label="Seriály (Novinky)",
        plot=(
            "Nově přidané seriály na VOD v ČR.\n"
            "Nejdřív zvolte službu nebo „Všechny služby“."
        ),
        action="tmdb_vod_cz_streaming_catalog_menu",
        art_icon=ui_pick_icon(addon, "tv_popular.png", "DefaultTVShows.png", "tmdb"),
        is_folder=True,
        sa_country=DEFAULT_COUNTRY,
        sa_show_type="series",
    )
    log.info(f"TMDB_PROFILER: show_vod_cz_streaming_menu {time.perf_counter() - start:.3f}s")
    xbmcplugin.endOfDirectory(addon_handle, succeeded=True, cacheToDisc=False)


def show_vod_cz_streaming_catalog_menu(params: dict) -> None:
    """Výběr katalogu: Všechny služby nebo jedna platforma → seznam novinek."""
    start = time.perf_counter()
    handle = int(sys.argv[1])
    show_kind = (params.get("sa_show_type") or "movie").lower()
    if show_kind not in ("movie", "series"):
        show_kind = "movie"
    type_label = "Seriály" if show_kind == "series" else "Filmy"
    xbmcplugin.setPluginCategory(handle, "VOD služby ČR • %s" % type_label)

    ui_add_item(
        handle=handle,
        build_url_fn=_build_url,
        label="Všechny služby",
        plot=(
            "Jeden dotaz na všechny katalogy (%s) — šetrnější k měsíční kvótě API."
            % ", ".join(STREAMING_CATALOG_LABELS.get(c, c) for c in DEFAULT_CATALOGS)
        ),
        action="tmdb_vod_cz_streaming_list",
        art_icon=ui_pick_icon(addon, "DefaultStudios.png", "DefaultFolder.png", "tmdb"),
        is_folder=True,
        sa_country=DEFAULT_COUNTRY,
        sa_show_type=show_kind,
    )
    for cat_id in DEFAULT_CATALOGS:
        lbl = STREAMING_CATALOG_LABELS.get(cat_id, cat_id.title())
        ui_add_item(
            handle=handle,
            build_url_fn=_build_url,
            label=lbl,
            plot=("Novinky pouze na %s (samostatný dotaz / cache)." % lbl),
            action="tmdb_vod_cz_streaming_list",
            art_icon=ui_pick_icon(addon, "DefaultStudios.png", "DefaultFolder.png", "tmdb"),
            is_folder=True,
            sa_country=DEFAULT_COUNTRY,
            sa_show_type=show_kind,
            sa_catalog=cat_id,
        )
    log.info(f"TMDB_PROFILER: show_vod_cz_streaming_catalog_menu {time.perf_counter() - start:.3f}s")
    xbmcplugin.endOfDirectory(handle, succeeded=True, cacheToDisc=False)


def show_vod_cz_streaming_list(params: dict) -> None:
    """Seznam titulků z /changes; stránkování přes sa_cursor."""
    start = time.perf_counter()
    handle = int(sys.argv[1])

    if not is_api_key_configured():
        xbmcgui.Dialog().notification(
            "VOD služby ČR",
            "Doplňte RapidAPI klíč: Nastavení doplňku → Nastavení OMDB → RapidAPI klíč (VOD novinky).",
            xbmcgui.NOTIFICATION_WARNING,
            5000,
        )
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    country = (params.get("sa_country") or DEFAULT_COUNTRY).lower()
    show_kind = (params.get("sa_show_type") or "movie").lower()
    if show_kind not in ("movie", "series"):
        show_kind = "movie"

    catalog_list = _catalogs_for_list_params(params)

    cursor_raw = params.get("sa_cursor") or ""
    cursor = urllib.parse.unquote(cursor_raw) if cursor_raw else None

    data, err = fetch_changes(
        country=country,
        change_type="new",
        item_type="show",
        show_type=show_kind,
        catalogs=catalog_list,
        cursor=cursor,
        output_language="en",
    )

    if err == "missing_api_key":
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return
    if err == "unauthorized":
        xbmcgui.Dialog().notification(
            "VOD služby ČR", "Neplatný API klíč (401).", xbmcgui.NOTIFICATION_ERROR, 4000
        )
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return
    if err == "rate_limit":
        xbmcgui.Dialog().notification(
            "VOD služby ČR", "Limit požadavků (429). Zkuste později.", xbmcgui.NOTIFICATION_ERROR, 4000
        )
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return
    if not data:
        xbmcgui.Dialog().notification(
            "VOD služby ČR",
            "Chyba API: %s" % (err or "neznámá"),
            xbmcgui.NOTIFICATION_ERROR,
            4000,
        )
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    changes = data.get("changes") or []
    shows_map = data.get("shows") or {}
    rows = _merge_change_rows(changes, shows_map)
    rows.sort(key=lambda r: int(r.get("vod_ts") or 0), reverse=True)

    if not rows:
        xbmcgui.Dialog().notification(
            "VOD služby ČR", "Žádné novinky pro zvolený typ.", xbmcgui.NOTIFICATION_INFO, 3000
        )
        xbmcplugin.endOfDirectory(handle, succeeded=True, cacheToDisc=False)
        return

    media_type = "tv" if show_kind == "series" else "movie"
    type_label = "Seriály" if show_kind == "series" else "Filmy"
    if len(catalog_list) == 1:
        cat_lbl = STREAMING_CATALOG_LABELS.get(catalog_list[0], catalog_list[0])
        cat_part = " • %s" % cat_lbl
    else:
        cat_part = ""
    xbmcplugin.setPluginCategory(handle, "VOD služby ČR • %s (novinky)%s" % (type_label, cat_part))
    xbmcplugin.setContent(handle, "tvshows" if show_kind == "series" else "movies")

    _seen_tmdb = set()
    _tmdb_ids_batch: List[int] = []
    for _r in rows:
        _tid = _tmdb_id(_r.get("show") or {})
        if _tid and _tid not in _seen_tmdb:
            _seen_tmdb.add(_tid)
            _tmdb_ids_batch.append(_tid)
    log.info(
        f"[VOD SA] řádků={len(rows)} unikátních_tmdb_id={len(_tmdb_ids_batch)} "
        f"(bez TMDb id se cs-CZ z API nedotáhne)"
    )
    if len(rows) > 0 and len(_tmdb_ids_batch) == 0:
        log.error(
            "[VOD SA] Žádné tmdbId u titulů v odpovědi — české názvy z TMDb nelze načíst "
            "(kontrola pole show.tmdbId v datech Streaming API)."
        )
    title_cs_map = _batch_tmdb_cs_titles(media_type, _tmdb_ids_batch)

    try:
        from resources.lib.tmdb.tmdb_kodi import set_video_info
    except Exception as e:
        log.error(f"[VOD SA] set_video_info import: {e}")
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    try:
        from resources.lib.tmdb.tmdb_utils import get_english_title
        _en_ok = True
    except Exception as e:
        log.warning(f"[VOD SA] get_english_title: {e}")
        _en_ok = False

    try:
        from resources.lib.database.metadata_db import db
        _db_ok = True
    except Exception as e:
        log.warning(f"[VOD SA] metadata db: {e}")
        _db_ok = False

    try:
        from resources.lib.utils.context_builder import build_context_menu
        _ctx_ok = True
    except Exception as e:
        log.warning(f"[VOD SA] context_builder: {e}")
        _ctx_ok = False

    directory_items: List[Tuple[str, xbmcgui.ListItem, bool]] = []

    for row in rows:
        show = row.get("show") or {}
        services = row.get("services") or []
        vod_ts = int(row.get("vod_ts") or 0)
        try:
            tmdb_id = _tmdb_id(show)
            api_title = (show.get("title") or "").strip()
            title_orig = (show.get("originalTitle") or "").strip()
            year_txt = _year_text(show, show_kind)
            rating_val = _rating_float(show)
            plot = (show.get("overview") or "").strip()
            poster = _poster_url(show)
            fanart = _fanart_url(show)

            en_title = None
            if _en_ok and tmdb_id:
                try:
                    en_title = get_english_title(media_type, tmdb_id)
                except Exception as e:
                    log.debug(f"[VOD SA] get_english_title tmdb_id={tmdb_id}: {e}")

            if tmdb_id:
                label_cz_raw = (title_cs_map.get(tmdb_id) or "").strip()
            else:
                label_cz_raw = api_title

            label_en = (str(en_title).strip() if en_title else "") or (
                title_orig if _is_latinish(title_orig) else ""
            )
            if not label_en:
                label_en = api_title

            _cz_same = label_cz_raw and label_en and label_cz_raw.lower() == label_en.lower()
            _need_db = (not label_cz_raw) or (not _is_latinish(label_cz_raw)) or _cz_same
            label_cz = label_cz_raw
            if _need_db and _db_ok and tmdb_id:
                try:
                    dbrow = db.get(tmdb_id)
                    db_cs = (dbrow.get("title_cs") or "") if dbrow else ""
                    if (
                        db_cs
                        and _is_latinish(db_cs)
                        and db_cs.lower() != (label_en or "").lower()
                    ):
                        label_cz = db_cs
                except Exception:
                    pass

            if label_cz and _is_latinish(label_cz):
                primary_label = label_cz
                secondary_label = label_en if (label_en and label_en != primary_label) else None
            else:
                primary_label = label_en or (
                    title_orig if _is_latinish(title_orig) else "Bez názvu"
                )
                secondary_label = (
                    label_cz if (label_cz and _is_latinish(label_cz) and label_cz != primary_label) else None
                )

            display_label = primary_label
            if secondary_label and secondary_label != primary_label:
                display_label += " [COLOR grey][%s][/COLOR]" % secondary_label
            if year_txt:
                display_label += " (%s)" % year_txt
            vod_d = _vod_date_ddmmyyyy(vod_ts)
            if vod_d:
                display_label += " [VOD %s]" % vod_d
            if rating_val > 0.0:
                display_label += (
                    " [COLOR=FFFFFFFF]★[/COLOR][COLOR=FFFFD700]%.1f[/COLOR]" % rating_val
                )
            if services:
                display_label += " [COLOR steelblue][%s][/COLOR]" % ", ".join(services)

            li = xbmcgui.ListItem(label=display_label)
            if poster:
                li.setArt({"icon": poster, "thumb": poster, "poster": poster})
            if fanart:
                li.setArt({"fanart": fanart})

            info_labels = {
                "title": primary_label,
                "plot": plot,
                "rating": rating_val,
                "mediatype": "tvshow" if media_type == "tv" else "movie",
            }
            if year_txt and str(year_txt).isdigit():
                info_labels["year"] = int(year_txt)
            premiered_iso = _vod_premiered_iso(vod_ts)
            if premiered_iso:
                info_labels["premiered"] = premiered_iso
            info_labels = {k: v for k, v in info_labels.items() if v not in (None, "")}
            set_video_info(li, info_labels)

            if _db_ok and tmdb_id:
                try:
                    origin_lang = "cs" if (label_cz and primary_label == label_cz) else "en"
                    title_cs_for_db = (
                        primary_label if origin_lang == "cs" else (label_cz if _is_latinish(label_cz) else None)
                    )
                    title_en_for_db = label_en or (
                        primary_label if origin_lang == "en" else None
                    )
                    db.update(
                        tmdb_id,
                        {
                            "media_type": media_type,
                            "title_cs": title_cs_for_db,
                            "title_en": title_en_for_db,
                            "year": year_txt or None,
                        },
                        source="vod_streaming_sa",
                    )
                except Exception as e:
                    log.warning(f"[VOD SA] db.update tmdb_id={tmdb_id}: {e}")

            if _ctx_ok:
                try:
                    ctx_contexts = (
                        ["trakt_tv_default", "tmdb_csfd"]
                        if media_type == "tv"
                        else ["trakt_movies_default", "tmdb_csfd"]
                    )
                    item_data = {"movie_name": primary_label, "tmdb_id": tmdb_id or 0}
                    if tmdb_id:
                        cm = build_context_menu(_build_url_kwargs, item_data, contexts=ctx_contexts)
                        if cm:
                            li.addContextMenuItems(cm, replaceItems=True)
                except Exception as e:
                    log.warning(f"[VOD SA] context: {e}")

            title_cs_for_url = (label_cz if _is_latinish(label_cz) else "") or (
                primary_label if (api_title and primary_label == api_title) else ""
            )
            title_en_for_url = label_en or title_orig or primary_label

            if media_type == "tv" and tmdb_id:
                url = _build_url(
                    {
                        "action": "tmdb_seasons",
                        "tmdb_id": str(tmdb_id),
                        "cz_title": title_cs_for_url or "",
                        "eng_title": title_en_for_url or "",
                        "year": year_txt or "",
                        "mediatype": "tv",
                    }
                )
            elif media_type == "movie":
                url = _build_url(
                    {
                        "action": "show_search_dialog",
                        "cz_title": title_cs_for_url or primary_label,
                        "eng_title": title_en_for_url or primary_label,
                        "year": year_txt or "",
                        "tmdb_id": str(tmdb_id or ""),
                        "mediatype": "movie",
                    }
                )
            else:
                url = _build_url(
                    {
                        "action": "show_search_dialog",
                        "cz_title": title_cs_for_url or primary_label,
                        "eng_title": title_en_for_url or primary_label,
                        "year": year_txt or "",
                        "tmdb_id": "",
                        "mediatype": "movie" if show_kind != "series" else "tv",
                    }
                )

            directory_items.append((url, li, True))
        except Exception as e:
            log.error(f"[VOD SA] render row: {e}")
            continue

    if _db_ok:
        try:
            db.conn.commit()
        except Exception as e:
            log.warning(f"[VOD SA] db commit: {e}")

    if data.get("hasMore") and data.get("nextCursor"):
        next_c = data.get("nextCursor")
        enc = urllib.parse.quote(next_c, safe="")
        next_q: Dict[str, str] = {
            "action": "tmdb_vod_cz_streaming_list",
            "sa_country": country,
            "sa_show_type": show_kind,
            "sa_cursor": enc,
        }
        if len(catalog_list) == 1:
            next_q["sa_catalog"] = catalog_list[0]
        next_url = _build_url(next_q)
        li_next = xbmcgui.ListItem(label="[COLOR gold]Další strana »[/COLOR]")
        li_next.setArt({"icon": "DefaultFolder.png"})
        directory_items.append((next_url, li_next, True))

    xbmcplugin.addDirectoryItems(handle, directory_items)
    # WideList v Estuary (View_55); u jiného skinu může být potřeba jiné číslo.
    xbmc.executebuiltin("Container.SetViewMode(55)")
    log.info(
        f"TMDB_PROFILER: show_vod_cz_streaming_list {time.perf_counter() - start:.3f}s "
        f"rows={len(directory_items)}"
    )
    xbmcplugin.endOfDirectory(handle, succeeded=True, cacheToDisc=False)
