# -*- coding: utf-8 -*-
import os
import json
import hashlib
import urllib.parse
import xbmc
import xbmcgui
import xbmcplugin
from collections import defaultdict
from datetime import datetime, date, timedelta, timezone
from xbmcvfs import translatePath
from resources.lib.utils.log_utils import get_logger
from resources.lib.tmdb.tmdb_kodi import set_video_info

log = get_logger()

class TVProgram:
    """
    Znovupoužitelný modul pro prohlížení EPG v Kodi.

    Inicializace (v hostitelském pluginu):
        TV = TVProgram(
            handle=_handle,
            base_url=_url,
            addon_id=ADDON_ID,
            addon_path=translatePath(_addon.getAddonInfo("path")),
            addon_data_path=_profile,
            json_file=EPG_JSON,
            route_prefix="tvprog",
            use_colors=True  # / False podle nastavení
        )

    V routeru mapuj:
        tvprog_root, tvprog_channels, tvprog_channel,
        tvprog_now, tvprog_genres, tvprog_genre, tvprog_info
    """

    def __init__(
        self,
        handle: int,
        base_url: str,
        addon_id: str,
        addon_path: str,
        addon_data_path: str,
        json_file: str,
        route_prefix: str = "tvprog",
        use_colors: bool = True,
    ):
        self.handle = handle
        self.base_url = base_url
        self.addon_id = addon_id
        self.addon_path = addon_path
        self.addon_data_path = addon_data_path
        self.json_file = json_file
        self.route_prefix = route_prefix
        self.use_colors = use_colors

        self.genre_icons_path = os.path.join(self.addon_path, "resources", "media", "genres")
        self._EPG_CACHE = {}  # (day_iso, mtime) -> dict

    # ----------------- Utils -----------------
    # --- Žánrové aliasy: SK -> CS (rozšiřitelný slovník) ---


    GENRE_ALIAS_SK2CS = {
        "správy": "Zprávy",
        "spravodajstvo": "Zpravodajství",  # ← nový
        "publicistika": "Publicistika",
        "relácia": "Pořad",
        "seriál": "Seriál",
        "film": "Film",
        "dokumentárne": "Dokument",
        "dokument": "Dokument",
        "šport": "Sport",
        "zábava": "Zábava",
        "hudba": "Hudba",
        "komédia": "Komedie",
        "drama": "Drama",
        "dráma": "Drama",
        "rodinné": "Rodinné",
        "deti": "Děti",
        "detský": "Pro děti",
        "detské": "Pro děti",
        "krimi": "Krimi",
        "thriller": "Thriller",
        "horor": "Horor",
        "historické": "Historické",
        "história": "Historie",
        "lifestyle": "Lifestyle",
        "varenie": "Vaření",
        "cestovanie": "Cestování",
        "príroda": "Příroda",
        "technológie": "Technologie",
        "náboženstvo": "Náboženství",
        "rozprávka": "Pohádka",
        "akcia": "Akce",
        "dobrodružstvo": "Dobrodružství",
        "animovaný": "Animovaný",
        "fantastika": "Fantasy",
        "vojnový": "Válečný"  # ← nový
    }

    def _genre_key_norm(self, raw: str) -> str:
        """Vytvoří stabilní klíč žánru (lower, bez diakritiky mezer)."""
        if not raw:
            return ""
        s = str(raw).strip().lower()
        # normalizace pomlček/mezery – klíč držíme jednoduše v lower
        return s

    def _genre_display_cs(self, raw: str) -> str:
        """Vrátí český display pro žánr, pokud známe alias; jinak původní text s první velkou."""
        if not raw:
            return ""
        key = str(raw).strip().lower()
        cs = self.GENRE_ALIAS_SK2CS.get(key)  # ← TADY je změna
        if cs:
            return cs
        return str(raw).strip().capitalize()


    def route(self, name: str) -> str:
        return f"{self.route_prefix}_{name}"

    def build_url(self, **params) -> str:
        # používáme parametr 'action' (kompatibilní se zbytkem pluginu)
        return f"{self.base_url}?{urllib.parse.urlencode(params)}"

    @staticmethod
    def local_tz():
        now = datetime.now()
        return timezone(now - datetime.utcnow())

    @classmethod
    def day_bounds_local(cls, d: date):
        tz = cls.local_tz()
        start = datetime(d.year, d.month, d.day, 0, 0, 0, tzinfo=tz)
        return start, start + timedelta(days=1)

    @staticmethod
    def _fix_offset_colon(s: str):
        if not s:
            return s
        # +0100 -> +01:00 / -0330 -> -03:30
        if len(s) >= 5 and (s[-5] in ["+", "-"]) and s[-2:].isdigit() and s[-4:-2].isdigit() and s[-3] != ":":
            return s[:-5] + s[-5:-2] + ":" + s[-2:]
        return s

    @classmethod
    def parse_isoaware(cls, s):
        if not s:
            return None
        try:
            s = s.strip().replace("Z", "+00:00").replace(" ", "T")
            s = cls._fix_offset_colon(s)
            # doplnění sekund, je-li jen HH:MM(+/-TZ)
            if "T" in s:
                tpart = s.split("T", 1)[1]
                off_pos = max(tpart.rfind("+"), tpart.rfind("-"))
                core = tpart if off_pos <= 0 else tpart[:off_pos]
                if len(core) == 5 and core.count(":") == 1:
                    s = s.replace(core, core + ":00", 1)
            dt = datetime.fromisoformat(s)
            return dt if dt.tzinfo is not None else None
        except Exception as e:
            log.warning(f"Chyba parsování datetime '{s}': {e}")
            return None

    @staticmethod
    def overlaps(a_start, a_end, b_start, b_end):
        # polootevřené intervaly [a_start, a_end) a [b_start, b_end)
        return (a_start is not None and a_end is not None and b_start < a_end and a_start < b_end)

    @staticmethod
    def fmt_hm(dt):
        try:
            return dt.strftime("%H:%M")
        except Exception:
            return "??:??"

    @staticmethod
    def normalize_channel_key(raw):
        return str(raw).strip().lower()

    @staticmethod
    def present_channel_name(key: str):
        return key.replace("_", " ").title()

    def safe_icon_path(self, icon_path):
        if not icon_path:
            return None
        if isinstance(icon_path, str) and icon_path.startswith("special://"):
            full = translatePath(icon_path)
            return full if os.path.exists(full) else None
        if os.path.isabs(icon_path):
            return icon_path if os.path.exists(icon_path) else None
        rel = icon_path.lstrip("/\\")
        full = os.path.normpath(os.path.join(self.addon_data_path, rel))
        return full if os.path.exists(full) else None

    def genre_icon_for(self, gkey: str, display: str):
        if not os.path.isdir(self.genre_icons_path):
            return None
        candidates = []
        if gkey:
            candidates += [gkey, gkey.replace(" ", "_"), gkey.replace(" ", "")]
        if display:
            d = display.strip().lower()
            candidates += [d, d.replace(" ", "_"), d.replace(" ", "")]
        tried = set()
        for base in candidates:
            if not base:
                continue
            for ext in ("png", "jpg", "jpeg", "webp"):
                p = os.path.join(self.genre_icons_path, f"{base}.{ext}")
                if p in tried:
                    continue
                tried.add(p)
                if os.path.exists(p):
                    return p
        return None

    def parse_day_param(self, day_str: str) -> date:
        if not day_str:
            return date.today()
        try:
            return datetime.fromisoformat(day_str).date()
        except Exception:
            return date.today()

    # ------- Color helpers (respektují use_colors) -------

    def color(self, text: str, argb_hex: str) -> str:
        if not self.use_colors or not text:
            return text
        return f"[COLOR {argb_hex}]{text}[/COLOR]"

    def color_named(self, text: str, name: str) -> str:
        if not self.use_colors or not text:
            return text
        return f"[COLOR {name}]{text}[/COLOR]"

    def green_count_label(self, name: str, count: int) -> str:
        # "Název (počet)" – s barvou, když je povolená
        ct = f"({count})"
        if self.use_colors:
            ct = self.color_named(ct, "lightgreen")
        return f"{name} {ct}"

    # ----------------- EPG načtení + sloučení -----------------

    def get_epg_data(self, day: date = None):
        if day is None:
            day = date.today()

        if not os.path.exists(self.json_file):
            log.warning(f"EPG soubor nenalezen: {self.json_file}")
            return {}, day

        try:
            mtime = os.path.getmtime(self.json_file)
        except Exception:
            mtime = None

        cache_key = (day.isoformat(), mtime)
        if cache_key in self._EPG_CACHE:
            return self._EPG_CACHE[cache_key], day

        try:
            with open(self.json_file, "r", encoding="utf-8") as f:
                raw = json.load(f)
        except Exception as e:
            log.error(f"Chyba při čtení JSON: {e}")
            return {}, day

        day_start, day_end = self.day_bounds_local(day)

        # podpora dict i střídajícího listu
        if isinstance(raw, dict):
            iterable = list(raw.items())
        elif isinstance(raw, list):
            iterable = []
            i = 0
            while i < len(raw):
                key = raw[i] if i < len(raw) else None
                progs = raw[i + 1] if i + 1 < len(raw) else None
                i += 2
                if isinstance(key, str) and isinstance(progs, list):
                    iterable.append((key, progs))
                else:
                    log.warning(f"Neočekávaný pár v EPG listu – přeskočeno ({type(key)}, {type(progs)})")
        else:
            log.error(f"Neočekávaná struktura EPG: {type(raw)}")
            return {}, day

        # agregace všech bloků stejného kanálu
        acc = defaultdict(list)

        for ch_raw, programs in iterable:
            if not isinstance(ch_raw, str) or not isinstance(programs, list):
                continue
            ch_key = self.normalize_channel_key(ch_raw)
            parsed = []
            for p in programs:
                if not isinstance(p, dict):
                    continue
                st = self.parse_isoaware(p.get("Start"))
                en = self.parse_isoaware(p.get("Stop"))
                if not st or not en:
                    continue
                if self.overlaps(st, en, day_start, day_end):
                    q = dict(p)
                    q["_start"] = st
                    q["_stop"] = en
                    parsed.append(q)
            if parsed:
                acc[ch_key].extend(parsed)

        # deduplikace + řazení
        epg = {}
        for ch_key, items in acc.items():
            seen = set()
            uniq = []
            for p in items:
                st = p.get("_start")
                en = p.get("_stop")
                t = (p.get("Title") or "") + "|" + (p.get("Subtitle") or "")
                k = (int(st.timestamp()) if st else 0, int(en.timestamp()) if en else 0, t)
                if k in seen:
                    continue
                seen.add(k)
                uniq.append(p)
            uniq.sort(key=lambda x: x["_start"])
            epg[ch_key] = uniq

        self._EPG_CACHE[cache_key] = epg
        log.info(f"ÚSPĚCH: Kanálů s programem: {len(epg)} pro {day.strftime('%d.%m.%Y')}")
        return epg, day

    # ----------------- UI: kořen -----------------

    def show_root(self):
        from resources.lib.utils.ui_utils import pick_icon_path, add_menu_item
        base = self.base_url
        today = date.today()

        # Funkce, kterou předáme utilitě pro stavbu URL
        def _builder(params_dict):
            return self.build_url(**params_dict)

        # Pomocník pro výběr ikony (z addon_path)
        def _icon(file_name, kodi_fallback):
            return pick_icon_path(self.addon_path, file_name, kodi_fallback)

        # Dnes
        add_menu_item(
            handle=self.handle,
            build_url_fn=_builder,
            label=f"[B]Dnes[/B] ({today.strftime('%d.%m.%Y')})",
            plot="Zobrazí program všech kanálů pro dnešní den. Položky lze otevřít na seznam pořadů kanálu, nebo filtrovat podle žánrů.",
            action=self.route("channels"),
            art_icon=_icon("today.png", "DefaultCalendar.png"),
            is_folder=True,
            day=today.isoformat()
        )

        # Zítra
        add_menu_item(
            handle=self.handle,
            build_url_fn=_builder,
            label=f"[B]Zítra[/B] ({(today + timedelta(days=1)).strftime('%d.%m.%Y')})",
            plot="Zobrazí program na zítřek. Data EPG se udržují tak, aby byl k dispozici minimálně následující den.",
            action=self.route("channels"),
            art_icon=_icon("tomorrow.png", "DefaultCalendar.png"),
            is_folder=True,
            day=(today + timedelta(days=1)).isoformat()
        )

        # Aktuálně běží
        add_menu_item(
            handle=self.handle,
            build_url_fn=_builder,
            label="[B]Aktuálně běží[/B]",
            plot="Okamžitý přehled pořadů, které právě běží na jednotlivých kanálech. Kliknutí otevře výsledky hledání daného pořadu.",
            action=self.route("now"),
            art_icon=_icon("now.png", "DefaultInProgressShows.png"),
            is_folder=True,
            day=today.isoformat()
        )

        # Žánry (Dnes)
        add_menu_item(
            handle=self.handle,
            build_url_fn=_builder,
            label="[B]Žánry (Dnes)[/B]",
            plot="Přehled žánrů z dnešního programu napříč všemi kanály. Po výběru uvidíš stanici, čas a název pořadu.",
            action=self.route("genres"),
            art_icon=_icon("genres.png", "DefaultGenre.png"),
            is_folder=True,
            day=today.isoformat()
        )

        xbmcplugin.endOfDirectory(self.handle, cacheToDisc=False)

    # -------- UI: seznam kanálů pro zvolený den --------

    def show_channels_by_date(self, day_str):
        d = self.parse_day_param(day_str)
        epg, display_date = self.get_epg_data(d)
        if not epg:
            xbmcgui.Dialog().ok("Chyba", f"Žádný program ({display_date.strftime('%d.%m.%Y')})")
            return

        xbmcplugin.setPluginCategory(self.handle, f"TV Program – {display_date.strftime('%d.%m.%Y')}")
        xbmcplugin.setContent(self.handle, "videos")
        xbmcplugin.addSortMethod(self.handle, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE)

        # podmenu: žánry pro tento den
        li = xbmcgui.ListItem("[B]Filtrovat podle žánru…[/B]")
        url = self.build_url(action=self.route("genres"), day=display_date.isoformat())
        xbmcplugin.addDirectoryItem(self.handle, url, li, isFolder=True)

        for ch_key in sorted(epg.keys(), key=lambda x: x.replace("_", " ")):
            name = self.present_channel_name(ch_key)
            count = len(epg[ch_key])
            label = self.green_count_label(name, count)
            li = xbmcgui.ListItem(label)
            url = self.build_url(action=self.route("channel"), ch=ch_key, day=display_date.isoformat())
            xbmcplugin.addDirectoryItem(self.handle, url, li, isFolder=True)

        xbmcplugin.endOfDirectory(self.handle, cacheToDisc=False)

    # -------- UI: pořady v kanálu --------

    def show_channel_programs(self, ch_key, day_str):
        d = self.parse_day_param(day_str)
        epg, _ = self.get_epg_data(d)
        progs = epg.get(self.normalize_channel_key(ch_key), [])
        if not progs:
            xbmcgui.Dialog().ok("Prázdno", f"V tento den není program ({d.strftime('%d.%m.%Y')})")
            return

        name = self.present_channel_name(ch_key)
        xbmcplugin.setPluginCategory(self.handle, f"{name} – {d.strftime('%d.%m.%Y')}")
        xbmcplugin.setContent(self.handle, "videos")

        now = datetime.now(self.local_tz())

        # fokus: běžící -> první budoucí -> poslední
        focus = 0
        running = None
        future = None
        for i, p in enumerate(progs):
            st, en = p.get("_start"), p.get("_stop")
            if st and en and st <= now < en:
                running = i
                break
        if running is not None:
            focus = running
        else:
            for i, p in enumerate(progs):
                st = p.get("_start")
                if st and st >= now:
                    future = i
                    break
            focus = future if future is not None else (len(progs) - 1)

        for i, p in enumerate(progs):
            st, en = p.get("_start"), p.get("_stop")
            start_time = self.fmt_hm(st)
            stop_time = self.fmt_hm(en)

            title = p.get("Title", "Bez názvu")
            subtitle = p.get("Subtitle") or ""
            title_clean = f"{title}" + (f": {subtitle}" if subtitle else "")

            # časové rozpětí – modře, pokud barvy povoleny
            time_span = f"{start_time}-{stop_time}"
            time_span = self.color(time_span, "ff3399ff")

            full_title = f"{time_span}  {title_clean}"
            label = f"[B]{full_title}[/B]"

            li = xbmcgui.ListItem(label)
            li.setLabel(label)
            li.setLabel2(f"{start_time}-{stop_time}")

            genres = p.get("Genres") or []
            set_video_info(li, {
                "title": full_title,  # barvy i v InfoTag.Title (kvůli skinům)
                "originaltitle": title_clean,
                "plot": p.get("Description", ""),
                "genre": " • ".join(genres),
            })

            icon = self.safe_icon_path(p.get("Icon"))
            if icon:
                li.setArt({"thumb": icon, "icon": icon})

            li.setProperty("IsPlayable", "false")

            # Přidáme logování názvu pořadu
            log.info(f"Vyhledávám na Webshare: {title}")

            # Změníme URL na search s parametrem what=title
            url = self.build_url(action="search", what=title)
            li.setPath(url)

            # Změníme isFolder na True, aby Kodi otevřelo nový seznam
            xbmcplugin.addDirectoryItem(self.handle, url, li, isFolder=True)
            if i == focus:
                li.select(True)

        xbmcplugin.addSortMethod(self.handle, xbmcplugin.SORT_METHOD_NONE)
        xbmcplugin.endOfDirectory(self.handle, cacheToDisc=False)

    # -------- UI: žánry --------




    def collect_genres(self, epg):
        if not epg or not isinstance(epg, dict):
            return {}
        out = {}
        for items in epg.values():
            for p in items:
                glist = p.get("Genres") or []
                if not isinstance(glist, list):
                    continue
                for g in glist:
                    if not g:
                        continue
                    key = self._genre_key_norm(g)
                    if not key:
                        continue
                    disp_cs = self._genre_display_cs(g)
                    if key not in out:
                        out[key] = {"display": disp_cs, "count": 1}
                    else:
                        out[key]["count"] += 1
        return out

    def show_genres(self, day_str):
        d = self.parse_day_param(day_str)
        epg, display_date = self.get_epg_data(d)
        if not epg or not isinstance(epg, dict):
            xbmcgui.Dialog().ok("Chyba", f"Žádný program ({display_date.strftime('%d.%m.%Y')})")
            return

        xbmcplugin.setPluginCategory(self.handle, f"Žánry – {display_date.strftime('%d.%m.%Y')}")
        xbmcplugin.setContent(self.handle, "videos")

        genres = self.collect_genres(epg)
        if not genres:
            xbmcgui.Dialog().ok("Žánry", "Pro tento den nejsou dostupné žádné žánry.")
            return

        for gkey, obj in sorted(genres.items(), key=lambda kv: kv[1]["display"].lower()):
            disp = obj["display"]
            count = obj["count"]
            label = self.green_count_label(disp, count)
            li = xbmcgui.ListItem(label)
            icon = self.genre_icon_for(gkey, disp)
            if icon:
                li.setArt({"thumb": icon, "icon": icon})
            url = self.build_url(action=self.route("genre"), g=gkey, day=display_date.isoformat())
            xbmcplugin.addDirectoryItem(self.handle, url, li, isFolder=True)

        xbmcplugin.endOfDirectory(self.handle, cacheToDisc=False)

    def show_programs_by_genre(self, day_str, genre_key):
        d = self.parse_day_param(day_str)
        epg, _ = self.get_epg_data(d)
        if not epg:
            xbmcgui.Dialog().ok("Chyba", "Žádný program k dispozici")
            return

        gkey = self._genre_key_norm(genre_key)
        if not gkey:
            xbmcgui.Dialog().ok("Žánry", "Neplatný žánr")
            return

        matches = []
        for ch_key, items in epg.items():
            for idx, p in enumerate(items):
                glist = p.get("Genres") or []
                glkeys = {self._genre_key_norm(g) for g in glist if g}
                if gkey in glkeys:
                    matches.append((ch_key, idx, p))

        if not matches:
            xbmcgui.Dialog().ok("Žánry", "Pro tento žánr dnes nic není.")
            return

        disp_name_cs = self._genre_display_cs(genre_key)
        xbmcplugin.setPluginCategory(self.handle, f"Žánr: {disp_name_cs} – {d.strftime('%d.%m.%Y')}")
        xbmcplugin.setContent(self.handle, "videos")

        matches.sort(key=lambda t: t[2]["_start"])
        for ch_key, idx, p in matches:
            st, en = p.get("_start"), p.get("_stop")
            start_time = self.fmt_hm(st)
            stop_time = self.fmt_hm(en)
            channel_name = self.present_channel_name(ch_key)
            title = p.get("Title", "Bez názvu")
            subtitle = p.get("Subtitle") or ""
            title_clean = f"{title}" + (f": {subtitle}" if subtitle else "")
            channel_txt = channel_name
            time_txt = f"{start_time}-{stop_time}"
            channel_col = self.color(channel_txt, "ffff3333")
            time_col = self.color(time_txt, "ff3399ff")
            full_title = f"{channel_col} • {time_col} {title_clean}"
            label = f"[B]{full_title}[/B]"
            li = xbmcgui.ListItem(label)
            li.setLabel(label)
            genres = p.get("Genres") or []
            genres_cs = [self._genre_display_cs(g) for g in genres]
            set_video_info(li, {
                "title": full_title,
                "originaltitle": f"{title_clean} – {channel_name}",
                "plot": p.get("Description", ""),
                "genre": " • ".join(genres_cs),
            })
            icon = self.safe_icon_path(p.get("Icon"))
            if icon:
                li.setArt({"thumb": icon, "icon": icon})
            li.setProperty("IsPlayable", "false")
            url = self.build_url(action="search", what=title)
            li.setPath(url)
            xbmcplugin.addDirectoryItem(self.handle, url, li, isFolder=True)

        xbmcplugin.addSortMethod(self.handle, xbmcplugin.SORT_METHOD_NONE)
        xbmcplugin.endOfDirectory(self.handle, cacheToDisc=False)

    # -------- UI: „Aktuálně běží“ --------

    def show_now_playing(self, day_str):
        d = self.parse_day_param(day_str)
        epg, _ = self.get_epg_data(d)
        if not epg:
            xbmcgui.Dialog().ok("Chyba", "Žádný program k dispozici")
            return

        now = datetime.now(self.local_tz())
        xbmcplugin.setPluginCategory(self.handle, f"Aktuálně běží – {d.strftime('%d.%m.%Y')}")
        xbmcplugin.setContent(self.handle, "videos")

        running = []
        for ch_key, items in epg.items():
            for idx, p in enumerate(items):
                st, en = p.get("_start"), p.get("_stop")
                if st and en and st <= now < en:
                    running.append((ch_key, idx, p))
                    break  # jen jeden běžící na kanál

        if not running:
            xbmcgui.Dialog().ok("Info", "Aktuálně nic neběží.")
            return

        running.sort(key=lambda x: x[2]["_start"])

        for ch_key, idx, p in running:
            st, en = p.get("_start"), p.get("_stop")
            start_time = self.fmt_hm(st)
            stop_time = self.fmt_hm(en)
            channel_name = self.present_channel_name(ch_key)

            title = p.get("Title", "Bez názvu")
            subtitle = p.get("Subtitle") or ""
            title_clean = f"{title}" + (f": {subtitle}" if subtitle else "")

            channel_txt = channel_name
            time_txt = f"{start_time}-{stop_time}"

            channel_col = self.color(channel_txt, "ffff3333")  # červeně stanice
            time_col = self.color(time_txt, "ff3399ff")        # modře čas

            full_title = f"{channel_col} • {time_col}  {title_clean}"
            label = f"[B]{full_title}[/B]"

            li = xbmcgui.ListItem(label)
            li.setLabel(label)
            li.setLabel2(f"{start_time}-{stop_time}")

            genres = p.get("Genres") or []
            set_video_info(li, {
                "title": full_title,
                "originaltitle": f"{channel_name} – {title_clean}",
                "plot": p.get("Description", ""),
                "genre": " • ".join(genres),
            })

            icon = self.safe_icon_path(p.get("Icon"))
            if icon:
                li.setArt({"thumb": icon, "icon": icon})

            li.setProperty("IsPlayable", "false")

            # Přidáme logování názvu pořadu
            log.info(f"Vyhledávám na Webshare: {title}")

            # Změníme URL na search s parametrem what=title
            url = self.build_url(action="search", what=title)
            li.setPath(url)

            # Změníme isFolder na True, aby Kodi otevřelo nový seznam
            xbmcplugin.addDirectoryItem(self.handle, url, li, isFolder=True)
        xbmcplugin.addSortMethod(self.handle, xbmcplugin.SORT_METHOD_NONE)
        xbmcplugin.endOfDirectory(self.handle, cacheToDisc=False)

    # -------- UI: detail pořadu --------

    def show_program_info(self, ch_key, idx_str, day_str):
        d = self.parse_day_param(day_str)
        try:
            idx = int(idx_str)
        except Exception:
            xbmcgui.Dialog().ok("Chyba", "Neplatný index")
            return

        epg, _ = self.get_epg_data(d)
        progs = epg.get(self.normalize_channel_key(ch_key), [])
        if not progs or not (0 <= idx < len(progs)):
            xbmcgui.Dialog().ok("Chyba", "Položka nenalezena")
            return

        p = progs[idx]
        st, en = p.get("_start"), p.get("_stop")
        start_time = st.strftime("%Y-%m-%d %H:%M") if st else "?"
        stop_time = en.strftime("%Y-%m-%d %H:%M") if en else "?"

        title = p.get("Title", "Bez názvu")
        subtitle = p.get("Subtitle") or ""
        desc = p.get("Description") or ""
        genres = ", ".join(p.get("Genres") or "")

        lines = [
            f"[B]{title}[/B]" + (f" – {subtitle}" if subtitle else ""),
            f"[I]{start_time} – {stop_time}[/I]",
        ]
        if genres:
            lines.append(genres)
        if desc:
            lines += ["", desc]

        xbmcgui.Dialog().textviewer("Detail pořadu", "\n".join(lines))