# -*- coding: utf-8 -*-
import xbmc
import xbmcgui
import xbmcplugin
import xbmcaddon
import sys
import urllib.parse
from datetime import date, datetime
from resources.lib.utils.ui_utils import pick_icon as ui_pick_icon
from resources.lib.tmdb.tmdb_utils import get_english_title
from resources.lib.utils.log_utils import get_logger
from resources.lib.utils.context_builder import style_context_menu

log = get_logger()

import time
import concurrent.futures
from resources.lib.tmdb.tmdb_module import TMDb, API_URL, IMAGE_BASE_URL_POSTER
from resources.lib.tmdb.omdb import build_label2
from resources.lib.tmdb.tmdb_series_detail import show_seasons, show_episodes
from resources.lib.cache.menu_cache_server import get_menu_cache_server
from resources.lib.cache.menu_cache_config import (
    is_cache_enabled,
    get_cache_ttl,
    log_cache_event,
    kodi_cache_to_disc_for_menu,
)

from resources.lib.database.metadata_db import db
from typing import Optional

# Společné utilitky pro ikonky a položky s plot

# --- TMDb Konstanty pro obrázky ---
IMAGE_BASE_URL = 'https://image.tmdb.org/t/p/w500' 
FANART_BASE_URL = 'https://image.tmdb.org/t/p/original' 
PERSON_NEXTCALL_PROPERTY = 'plugin.video.mmirousek_v3.person-nextcall'
PERSON_ACTION_PROPERTY = 'plugin.video.mmirousek_v3.person-action'

# Standardní Kodi setup pro práci s doplňkem
addon_handle = int(sys.argv[1])
addon = xbmcaddon.Addon('plugin.video.mmirousek_v3')
base_url = sys.argv[0]

# --- build_url: původní funkce ---
def build_url(query):
    """Pomocná funkce pro sestavení URL, kterou Kodi pošle zpět do routeru."""
    return base_url + '?' + urllib.parse.urlencode(query)

def build_url_kwargs(**kwargs):
    """Wrapper kolem build_url, který akceptuje **kwargs (dict-like)."""
    return build_url(kwargs)


def _current_plugin_folder_url() -> str:
    """Vrátí aktuální URL listu, který je právě renderován tímto router voláním."""
    try:
        query = sys.argv[2] if len(sys.argv) > 2 else ''
        query = str(query or '').strip()
        if query and not query.startswith('?'):
            query = '?' + query
        return f"{base_url}{query}"
    except Exception:
        return xbmc.getInfoLabel('Container.FolderPath') or ''


def _append_menu_item(directory_items: list, label: str, plot: str, action: str,
                      art_icon: str = None, is_folder: bool = True, **params) -> None:
    """Přidá jednu položku menu do bufferu pro hromadné renderování."""
    li = xbmcgui.ListItem(label=label)
    set_video_info(li, {'title': label, 'plot': plot})
    if art_icon:
        li.setArt({'icon': art_icon, 'thumb': art_icon})
    url = build_url({'action': action, **params})
    directory_items.append((url, li, is_folder))


def _render_cached_menu(cache_key: str, directory_items: list) -> bool:
    """Pokusí se vykreslit menu z cache a vrátí True při HIT."""
    if not is_cache_enabled(cache_key):
        return False

    cached = get_menu_cache_server().get_cached_menu(cache_key)
    if not cached:
        log_cache_event('MISS', cache_key)
        return False

    for item in cached.get('items', []):
        li = xbmcgui.ListItem(label=item.get('label', ''))
        info = item.get('info') or {}
        art = item.get('art') or {}
        props = item.get('properties') or {}
        cm = item.get('context') or []

        if info:
            set_video_info(li, info)
        if art:
            li.setArt(art)
        for k, v in props.items():
            li.setProperty(str(k), str(v))
        if cm:
            cm_norm = []
            for c in cm:
                if isinstance(c, (list, tuple)) and len(c) >= 2:
                    cm_norm.append((str(c[0]), str(c[1])))
            if cm_norm:
                cm_norm = style_context_menu(cm_norm)
                li.addContextMenuItems(cm_norm, replaceItems=True)

        directory_items.append((item.get('url', ''), li, bool(item.get('isFolder', True))))

    log_cache_event('HIT', cache_key, f"{cached.get('count', len(directory_items))} items")
    xbmcplugin.addDirectoryItems(addon_handle, directory_items)
    xbmcplugin.endOfDirectory(
        addon_handle, cacheToDisc=kodi_cache_to_disc_for_menu(cache_key)
    )
    return True


def _save_menu_cache(cache_key: str, directory_items: list) -> None:
    """Uloží právě sestavené menu do MenuCacheServer."""
    if not is_cache_enabled(cache_key):
        return

    serializable = []
    for url, li, is_folder in directory_items:
        art = {}
        if hasattr(li, 'getArt'):
            for key in ('icon', 'thumb', 'poster', 'fanart'):
                try:
                    val = li.getArt(key)
                except Exception:
                    val = ''
                if val:
                    art[key] = val

        serializable.append((
            url,
            li.getLabel(),
            bool(is_folder),
            {'title': li.getLabel()},
            art,
            {},
            [],
            False,
        ))

    get_menu_cache_server().cache_menu(cache_key, serializable, get_cache_ttl(cache_key))

# Kategorie (zachovány přesně)
MOVIE_CATEGORIES = {
    'movie_popular': 'Populární filmy',
    'movie_top_rated': 'Nejlépe hodnocené filmy',
    'movie_upcoming': 'Připravované filmy',
}

TV_CATEGORIES = {
    'tv_popular': 'Populární seriály',
    'tv_top_rated': 'Nejlépe hodnocené seriály',
    'tv_on_the_air': 'Právě vysílané (týden)',
    'tv_airing_today': 'Vysílané dnes',
}
PERSON_CATEGORIES = {
    'person_popular': 'Populární osoby (herci, tvůrci)',
    'person_trending_day': 'Trendy osoby dnes',
    'person_trending_week': 'Trendy osoby týdne',
}


def set_video_info(li, info_labels: dict):
    """
    Bezpečně nastaví video metadata. Převezme existující slovník info_labels
    a nastaví jednotlivá pole přes InfoTagVideo, aby zmizel deprekační warning.
    Funguje pro filmy/seriály/epizody (typuje se podle 'mediatype').
    """
    try:
        tag = li.getVideoInfoTag()
    except Exception:
        # fallback (nemělo by nastat v nových Kodi), ale ať nic nespadne
        try:
            li.setInfo('Video', info_labels or {})
        except Exception:
            pass
        return

    if not isinstance(info_labels, dict):
        return

    # --- Title / Plot / PlotOutline / Tagline ---
    title = info_labels.get('title')
    originaltitle = info_labels.get('originaltitle')
    tvshowtitle = info_labels.get('tvshowtitle')
    plot = info_labels.get('plot')
    plotoutline = info_labels.get('plotoutline')
    tagline = info_labels.get('tagline')

    if title:
        tag.setTitle(str(title))
    if originaltitle:
        try:
            tag.setOriginalTitle(str(originaltitle))
        except Exception:
            pass
    if tvshowtitle:
        try:
            tag.setTvShowTitle(str(tvshowtitle))
        except Exception:
            pass
    if plot:
        tag.setPlot(str(plot))
    elif plotoutline:
        # pokud máš jen outline, použij ho jako plot
        tag.setPlot(str(plotoutline))
    # Tagline – některé skiny ho zobrazují
    if tagline:
        try:
            tag.setTagLine(str(tagline))
        except Exception:
            # starší verze nemusí mít setter – ignoruj
            pass

    # --- Media type ---
    mtype = info_labels.get('mediatype')
    if isinstance(mtype, str) and mtype:
        tag.setMediaType(mtype)

    # --- Year / Rating / Votes ---
    year = info_labels.get('year')
    rating = info_labels.get('rating')
    votes = info_labels.get('votes') or info_labels.get('imdbvotes')

    try:
        if isinstance(year, int):
            tag.setYear(year)
        elif isinstance(year, str) and year.isdigit():
            tag.setYear(int(year))
    except Exception:
        pass

    try:
        if rating is not None:
            tag.setRating(float(rating))
    except Exception:
        pass

    try:
        if votes is not None:
            tag.setVotes(int(votes))
    except Exception:
        pass

    # --- Season/Episode (pokud je to epizoda) ---
    season = info_labels.get('season')
    episode = info_labels.get('episode')
    if season is not None:
        try:
            tag.setSeason(int(season))
        except Exception:
            pass
    if episode is not None:
        try:
            tag.setEpisode(int(episode))
        except Exception:
            pass

    # --- Duration (minuty) ---
    duration = info_labels.get('duration')
    try:
        if isinstance(duration, int) and duration > 0:
            tag.setDuration(duration)
        elif isinstance(duration, str) and duration.isdigit():
            tag.setDuration(int(duration))
    except Exception:
        pass

    # --- Datum (správný setter podle typu) ---
    # Pozn.: pro epizody používáme setFirstAired(),
    # pro filmy/TV show setPremiered() (pokud někdy vyloženě předáváš 'aired')
    aired = info_labels.get('aired')      # 'YYYY-MM-DD'
    premiered = info_labels.get('premiered') or info_labels.get('released')  # někdy jiný klíč
    try:
        if mtype == 'episode' and aired:
            tag.setFirstAired(aired)           # epizody
        elif premiered:
            tag.setPremiered(premiered)        # filmy/TV show
        elif mtype == 'movie' and aired:
            tag.setPremiered(aired)            # fallback pro filmy
    except Exception:
        pass

    # --- Genres / Directors / Writers (pokud je někde předáváš do dictu) ---
    genres = info_labels.get('genre') or info_labels.get('genres')
    if isinstance(genres, (list, tuple)) and genres:
        try:
            tag.setGenres([str(g) for g in genres if g])
        except Exception:
            pass
    elif isinstance(genres, str) and genres:
        try:
            tag.setGenres([g.strip() for g in genres.split(',') if g.strip()])
        except Exception:
            pass

    countries = info_labels.get('countries') or info_labels.get('country')
    if isinstance(countries, (list, tuple)) and countries:
        try:
            tag.setCountries([str(c) for c in countries if c])
        except Exception:
            pass
    elif isinstance(countries, str) and countries:
        try:
            tag.setCountries([c.strip() for c in countries.split('/') if c.strip()])
        except Exception:
            pass

    directors = info_labels.get('director') or info_labels.get('directors')
    if isinstance(directors, (list, tuple)) and directors:
        try:
            tag.setDirectors([str(d) for d in directors if d])
        except Exception:
            pass
    elif isinstance(directors, str) and directors:
        try:
            tag.setDirectors([d.strip() for d in directors.split(',') if d.strip()])
        except Exception:
            pass

    writers = info_labels.get('writer') or info_labels.get('writers')
    if isinstance(writers, (list, tuple)) and writers:
        try:
            tag.setWriters([str(w) for w in writers if w])
        except Exception:
            pass
    elif isinstance(writers, str) and writers:
        try:
            tag.setWriters([w.strip() for w in writers.split(',') if w.strip()])
        except Exception:
            pass

    # --- IMDB number (pokud někde v dictu máš) ---
    imdb_id = info_labels.get('imdbnumber') or info_labels.get('imdb_id')
    if isinstance(imdb_id, str) and imdb_id:
        try:
            tag.setIMDBNumber(imdb_id)
        except Exception:
            pass

    playcount = info_labels.get('playcount')
    if playcount is not None:
        try:
            tag.setPlaycount(int(playcount))
        except Exception:
            pass

    # Hotovo: žádné li.setInfo() se nevolá → zmizí warning a hodnoty jsou
    # nastavené přes InfoTagVideo per-field settery.


# ----------------------------------------------------------------------
# 1. HLAVNÍ MENU FUNKCE
# ----------------------------------------------------------------------
def show_tmdb_menu():
    """Vytvoří základní menu: Trendy, Filmy a Seriály (sjednocený look)."""
    start_time = time.perf_counter() # PROFILER_START
    cache_key = 'tmdb_main_menu_v2'
    directory_items = []
    if _render_cached_menu(cache_key, directory_items):
        log.info(f"TMDB_PROFILER: show_tmdb_menu (HlavniTMDBMenu) trvalo {time.perf_counter() - start_time:.3f} s [CACHE HIT]")
        return

    _append_menu_item(
        directory_items,
        label='Vyhledat na TMDb',
        plot='Zadej název filmu nebo seriálu a zobraz výsledky vyhledávání přímo z TMDb.',
        action='tmdb_search',
        art_icon=ui_pick_icon(addon, 'trending_day.png', 'DefaultAddonsSearch.png', 'tmdb'),
        is_folder=True
    )

    _append_menu_item(
        directory_items,
        label='Trendy dnes (Filmy & Seriály)',
        plot='Kombinovaný žebříček toho, co je dnes na TMDb nejvíce trendy.',
        action='tmdb_list',
        art_icon=ui_pick_icon(addon, 'trending_day.png', 'DefaultFavourites.png', 'tmdb'),
        is_folder=True,
        category='trending_day',
        page=1
    )

    _append_menu_item(
        directory_items,
        label='Filmy',
        plot='Procházení filmových kategorií: Populární, Nejlépe hodnocené, Připravované.',
        action='tmdb_movie_categories',
        art_icon=ui_pick_icon(addon, 'movie_popular.png', 'DefaultMovies.png', 'tmdb'),
        is_folder=True
    )

    _append_menu_item(
        directory_items,
        label='Seriály',
        plot='Procházení seriálových kategorií: Populární, Nejlépe hodnocené, Právě vysílané, Dnes v TV.',
        action='tmdb_tv_categories',
        art_icon=ui_pick_icon(addon, 'tv_popular.png', 'DefaultTVShows.png', 'tmdb'),
        is_folder=True
    )

    _append_menu_item(
        directory_items,
        label='Osoby (Herci, Tvůrci)',
        plot='Procházení populárních a trendy osobností na TMDb a jejich vyhledávání.',
        action='tmdb_person_categories',
        art_icon=ui_pick_icon(addon, 'person_popular.png', 'DefaultActors.png', 'tmdb'),
        is_folder=True
    )

    _append_menu_item(
        directory_items,
        label='VOD premiéry (posledních 14 dní)',
        plot='Filmy čerstvě vydané na VOD platformách. Výběr Česko (Netflix, Prima+, Disney+...) nebo Globální.',
        action='tmdb_vod_premieres_menu',
        art_icon=ui_pick_icon(addon, 'movie_upcoming.png', 'DefaultVideoPlaylists.png', 'tmdb'),
        is_folder=True
    )

    _append_menu_item(
        directory_items,
        label='VOD služby ČR',
        plot=(
            'Novinky na Netflix, Max, Disney+, Apple TV+ a Prime (ČR) přes Streaming Availability API. '
            'Filmy a seriály – nově přidané tituly; data cachovaná 24 h.'
        ),
        action='tmdb_vod_cz_streaming_menu',
        art_icon=ui_pick_icon(addon, 'movie_popular.png', 'DefaultStudios.png', 'tmdb'),
        is_folder=True
    )

    _save_menu_cache(cache_key, directory_items)
    xbmcplugin.addDirectoryItems(addon_handle, directory_items)

    log.info(f"TMDB_PROFILER: show_tmdb_menu (HlavniTMDBMenu) trvalo {time.perf_counter() - start_time:.3f} s") # PROFILER_END
    xbmcplugin.endOfDirectory(
        addon_handle, cacheToDisc=kodi_cache_to_disc_for_menu(cache_key)
    )


def show_movie_categories():
    start_time = time.perf_counter() # PROFILER_START
    cache_key = 'tmdb_movie_categories'
    directory_items = []
    if _render_cached_menu(cache_key, directory_items):
        log.info(f"TMDB_PROFILER: show_movie_categories trvalo {time.perf_counter() - start_time:.3f} s [CACHE HIT]")
        return

    items = [
        ('movie_popular',   'Populární filmy',         'Filmy, které jsou právě nejvíce populární.',         'movie_popular.png'),
        ('movie_top_rated', 'Nejlépe hodnocené filmy', 'Filmy s nejvyšším hodnocením na TMDb.',               'movie_top_rated.png'),
        ('movie_upcoming',  'Připravované filmy',      'Nadcházející premiéry a brzké uvedení do kin.',       'movie_upcoming.png'),
    ]

    for key, label, plot, icon in items:
        _append_menu_item(
            directory_items,
            label=label,
            plot=plot,
            action='tmdb_list',
            art_icon=ui_pick_icon(addon, icon, 'DefaultFolder.png', 'tmdb'),
            is_folder=True,
            category=key,
            page=1
        )

    # --- NOVĚ: Pokročilý filtr (TMDb Discover) ---
    _append_menu_item(
        directory_items,
        label='Pokročilý filtr (TMDb)',
        plot=('Filtrování filmů podle žánru, volitelného roku/rozpětí a minimálního ratingu.\n'
              'Výsledky se zobrazí se stránkováním jen s „Další strana“.'),
        action='tmdb_advanced_filter_menu',
        art_icon=ui_pick_icon(addon, 'filter_advanced.png', 'DefaultMovieTitle.png', 'tmdb'),
        is_folder=True
    )
    _append_menu_item(
        directory_items,
        label='Pokračovat s posledním filtrem (TMDb)',
        plot='Znovu otevře TMDb Discover s naposledy použitými parametry.',
        action='tmdb_discover_resume',
        art_icon=ui_pick_icon(addon, 'filter_advanced.png', 'DefaultMovieTitle.png', 'tmdb'),
        is_folder=True
    )

    _save_menu_cache(cache_key, directory_items)
    xbmcplugin.addDirectoryItems(addon_handle, directory_items)

    log.info(f"TMDB_PROFILER: show_movie_categories trvalo {time.perf_counter() - start_time:.3f} s") # PROFILER_END
    xbmcplugin.endOfDirectory(
        addon_handle, cacheToDisc=kodi_cache_to_disc_for_menu(cache_key)
    )

def show_tv_categories():
    start_time = time.perf_counter() # PROFILER_START
    cache_key = 'tmdb_tv_categories'
    directory_items = []
    if _render_cached_menu(cache_key, directory_items):
        log.info(f"TMDB_PROFILER: show_tv_categories trvalo {time.perf_counter() - start_time:.3f} s [CACHE HIT]")
        return

    items = [
        ('tv_popular',       'Populární seriály',          'Seriály, které jsou právě nejvíce populární.',            'tv_popular.png'),
        ('tv_top_rated',     'Nejlépe hodnocené seriály',  'Seriály s nejvyšším hodnocením na TMDb.',                 'tv_top_rated.png'),
        ('tv_on_the_air',    'Právě vysílané (týden)',     'Epizody, které se aktuálně vysílají v tomto týdnu.',      'tv_on_the_air.png'),
        ('tv_airing_today',  'Vysílané dnes',              'Seriály, které mají novou epizodu právě dnes.',           'tv_airing_today.png'),
    ]
    for key, label, plot, icon in items:
        _append_menu_item(
            directory_items,
            label=label,
            plot=plot,
            action='tmdb_list',
            art_icon=ui_pick_icon(addon, icon, 'DefaultFolder.png', 'tmdb'),
            is_folder=True,
            category=key,
            page=1
        )

    _save_menu_cache(cache_key, directory_items)
    xbmcplugin.addDirectoryItems(addon_handle, directory_items)
    log.info(f"TMDB_PROFILER: show_tv_categories trvalo {time.perf_counter() - start_time:.3f} s") # PROFILER_END
    xbmcplugin.endOfDirectory(
        addon_handle, cacheToDisc=kodi_cache_to_disc_for_menu(cache_key)
    )

def tmdb_search_menu():
    kb = xbmc.Keyboard('', 'Vyhledat na TMDb')
    kb.doModal() 
    if kb.isConfirmed():
        search_query = kb.getText()
        if not search_query:
            xbmcplugin.endOfDirectory(addon_handle, succeeded=False)
            return
        log.info(f"Vyhledávání dotazu (Keyboard): {search_query}")
        url = build_url({
            'action': 'tmdb_list', 
            'category': 'search', 
            'q': search_query,
            'page': 1
        })
        # Bez `replace` — zachová rodiče v historii navigace (návrat z přehrávače zůstane v doplňku).
        xbmc.executebuiltin(f'Container.Update({url})')
        # succeeded=True: jinak Kodi hlásí GetDirectory failed pro URL action=tmdb_search (prázdný výpis je OK, Update přepne obsah).
        try:
            xbmcplugin.endOfDirectory(addon_handle, succeeded=True, cacheToDisc=False)
        except Exception:
            pass
        return
    xbmcplugin.endOfDirectory(addon_handle)

# ----------------------------------------------------------------------
# 2. ZOBRAZENÍ SEZNAMU POLOŽEK (Filmy/Seriály) – VYLEPŠENO O HROMADNÉ NAČÍTÁNÍ
# ----------------------------------------------------------------------
# resources/lib/tmdb_kodi.py (Nová funkce, přidejte ji pod show_tv_categories)

def show_person_categories():
    """Vykreslí podkategorie pro objevování osob (Populární, Trendy)."""
    start_time = time.perf_counter() # PROFILER_START
    cache_key = 'tmdb_person_categories'
    directory_items = []
    if _render_cached_menu(cache_key, directory_items):
        log.info(f"TMDB_PROFILER: show_person_categories trvalo {time.perf_counter() - start_time:.3f} s [CACHE HIT]")
        return

    items = [
        ('person_popular',      'Populární osoby',      'Aktuálně nejpopulárnější herci a tvůrci na TMDb.',    'person_popular.png'),
        ('person_trending_day', 'Trendy osoby dnes',    'Osoby s největším nárůstem popularity během 24h.',   'trending_day.png'),
        ('person_trending_week','Trendy osoby týdne',   'Osoby s největším nárůstem popularity za poslední týden.', 'trending_week.png'),
    ]
    
    # První položka je vyhledávání
    _append_menu_item(
        directory_items,
        label='Vyhledat osobu na TMDb',
        plot='Zadej jméno osoby a zobraz výsledky vyhledávání.',
        action='tmdb_search_person',
        art_icon=ui_pick_icon(addon, 'trending_day.png', 'DefaultAddonsSearch.png', 'tmdb'),
        is_folder=True
    )

    for key, label, plot, icon in items:
        _append_menu_item(
            directory_items,
            label=label,
            plot=plot,
            action='tmdb_list',
            art_icon=ui_pick_icon(addon, icon, 'DefaultFolder.png', 'tmdb'),
            is_folder=True,
            category=key
        )

    _save_menu_cache(cache_key, directory_items)
    xbmcplugin.addDirectoryItems(addon_handle, directory_items)
    log.info(f"TMDB_PROFILER: show_person_categories trvalo {time.perf_counter() - start_time:.3f} s") # PROFILER_END
    xbmcplugin.endOfDirectory(
        addon_handle, cacheToDisc=kodi_cache_to_disc_for_menu(cache_key)
    )
# resources/lib/tmdb_kodi.py (Nová funkce, přidejte ji pod tmdb_search_menu)

def tmdb_search_person_menu():
    """Spustí dialog pro vyhledávání osoby a přesměruje na tmdb_list."""
    kb = xbmc.Keyboard('', 'Vyhledat osobu na TMDb')
    kb.doModal() 
    if kb.isConfirmed():
        search_query = kb.getText()
        if not search_query:
            xbmcplugin.endOfDirectory(addon_handle, succeeded=False)
            return
        log.info(f"Vyhledávání osoby (Keyboard): {search_query}")
        url = build_url({
            'action': 'tmdb_list', 
            'category': 'search_person', # NOVÁ KATEGORIE!
            'q': search_query
        })
        xbmc.executebuiltin(f'Container.Update({url})')
        try:
            xbmcplugin.endOfDirectory(addon_handle, succeeded=True, cacheToDisc=False)
        except Exception:
            pass
        return
    xbmcplugin.endOfDirectory(addon_handle)

def show_tmdb_list(params):
    start_time = time.perf_counter() # PROFILER_START
    category = params.get('category')
    query = params.get('q')
    try:
        page = int(params.get('page', 1))
    except Exception:
        page = 1

    cacheable_categories = {
        'trending_day',
        'movie_popular', 'movie_top_rated', 'movie_upcoming',
        'tv_popular', 'tv_top_rated', 'tv_on_the_air', 'tv_airing_today',
        'person_popular', 'person_trending_day', 'person_trending_week',
    }
    cache_key_version = 'v2'
    can_use_menu_cache = (
        is_cache_enabled('tmdb_list_pages')
        and (category in cacheable_categories)
        and not query
    )
    cache_lookup_key = f"tmdb_list_{cache_key_version}_{category}_page_{page}" if can_use_menu_cache else None
    if cache_lookup_key:
        cached = get_menu_cache_server().get_cached_menu(cache_lookup_key)
        if cached:
            directory_items = []
            for item in cached.get('items', []):
                li = xbmcgui.ListItem(label=item.get('label', ''))
                info = item.get('info') or {}
                art = item.get('art') or {}
                props = item.get('properties') or {}
                cm = item.get('context') or []

                if info:
                    set_video_info(li, info)
                if art:
                    li.setArt(art)
                for k, v in props.items():
                    li.setProperty(str(k), str(v))
                cm_norm = []
                for c in cm:
                    if isinstance(c, (list, tuple)) and len(c) >= 2:
                        cm_norm.append((str(c[0]), str(c[1])))
                if cm_norm:
                    cm_norm = style_context_menu(cm_norm)
                    li.addContextMenuItems(cm_norm, replaceItems=True)

                directory_items.append((item.get('url', ''), li, bool(item.get('isFolder', True))))

            if str(category).startswith('person_'):
                xbmcplugin.setContent(addon_handle, 'actors')
            elif str(category).startswith('tv_'):
                xbmcplugin.setContent(addon_handle, 'tvshows')
            elif str(category).startswith('movie_'):
                xbmcplugin.setContent(addon_handle, 'movies')
            else:
                xbmcplugin.setContent(addon_handle, 'videos')

            xbmcplugin.addDirectoryItems(addon_handle, directory_items)
            xbmcplugin.endOfDirectory(addon_handle)
            log_cache_event('HIT', 'tmdb_list_pages', f"{category} page {page}, {cached.get('count', len(directory_items))} items")
            log.info(f"TMDB_PROFILER: show_tmdb_list category={category} trvalo {time.perf_counter() - start_time:.3f} s [CACHE HIT]")
            return
        else:
            log_cache_event('MISS', 'tmdb_list_pages', f"{category} page {page}")

    # --- Ověření TMDb API klíče ---
    api_key = addon.getSetting('tmdb_api_key')
    if not api_key:
        xbmcgui.Dialog().notification('TMDb chyba', 'Chybí TMDb API klíč v nastavení doplňku.',
                                      xbmcgui.NOTIFICATION_WARNING, 3000)
        xbmcplugin.endOfDirectory(addon_handle, succeeded=False)
        return

    tmdb = TMDb(api_key)

    # --- Načtení jedné stránky dat (paging) ---
    try:
        items, total_pages, page_size = tmdb.get_list_page(category, query=query, page=page)
    except Exception as e:
        log.error(f"Chyba při získávání seznamu/vyhledávání: {e}")
        xbmcgui.Dialog().notification('TMDb chyba', f'Chyba při volání API: {e}',
                                      xbmcgui.NOTIFICATION_ERROR, 3000)
        xbmcplugin.endOfDirectory(addon_handle, succeeded=False)
        return

    if not items:
        log.info(f"Načteno 0 položek pro kategorii {category} (q={query}) page={page}")
        xbmcplugin.endOfDirectory(addon_handle)
        return

    # --- Nastavení content typu podle obsahu stránky ---
    has_tv = any((it.get('media_type') or '') == 'tv' for it in items)
    has_movies = any((it.get('media_type') or '') == 'movie' for it in items)
    if has_tv and not has_movies:
        xbmcplugin.setContent(addon_handle, 'tvshows')
    elif has_movies and not has_tv:
        xbmcplugin.setContent(addon_handle, 'movies')
    else:
        xbmcplugin.setContent(addon_handle, 'videos')

    # --- Helper: bezpečný build „zlatého“ OMDb headeru ---
    def _build_gold_header_from_metrics(metrics_dict: dict) -> str:
        try:
            parts = []

            # IMDb
            r = metrics_dict.get('imdbRating')
            v = metrics_dict.get('imdbVotes')
            if r not in (None, "", 0, "N/A"):
                try:
                    r_val = float(r)
                    star = f"[COLOR=FFFFD700]★ {r_val:.1f}[/COLOR]"
                except Exception:
                    star = f"[COLOR=FFFFD700]★ {r}[/COLOR]"
                votes_txt = None
                if v not in (None, "", 0, "N/A"):
                    try:
                        v_clean = int(str(v).replace(",", "").replace(" ", ""))
                        votes_txt = f"{v_clean:,}".replace(",", " ")
                    except Exception:
                        votes_txt = str(v)
                parts.append("IMDb: " + (f"{star} ({votes_txt})" if votes_txt else star))

            # Metascore
            m = metrics_dict.get('Metascore')
            if m not in (None, "", 0, "N/A"):
                parts.append(f"Metascore: [B]{m}[/B]")

            # RottenTomatoes
            rt = metrics_dict.get('RottenTomatoes')
            if rt not in (None, "", "N/A"):
                parts.append(f"RT: [B]{rt}[/B]")

            # Box Office
            bo = metrics_dict.get('BoxOffice')
            if bo not in (None, "", "N/A"):
                parts.append(f"Box Office: [B]{bo}[/B]")

            safe_parts = [p for p in parts if isinstance(p, str) and p.strip()]
            if safe_parts:
                return f"[COLOR FFFFFF00]{' • '.join(safe_parts)}[/COLOR]"
        except Exception as e:
            log.warning(f"build gold header error: {e}")
        return ""

    # --- Helper: bezpečné vložení headeru do plotu ---
    def _inject_header_into_plot(li: xbmcgui.ListItem, info_labels: dict, header: str, fallback_plot: str):
        try:
            if not header:
                return
            plot_existing = info_labels.get('plot')
            if not isinstance(plot_existing, str):
                plot_existing = str(plot_existing) if plot_existing is not None else ""
            if not plot_existing:
                plot_existing = str(fallback_plot or "")
            info_labels['plot'] = f"{header}\n\n{plot_existing}".strip()
            set_video_info(li, info_labels)
        except Exception as e:
            log.warning(f"inject header into plot error: {e}")

    # --- Jednoduchá větev pro OSOBY (paging + bezpečný artwork) ---
    is_person_list = items[0].get('media_type') == 'person'
    if is_person_list:
        xbmcplugin.setPluginCategory(addon_handle, (PERSON_CATEGORIES.get(category) or 'Vyhledávání osob'))
        xbmcplugin.setContent(addon_handle, 'actors')
        directory_items = []
        serializable_items = []
        person_return_url = build_url({
            'action': 'tmdb_list',
            'category': str(category or ''),
            'q': str(query or ''),
            'page': str(page),
        })

        for item in items:
            person_id = item.get('id')
            name = item.get('label') or 'Neznámá osoba'
            profile_path = item.get('profile_path')
            known_for_str = ", ".join([it.get('title') or it.get('name') or ''
                                       for it in item.get('known_for', [])[:3] if it]) or ''

            li = xbmcgui.ListItem(label=name)
            li.setProperty('mediatype', 'actor')
            if profile_path:
                try:
                    p = str(profile_path or '').strip()
                    if p:
                        li.setArt({'icon': IMAGE_BASE_URL + p, 'thumb': IMAGE_BASE_URL + p})
                except Exception as e:
                    log.warning(f"Person art concat error person_id={person_id}: {e}")
            else:
                li.setArt({'icon': 'DefaultActor.png', 'thumb': 'DefaultActor.png'})

            info_labels = {'title': name, 'plot': f"Známý pro: {known_for_str}".strip(), 'mediatype': 'actor'}
            set_video_info(li, info_labels)

            url = build_url({
                'action': 'person_detail',
                'person_id': str(person_id or ''),
                'return_url': person_return_url,
            })
            directory_items.append((url, li, True))
            art_snapshot = {}
            if hasattr(li, 'getArt'):
                for k in ('icon', 'thumb', 'poster', 'fanart'):
                    try:
                        v = li.getArt(k)
                    except Exception:
                        v = ''
                    if v:
                        art_snapshot[k] = v
            serializable_items.append((
                url,
                name,
                True,
                info_labels,
                art_snapshot,
                {'mediatype': 'actor'},
                [],
                False,
            ))
            
        xbmcplugin.addDirectoryItems(addon_handle, directory_items)

        # Další strana (dole)
        display_total = (str(total_pages) if total_pages else '?')

        # 1) Tlačítko DALŠÍ STRANA (Next Page)
        # Podmínka: Pokud aktuální stránka je menší než celkový počet
        if total_pages and page < total_pages:
            next_page = page + 1
            
            # Musíme sestavit URL se všemi parametry (category, q), aby vyhledávání pokračovalo
            url_next = build_url({
                'action': 'tmdb_list',
                'category': str(category or ''),
                'q': str(query or ''),
                'page': str(next_page)
            })
            
            li_next = xbmcgui.ListItem(label=f"[COLOR gold]>>> Další strana ({next_page}/{display_total}) >>>[/COLOR]")
            li_next.setArt({'icon': 'DefaultVideoPlaylists.png'})
        
            xbmcplugin.addDirectoryItem(addon_handle, url_next, li_next, isFolder=True)
            serializable_items.append((
                url_next,
                li_next.getLabel(),
                True,
                {'title': li_next.getLabel()},
                {'icon': 'DefaultVideoPlaylists.png'},
                {},
                [],
                False,
            ))

        if cache_lookup_key and serializable_items:
            if page == 1 and len(serializable_items) < 5:
                log.warning(f"[MenuCache] Skip cache write for suspiciously small TMDB page: category={category} page={page} items={len(serializable_items)}")
            else:
                get_menu_cache_server().cache_menu(cache_lookup_key, serializable_items, get_cache_ttl('tmdb_list_pages'))


        xbmcplugin.endOfDirectory(addon_handle)
        return  # ukončit person větev (bez OMDb)

    # --- OMDb konfigurace (na stránku) ---
    addon_settings = xbmcaddon.Addon('plugin.video.mmirousek_v3')
    try:
        omdb_enabled = addon_settings.getSettingBool('omdb_enable')
    except Exception:
        omdb_enabled = True

    # --- Bulk external_ids pro tuto stránku ---
    tmdb_ids_movie = [it['id'] for it in items if it.get('media_type') == 'movie']
    tmdb_ids_tv = [it['id'] for it in items if it.get('media_type') == 'tv']
    external_movie = tmdb.get_external_ids_bulk(tmdb_ids_movie, 'movie') if tmdb_ids_movie else {}
    external_tv = tmdb.get_external_ids_bulk(tmdb_ids_tv, 'tv') if tmdb_ids_tv else {}
    all_external = {**external_movie, **external_tv}

    # === HROMADNÉ STAŽENÍ CHYBĚJÍCÍCH DETAILŮ PŘES THREADPOOL ===
    items_to_fetch = []
    for item in items:
        m_type = item.get('media_type')
        t_id = item.get('id')
        if not t_id or not m_type: continue
        
        cached = db.get(t_id) or {}
        t_cs = cached.get('title_cs')
        t_en = cached.get('title_en')
        t_orig = cached.get('title_original')
        post = cached.get('poster_path') or item.get('poster_path')
        oplot = cached.get('overview_cs') or item.get('plot') or ''
        
        need_detail = (not (t_cs or t_en or t_orig)) or (not post) or (not oplot)
        if need_detail:
            items_to_fetch.append((m_type, t_id, item.get('original_language', 'en')))

    if items_to_fetch:
        def fetch_detail(task_args):
            m_type, t_id, orig_lang = task_args
            try:
                detail = tmdb._call_api_raw(f"{API_URL}/{m_type}/{t_id}", {'language': 'cs-CZ'}).json()
                data = {
                    'title_cs': detail.get('title') or detail.get('name'),
                    'title_en': detail.get('original_title') or detail.get('original_name'),
                    'title_original': detail.get('original_title') or detail.get('original_name'),
                    'year': (detail.get('release_date') or detail.get('first_air_date') or '')[:4],
                    'poster_path': detail.get('poster_path'),
                    'backdrop_path': detail.get('backdrop_path'),
                    'overview_cs': detail.get('overview')
                }
                if orig_lang not in ['en', 'cs']:
                    english_label = get_english_title(m_type, t_id)
                    if english_label:
                        data['title_cs'] = english_label
                        log.info(f"Použit EN název pro TMDb {t_id}: {english_label}")
                # odstraneni None/prazdnych klíčů aby se nepřebily platné s prázdnýma stringama
                return t_id, m_type, {k: v for k, v in data.items() if v} 
            except Exception as e:
                log.warning(f"Bulk fallback fetch failed for {t_id}: {e}")
                return t_id, m_type, None

        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
            results = executor.map(fetch_detail, items_to_fetch)
            
        # Zapis naráz (SQLite nebolí tolik jako webová odezva)
        for t_id, m_type, new_data in results:
            if new_data:
                new_data['media_type'] = m_type
                db.update(t_id, new_data, source='tmdb_detail_bulk')

    # --- Render položek (bezpečně a HROMADNĚ) ---
    directory_items = []
    serializable_items = []
    
    for idx, item in enumerate(items):
        try:
            media_type = item.get('media_type')
            tmdb_id = item.get('id')

            cached = db.get(tmdb_id) or {}
            title_cs = cached.get('title_cs')
            title_en = cached.get('title_en')
            title_original = cached.get('title_original')
            year = cached.get('year') or item.get('year')
            poster_path = cached.get('poster_path') or item.get('poster_path')
            backdrop_path = cached.get('backdrop_path') or item.get('backdrop_path')
            original_plot = cached.get('overview_cs') or item.get('plot') or ''
            original_language = item.get('original_language') or 'en'
            trakt_watched = cached.get('trakt_watched')

            # Sestava labelu
            label = title_cs or title_en or title_original or item.get('label') or ''
            fallback_title = title_en or title_original or label
            secondary_label = None
            if original_language == 'en' and title_original and str(title_original) != str(label):
                secondary_label = str(title_original)
            if original_language not in ['en', 'cs'] and tmdb_id:
                try:
                    english_label = get_english_title(media_type, tmdb_id)
                    if english_label:
                        label = english_label
                        fallback_title = english_label
                except Exception:
                    pass

            prefix = "[COLOR orange]Seriál[/COLOR] " if media_type == 'tv' else "[COLOR purple]Film[/COLOR] "
            watched_prefix = "[COLOR green]● [/COLOR]" if (trakt_watched and int(trakt_watched) >= 1) else ""
            label = "" if label is None else str(label)
            year_str = str(year) if year not in (None, "") else ""
            display_label = f"{watched_prefix}{prefix}{label}"
            if secondary_label and secondary_label != label:
                display_label += f" [{secondary_label}]"
            if year_str:
                display_label += f" ({year_str})"
            rating_val = item.get('rating', 0) or 0.0
            try:
                rating_val = float(rating_val)
            except Exception:
                rating_val = 0.0
            if rating_val > 0.0:
                display_label += f" [COLOR=FFFFFFFF]★[/COLOR][COLOR=FFFFD700]{rating_val:.1f}[/COLOR]"

            # URL po kliku
            if media_type == 'tv':
                url = build_url({
                    'action': 'tmdb_seasons',
                    'tmdb_id': str(tmdb_id or ''),
                    'title': str(label or ''),
                    'fallback_title': str(fallback_title or '')
                })
                is_folder = True
            else:
                url = build_url({
                    'action': 'show_search_dialog',
                    'cz_title': str(title_cs or ''),
                    'eng_title': str(title_en or ''),
                    'year': str(year or ''),
                    'tmdb_id': str(tmdb_id or ''),
                    'mediatype': 'movie',
                    'return_after_play_url': _current_plugin_folder_url(),
                })
                is_folder = False

            # ListItem + bezpečný artwork
            li = xbmcgui.ListItem(label=display_label)
            if poster_path:
                try:
                    p = str(poster_path or '').strip()
                    if p:
                        li.setArt({'icon': IMAGE_BASE_URL + p, 'thumb': IMAGE_BASE_URL + p})
                except Exception as e:
                    log.warning(f"Poster path concat error tmdb_id={tmdb_id}: {e}")
            if backdrop_path:
                try:
                    b = str(backdrop_path or '').strip()
                    if b:
                        li.setArt({'fanart': FANART_BASE_URL + b})
                except Exception as e:
                    log.warning(f"Fanart path concat error tmdb_id={tmdb_id}: {e}")

            info_labels = {
                'title': label,
                'plot': original_plot or '',
                'year': int(year) if isinstance(year, str) and year.isdigit() else (year if isinstance(year, int) else None),
                'rating': rating_val,
                'mediatype': 'tvshow' if media_type == 'tv' else 'movie',
            }
            info_labels = {k: v for k, v in info_labels.items() if v not in (None, '')}
            set_video_info(li, info_labels)
            if tmdb_id:
                li.setProperty('tmdb_id', str(tmdb_id))
                li.setProperty('mediatype', 'tvshow' if media_type == 'tv' else 'movie')
                if media_type != 'tv':
                    li.setProperty('IsPlayable', 'false')

            # OMDb: získání imdb_id
            imdb_id = (all_external.get(tmdb_id, {}) or {}).get('imdb_id') or cached.get('imdb_id')
            if tmdb_id and not imdb_id:
                try:
                    ext_func = tmdb.get_tv_external_ids if media_type == 'tv' else tmdb.get_movie_external_ids
                    ext = ext_func(tmdb_id)
                    imdb_id = ext.get('imdb_id')
                except Exception as e:
                    log.warning(f"external_ids selhalo pro {tmdb_id}: {e}")

            # DB větev OMDb
            row = None
            try:
                row = db.conn.execute("""
                  SELECT imdb_rating, imdb_votes, metascore, rt_rating, box_office
                  FROM media WHERE tmdb_id = ?
                """, (tmdb_id,)).fetchone()
            except Exception as e:
                log.warning(f"DB SELECT failed for tmdb_id={tmdb_id}: {e}")

            metrics_from_db = None
            if row:
                vals = list(row)
                if len(vals) < 5:
                    vals += [None] * (5 - len(vals))
                metrics_from_db = {
                    'imdbRating': vals[0],
                    'imdbVotes': vals[1],
                    'Metascore': vals[2],
                    'RottenTomatoes': vals[3],
                    'BoxOffice': vals[4],
                }

            use_omdb_api = True
            if metrics_from_db and metrics_from_db.get('imdbRating') not in (None, "", 0, "N/A"):
                label2 = build_label2(metrics_from_db)
                if label2:
                    try:
                        li.setLabel2(label2)
                    except Exception:
                        log.debug("[OMDb] setLabel2 failed — skin nepodporuje label2")

                colored = f"[B][COLOR=FFFFD700]{label2}[/COLOR][/B]" if label2 else ""
                if colored:
                    existing_tagline = info_labels.get('tagline') or ''
                    if str(existing_tagline).strip() != colored:
                        info_labels['tagline'] = colored
                        set_video_info(li, info_labels)

                # GOLD header do plotu
                header = _build_gold_header_from_metrics(metrics_from_db)
                _inject_header_into_plot(li, info_labels, header, original_plot)

                use_omdb_api = False

            # OMDb asynchronně pro background service (service.py -> background_enrich_omdb_ratings)
            # Pokud položka nemá v DB imdb_rating a získali jsme přes bulk external_ids nové imdb_id,
            # uložíme ho rovnou do databáze k pozdějšímu zpracování na pozadí.
            if omdb_enabled and tmdb_id and imdb_id and use_omdb_api:
                # Pouze pro film a seriál
                try:
                    # Neaktualizujeme nic jiného než imdb_id, aby background worker věděl, co má zpracovat
                    db.update(tmdb_id, {
                        'media_type': media_type,
                        'imdb_id': imdb_id
                    }, source='omdb_async_queue')
                except Exception as e:
                    log.warning(f"DB save async imdb id error pro {tmdb_id}: {e}")

            # Kontextové menu (Trakt + ČSFD)
            from resources.lib.utils.context_builder import build_context_menu
            contexts = ['trakt_shows_default'] if media_type == 'tv' else ['trakt_movies_default']
            contexts.append('tmdb_csfd')
            if media_type != 'tv':
                contexts.append('titulky_movie')

            item_data = {
                'series_name' if media_type == 'tv' else 'movie_name': label,
                'tmdb_id': tmdb_id,
                'title': label,
                'year': year,
            }
            
            cm = build_context_menu(build_url_kwargs, item_data, contexts=contexts)
            
            if cm:
                li.addContextMenuItems(cm, replaceItems=True)

            # --- Hromadné shromáždění místo okamžitého vykreslení ---
            directory_items.append((url, li, is_folder))
            art_snapshot = {}
            if hasattr(li, 'getArt'):
                for k in ('icon', 'thumb', 'poster', 'fanart'):
                    try:
                        v = li.getArt(k)
                    except Exception:
                        v = ''
                    if v:
                        art_snapshot[k] = v
            serializable_items.append((
                url,
                display_label,
                is_folder,
                info_labels,
                art_snapshot,
                {
                    'tmdb_id': str(tmdb_id or ''),
                    'mediatype': 'tvshow' if media_type == 'tv' else 'movie'
                },
                cm or [],
                False,
            ))

        except Exception as e:
            import traceback
            log.error(f"[LIST RENDER] FATAL tmdb_id={item.get('id')} media_type={item.get('media_type')}: {traceback.format_exc()}")
            continue  # pokračuj na další položku

    # --- COMMIT nově získaných metadat ---
    try:
        db.conn.commit()
    except Exception as e:
        log.error(f"Database COMMIT error: {e}")

    # --- Další strana (dole seznamu) ---
    # Další strana (dole)
    display_total = (str(total_pages) if total_pages else '?')

    # 1) Tlačítko DALŠÍ STRANA (Next Page)
    # Podmínka: Pokud aktuální stránka je menší než celkový počet
    if total_pages and page < total_pages:
        next_page = page + 1
        
        # Musíme sestavit URL se všemi parametry (category, q), aby vyhledávání pokračovalo
        url_next = build_url({
            'action': 'tmdb_list',
            'category': str(category or ''),
            'q': str(query or ''),
            'page': str(next_page)
        })
        
        li_next = xbmcgui.ListItem(label=f"[COLOR gold]>>> Další strana ({next_page}/{display_total}) >>>[/COLOR]")
        li_next.setArt({'icon': 'DefaultVideoPlaylists.png'})
    
        # Paginační tlačítko vkládáme napřímo jako 1 položku nakonec
        directory_items.append((url_next, li_next, True))
        serializable_items.append((
            url_next,
            li_next.getLabel(),
            True,
            {'title': li_next.getLabel()},
            {'icon': 'DefaultVideoPlaylists.png'},
            {},
            [],
            False,
        ))

    if cache_lookup_key and serializable_items:
        if page == 1 and len(serializable_items) < 5:
            log.warning(f"[MenuCache] Skip cache write for suspiciously small TMDB page: category={category} page={page} items={len(serializable_items)}")
        else:
            get_menu_cache_server().cache_menu(cache_lookup_key, serializable_items, get_cache_ttl('tmdb_list_pages'))

    # 4) Vložíme VŠECHNY POLOŽKY Naráz
    xbmcplugin.addDirectoryItems(addon_handle, directory_items)

    # Finální ukončení listu
    xbmcplugin.endOfDirectory(addon_handle)
    log.info(f"TMDB_PROFILER: show_tmdb_list category={category} trvalo {time.perf_counter() - start_time:.3f} s") # PROFILER_END

def _year_from_str(s: Optional[str]) -> int:
    try:
        t = (s or '').strip()
        return int(t[:4]) if len(t) >= 4 else 0
    except Exception:
        return 0


def _safe_date_from_iso(value: str):
    try:
        return datetime.strptime((value or '').strip(), '%Y-%m-%d').date()
    except Exception:
        return None


def _calculate_age(birthday: str, deathday: str = '') -> str:
    born = _safe_date_from_iso(birthday)
    if not born:
        return ''

    end_date = _safe_date_from_iso(deathday) or date.today()
    years = end_date.year - born.year - ((end_date.month, end_date.day) < (born.month, born.day))
    return str(years) if years >= 0 else ''


def _format_gender(value) -> str:
    mapping = {
        1: 'Žena',
        2: 'Muž',
        3: 'Nebinární',
    }
    try:
        return mapping.get(int(value), '')
    except Exception:
        return ''


def _poster_art(path: str, default_icon: str = 'DefaultVideoPoster.png') -> dict:
    clean_path = str(path or '').strip()
    if not clean_path:
        return {'thumb': default_icon, 'icon': default_icon, 'poster': default_icon}
    art_url = IMAGE_BASE_URL_POSTER + clean_path
    return {'thumb': art_url, 'icon': art_url, 'poster': art_url}


def _fanart_art(path: str) -> dict:
    clean_path = str(path or '').strip()
    if not clean_path:
        return {}
    art_url = FANART_BASE_URL + clean_path
    return {'fanart': art_url}


def _build_movie_target_url(item: dict, person_id: str = '', person_return_url: str = '') -> str:
    person_query = {'action': 'person_detail', 'person_id': person_id}
    if person_return_url:
        person_query['return_url'] = person_return_url
    cancel_return_url = build_url(person_query) if person_id else ''
    return build_url({
        'action': 'show_search_dialog',
        'cz_title': item.get('title') or '',
        'eng_title': item.get('original_title') or '',
        'year': item.get('year') or '',
        'tmdb_id': '',
        'mediatype': 'movie',
        'cancel_return_url': cancel_return_url,
        'return_after_play_url': _current_plugin_folder_url(),
    })


def _build_show_target_url(item: dict) -> str:
    tmdb_mid = item.get('tmdb_mid')
    if not tmdb_mid:
        return ''
    return build_url_kwargs(action='tmdb_seasons', tmdb_id=tmdb_mid, title=item.get('title') or '')


def _build_person_credit_sections(info: dict, person_return_url: str = ''):
    movie_credits = (info.get('movie_credits') or {})
    tv_credits = (info.get('tv_credits') or {})
    person_id = str(info.get('id') or '').strip()
    documentary_genre_id = 99
    show_blacklist = {10763, 10764, 10767}

    unique_movies = {}
    unique_shows = {}

    def _merge_credit(bucket: dict, credit: dict, media_type: str):
        title_id = credit.get('id')
        if not title_id:
            return

        if media_type == 'movie':
            title = credit.get('title') or credit.get('original_title') or 'Bez názvu'
            date_value = credit.get('release_date')
            original_title = credit.get('original_title') or ''
        else:
            title = credit.get('name') or credit.get('original_name') or 'Bez názvu'
            date_value = credit.get('first_air_date')
            original_title = credit.get('original_name') or ''

        role = (credit.get('character') or credit.get('job') or '').strip()
        unique_key = f"{title_id}_{media_type}"
        existing = bucket.get(unique_key)
        if existing:
            if role and role not in existing.get('role', ''):
                existing['role'] = (existing.get('role', '') + ' | ' + role).strip(' |')
            return

        item = {
            'label': title,
            'title': title,
            'year': _year_from_str(date_value),
            'plot': credit.get('overview') or 'Shrnutí není dostupné.',
            'role': role,
            'poster_path': credit.get('poster_path') or '',
            'tmdb_mid': title_id,
            'original_title': original_title,
            'popularity': float(credit.get('popularity') or 0.0),
        }
        bucket[unique_key] = item

    for credit in (movie_credits.get('cast') or []) + (movie_credits.get('crew') or []):
        genre_ids = credit.get('genre_ids') or []
        if documentary_genre_id in genre_ids:
            continue
        _merge_credit(unique_movies, credit, 'movie')

    for credit in (tv_credits.get('cast') or []) + (tv_credits.get('crew') or []):
        genre_ids = set(credit.get('genre_ids') or [])
        if genre_ids and genre_ids.intersection(show_blacklist):
            continue
        _merge_credit(unique_shows, credit, 'tv')

    movies = list(unique_movies.values())
    shows = list(unique_shows.values())
    movies.sort(key=lambda x: (x.get('year', 0), x.get('popularity', 0.0)), reverse=True)
    shows.sort(key=lambda x: (x.get('year', 0), x.get('popularity', 0.0)), reverse=True)

    movie_items = []
    for item in movies[:60]:
        label = item['title']
        role = item.get('role') or ''
        if role:
            label = f"{label} - {role}"
        movie_items.append({
            'label': label,
            'year': item.get('year') or '',
            'plot': item.get('plot') or '',
            'art': _poster_art(item.get('poster_path'), default_icon='DefaultVideo.png'),
            'properties': {
                'target_url': _build_movie_target_url(item, person_id=person_id, person_return_url=person_return_url),
                'role': role,
            }
        })

    show_items = []
    for item in shows[:60]:
        label = item['title']
        role = item.get('role') or ''
        if role:
            label = f"{label} - {role}"
        show_items.append({
            'label': label,
            'year': item.get('year') or '',
            'plot': item.get('plot') or '',
            'art': _poster_art(item.get('poster_path'), default_icon='DefaultTVShows.png'),
            'properties': {
                'target_url': _build_show_target_url(item),
                'role': role,
            }
        })

    return movie_items, show_items


def _build_person_image_items(info: dict):
    images = ((info.get('images') or {}).get('profiles') or [])
    items = []
    for index, image in enumerate(images[:30], start=1):
        file_path = str(image.get('file_path') or '').strip()
        if not file_path:
            continue
        art_url = IMAGE_BASE_URL_POSTER + file_path
        items.append({
            'label': f'Foto {index}',
            'path': art_url,
            'art': {'thumb': art_url, 'icon': art_url},
        })
    return items


def _build_person_dialog_payload(info: dict, person_return_url: str = ''):
    name = info.get('name') or 'Neznámé jméno'
    biography = (info.get('biography') or '').strip() or 'Biografie není k dispozici.'
    profile_path = str(info.get('profile_path') or '').strip()
    birthday = str(info.get('birthday') or '').strip()
    deathday = str(info.get('deathday') or '').strip()
    art = {'icon': 'DefaultActor.png', 'thumb': 'DefaultActor.png'}
    if profile_path:
        art.update({'icon': IMAGE_BASE_URL_POSTER + profile_path, 'thumb': IMAGE_BASE_URL_POSTER + profile_path})
        art.update(_fanart_art(profile_path))

    person_item = {
        'label': name,
        'biography': biography,
        'art': art,
        'properties': {
            'biography': biography,
            'Gender': _format_gender(info.get('gender')),
            'Known_For_Department': info.get('known_for_department') or '',
            'age': _calculate_age(birthday, deathday),
            'birthday': birthday,
            'deathday': deathday,
            'Place_Of_Birth': info.get('place_of_birth') or '',
        }
    }

    movie_items, show_items = _build_person_credit_sections(info, person_return_url=person_return_url)
    image_items = _build_person_image_items(info)
    return person_item, movie_items, show_items, image_items


def _render_person_detail_directory(handle: int, person_item: dict, movie_items: list, show_items: list):
    xbmcplugin.setPluginCategory(handle, f"TMDb • {person_item.get('label') or 'Osoba'}")
    xbmcplugin.setContent(handle, 'actors')

    li_info = xbmcgui.ListItem(label=f"[COLOR red]{person_item.get('label') or 'Neznámé jméno'}[/COLOR]")
    li_info.setArt(person_item.get('art') or {'icon': 'DefaultActor.png'})
    set_video_info(li_info, {
        'title': person_item.get('label') or 'Neznámá osoba',
        'plot': person_item.get('biography') or 'Biografie není k dispozici.',
        'mediatype': 'actor',
    })
    li_info.setProperty('mediatype', 'actor')
    xbmcplugin.addDirectoryItem(handle, build_url_kwargs(action='noop'), li_info, isFolder=False)

    if movie_items:
        header = xbmcgui.ListItem(label=f"[COLOR purple][Filmy] • {len(movie_items)}[/COLOR]")
        xbmcplugin.addDirectoryItem(handle, build_url_kwargs(action='noop'), header, isFolder=False)
        for item in movie_items:
            li = xbmcgui.ListItem(label=item.get('label') or 'Bez názvu')
            li.setProperty('mediatype', 'movie')
            li.setArt(item.get('art') or {})
            xbmcplugin.addDirectoryItem(handle, item.get('properties', {}).get('target_url') or build_url_kwargs(action='noop'), li, isFolder=True)

    if show_items:
        header = xbmcgui.ListItem(label=f"[COLOR orange][Seriály] • {len(show_items)}[/COLOR]")
        xbmcplugin.addDirectoryItem(handle, build_url_kwargs(action='noop'), header, isFolder=False)
        for item in show_items:
            li = xbmcgui.ListItem(label=item.get('label') or 'Bez názvu')
            li.setProperty('mediatype', 'tvshow')
            li.setArt(item.get('art') or {})
            is_folder = bool(item.get('properties', {}).get('target_url'))
            xbmcplugin.addDirectoryItem(handle, item.get('properties', {}).get('target_url') or build_url_kwargs(action='noop'), li, isFolder=is_folder)

    xbmcplugin.endOfDirectory(handle, cacheToDisc=False)


def person_detail_dialog_loop(
    person_item: dict,
    movie_items: list,
    show_items: list,
    image_items: list,
    return_url: str = '',
    plugin_handle: Optional[int] = None,
) -> bool:
    """
    Cyklus PersonInfoWindow (film z filmografie, biografie, návrat).
    plugin_handle=None: voláno z jiného modalu (např. CSFD) — bez endOfDirectory / Container.Update.
    Vrací True po ActivateWindow na plugin URL (uživatel zvolil film u osoby).
    """
    from resources.lib.ui.person_info_window import PersonInfoWindow

    while True:
        try:
            xbmcgui.Window(10000).clearProperty(PERSON_NEXTCALL_PROPERTY)
            xbmcgui.Window(10000).clearProperty(PERSON_ACTION_PROPERTY)
        except Exception:
            pass

        PersonInfoWindow.show_info(
            person_item, movies=movie_items, tvshows=show_items, images=image_items
        )

        next_url = ''
        next_action = ''
        try:
            next_url = xbmcgui.Window(10000).getProperty(PERSON_NEXTCALL_PROPERTY) or ''
            next_action = xbmcgui.Window(10000).getProperty(PERSON_ACTION_PROPERTY) or ''
            xbmcgui.Window(10000).clearProperty(PERSON_NEXTCALL_PROPERTY)
            xbmcgui.Window(10000).clearProperty(PERSON_ACTION_PROPERTY)
        except Exception:
            next_url = ''
            next_action = ''

        if next_url:
            if plugin_handle is not None:
                try:
                    xbmcplugin.endOfDirectory(plugin_handle, succeeded=True, cacheToDisc=False)
                except Exception:
                    pass
            safe_url = next_url.replace('"', '%22')
            xbmc.executebuiltin(f'ActivateWindow(Videos,"{safe_url}",return)')
            return True

        if next_action == 'biography':
            xbmcgui.Dialog().textviewer(
                person_item.get('label') or 'Biografie',
                person_item.get('biography') or 'Biografie není k dispozici.',
            )
            continue

        if return_url:
            if plugin_handle is not None:
                try:
                    xbmcplugin.endOfDirectory(plugin_handle, succeeded=True, cacheToDisc=False)
                except Exception:
                    pass
                safe_return_url = return_url.replace('"', '%22')
                xbmc.executebuiltin(f'Container.Update({safe_return_url},replace)')
            return False

        if plugin_handle is not None:
            xbmcplugin.endOfDirectory(plugin_handle, succeeded=False, cacheToDisc=False)
        return False


def open_person_detail_modal(person_id: int) -> bool:
    """
    Načte osobu z TMDb a otevře PersonInfoWindow mimo router (např. z CSFDInfoWindow).
    Vrací True, pokud uživatel přešel na položku pluginu (ActivateWindow).
    """
    api_key = xbmcaddon.Addon().getSetting('tmdb_api_key')
    tmdb = TMDb(api_key)
    info = tmdb.get_person_detail(int(person_id))
    if not info:
        return False
    person_item, movie_items, show_items, image_items = _build_person_dialog_payload(
        info, person_return_url=''
    )
    return person_detail_dialog_loop(
        person_item,
        movie_items,
        show_items,
        image_items,
        return_url='',
        plugin_handle=None,
    )


def show_person_detail(params):
    """
    Zobrazí detail osoby a její filmografii získanou z TMDb.
    Voláno z routeru akcí 'person_detail'.
    """
    person_id = params.get('person_id')
    return_url = (params.get('return_url') or '').strip()
    if not person_id:
        log.error("Chybí person_id pro show_person_detail")
        return

    handle = int(sys.argv[1])
    api_key = xbmcaddon.Addon().getSetting('tmdb_api_key')
    tmdb = TMDb(api_key)

    info = tmdb.get_person_detail(int(person_id))
    if not info:
        xbmcplugin.endOfDirectory(handle, cacheToDisc=False)
        return

    person_item, movie_items, show_items, image_items = _build_person_dialog_payload(
        info, person_return_url=return_url
    )

    try:
        person_detail_dialog_loop(
            person_item,
            movie_items,
            show_items,
            image_items,
            return_url=return_url,
            plugin_handle=handle,
        )
    except Exception as exc:
        log.warning(f"TMDb person dialog fallback pro person_id={person_id}: {exc}")
        _render_person_detail_directory(handle, person_item, movie_items, show_items)


def show_advanced_filter_menu():
    """
    Dialogy pro výběr žánru, (volitelně) roku/rozpětí a minimálního ratingu.
    Po potvrzení uloží 'poslední filtr' (SQL pokud je k dispozici, jinak addon settings)
    a otevře 'tmdb_discover_list' s page=1 přes ActivateWindow (robustní napříč skiny).
    """
    # --- IMPORTY / ENV ---
    import sys
    handle = None
    try:
        handle = int(sys.argv[1])
    except Exception:
        pass

    try:
        log.info("ENTER function")
    except Exception:
        pass

    # --- ŽÁNRY ---
    try:
        genres = [
            ('Akční', 28), ('Dobrodružný', 12), ('Animovaný', 16), ('Komediální', 35),
            ('Krimi', 80), ('Dokumentární', 99), ('Drama', 18), ('Rodinný', 10751),
            ('Fantasy', 14), ('Historický', 36), ('Horor', 27), ('Hudební', 10402),
            ('Mysteriózní', 9648), ('Romantický', 10749), ('Sci‑Fi', 878), ('Thriller', 53),
            ('Válečný', 10752), ('Western', 37)
        ]
        idx = xbmcgui.Dialog().select("Vyber žánr", [g[0] for g in genres])
        if idx < 0:
            log.info("cancelled at genre select")
            if handle is not None:
                xbmcplugin.endOfDirectory(handle, succeeded=False)
            return
        selected_genre = genres[idx][1]
        log.info(f"genre_id={selected_genre}")
    except Exception as e:
        log.error(f"GENRE DIALOG FAIL: {e}")
        if handle is not None:
            xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    # --- ROK / ROZPĚTÍ (volitelné) ---
    year = from_year = to_year = None
    try:
        mode = xbmcgui.Dialog().select("Rok / Rozpětí (volitelné)", ["Bez omezení", "Jeden rok…", "Rozpětí let…"])
        if mode == 1:
            y = xbmcgui.Dialog().numeric(0, "Zadejte rok (YYYY)")
            if y and y.isdigit():
                year = int(y)
        elif mode == 2:
            fy = xbmcgui.Dialog().numeric(0, "Od roku (YYYY)")
            ty = xbmcgui.Dialog().numeric(0, "Do roku (YYYY)")
            if fy and fy.isdigit():
                from_year = int(fy)
            if ty and ty.isdigit():
                to_year = int(ty)
        log.info(f"year={year} from={from_year} to={to_year}")
    except Exception as e:
        log.error(f"YEAR/RANGE DIALOG FAIL: {e}")
        if handle is not None:
            xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    # --- MINIMÁLNÍ RATING (volitelné) ---
    min_rating = None
    try:
        r = xbmcgui.Dialog().input("Minimální rating TMDb (např. 7.5)")
        if r:
            try:
                min_rating = float(str(r).replace(",", "."))
            except Exception:
                min_rating = None
        log.info(f"min_vote={min_rating}")
    except Exception as e:
        log.error(f"RATING INPUT FAIL: {e}")
        if handle is not None:
            xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    # --- ULOŽ POSLEDNÍ FILTR (SQL pokud existuje KV, jinak settings) ---
    addon_local = xbmcaddon.Addon('plugin.video.mmirousek_v3')
    last_filter = {
        'genre_id': str(selected_genre),
        'year': str(year or ''),
        'from_year': str(from_year or ''),
        'to_year': str(to_year or ''),
        'min_vote': str(min_rating or ''),
        'sort_by': 'popularity.desc',
        'media_type': 'movie'
    }

    # Pokus o SQL uložení do existující KV tabulky (např. app_state) – bez vytváření
    import json
    saved_sql = False
    try:
        # zjisti, zda existuje tabulka app_state
        row = db.conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name='app_state'"
        ).fetchone()
        if row and row[0] == 'app_state':
            db.conn.execute(
                "INSERT OR REPLACE INTO app_state(key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)",
                ('tmdb_last_filter', json.dumps(last_filter, ensure_ascii=False))
            )
            db.conn.commit()
            saved_sql = True
            log.info("Saved last filter to SQL app_state")
    except Exception as e:
        log.debug(f"SQL save skipped: {e}")

    # Fallback: addon settings (pokud SQL uložení neproběhlo)
    if not saved_sql:
        try:
            addon_local.setSetting('tmdb_last_genre_id', last_filter['genre_id'])
            addon_local.setSetting('tmdb_last_year', last_filter['year'])
            addon_local.setSetting('tmdb_last_from_year', last_filter['from_year'])
            addon_local.setSetting('tmdb_last_to_year', last_filter['to_year'])
            addon_local.setSetting('tmdb_last_min_vote', last_filter['min_vote'])
            addon_local.setSetting('tmdb_last_sort_by', last_filter['sort_by'])
            addon_local.setSetting('tmdb_last_media_type', last_filter['media_type'])
            log.info("Saved last filter to addon settings")
        except Exception as e:
            log.error(f"SAVE settings FAIL: {e}")

    # --- SESTAVENÍ URL + NAVIGACE ---
    try:
        url = build_url({'action': 'tmdb_discover_list', **last_filter, 'page': '1'})
        log.info(f"goto URL={url}")

        # ukonči aktuální container, ať Kodi navigaci přijme
        if handle is not None:
            try:
                xbmcplugin.endOfDirectory(handle, succeeded=True)
            except Exception:
                pass

        # robustně otevři výsledky Discoveru
        xbmc.executebuiltin(f'ActivateWindow(Videos,"{url}",return)')

    except NameError as ne:
        log.error(f"NameError (build_url scope): {ne}")
        xbmcgui.Dialog().notification("TMDb", "Interní chybay: chybí build_url v tmdb_kodi.py",
                                      xbmcgui.NOTIFICATION_ERROR, 3000)
        if handle is not None:
            try:
                xbmcplugin.endOfDirectory(handle, succeeded=False)
            except Exception:
                pass
    except Exception as e:
        log.error(f"NAVIGATE FAIL: {e}")
        if handle is not None:
            try:
                xbmcplugin.endOfDirectory(handle, succeeded=False)
            except Exception:
                pass


def show_tmdb_discover_list(params):
    """
    TMDb Discover (žánr, rok/rozpětí, min. rating) + paging.
    Primární název: CZ (pokud je latinkou) jinak EN fallback. Sekundární název: zobrazí se pouze CZ/EN a jen když se liší.
    Nezobrazuje se žádný jiný jazyk (KO/ZH apod.), aby skin neukazoval „čtverečky“.
    """
    # --- ENTER log ---
    log.info("ENTER: params=" + str(params))

    addon = xbmcaddon.Addon('plugin.video.mmirousek_v3')
    handle = int(sys.argv[1])

    # ------- pomocné validace skriptu (latinka) -------
    def is_latinish(s: str) -> bool:
        """
        Vrátí True, pokud je text převážně latinkou (včetně českých diakritik).
        Používá podíly znaků v Latin-1 Supplement a Latin Extended-A/B; filtruje Hangul/CJK/Cyrilici/Arabštinu.
        """
        if not s:
            return False
        total_letters = 0
        latin_letters = 0
        strong_nonlatin = 0
        for ch in s:
            code = ord(ch)
            # písmena: A-Z, a-z, diakritika v Latin-1 (00C0–00FF) + Latin Ext (0100–024F)
            is_latin = (0x0041 <= code <= 0x005A) or (0x0061 <= code <= 0x007A) or \
                       (0x00C0 <= code <= 0x00FF) or (0x0100 <= code <= 0x024F)
            # silné ne-latin skripty (typicky „čtverečky“ ve skinu)
            is_hangul = (0xAC00 <= code <= 0xD7AF) or (0x1100 <= code <= 0x11FF) or (0x3130 <= code <= 0x318F)
            is_cjk    = (0x4E00 <= code <= 0x9FFF) or (0x3400 <= code <= 0x4DBF)
            is_kata   = (0x30A0 <= code <= 0x30FF)
            is_hira   = (0x3040 <= code <= 0x309F)
            is_cyr    = (0x0400 <= code <= 0x04FF)
            is_arab   = (0x0600 <= code <= 0x06FF)
            # započítávej jen „písmena“; interpunkce a mezery nevadí
            if ch.isalpha():
                total_letters += 1
                if is_latin:
                    latin_letters += 1
                if is_hangul or is_cjk or is_kata or is_hira or is_cyr or is_arab:
                    strong_nonlatin += 1
        if total_letters == 0:
            return False
        # Pokud jsou přítomny „silné ne-latin“ bloky, odmítni
        if strong_nonlatin > 0:
            return False
        # Jinak vyžaduj aspoň ~70 % latinky
        return (latin_letters / float(total_letters)) >= 0.70

    def _to_int(s):
        try:
            return int(str(s))
        except Exception:
            return None

    # ------- parametry z URL -------
    genre_id  = params.get('genre_id')
    year      = _to_int(params.get('year')) if params.get('year') else None
    from_year = _to_int(params.get('from_year')) if params.get('from_year') else None
    to_year   = _to_int(params.get('to_year')) if params.get('to_year') else None

    try:
        min_vote = float(params.get('min_vote')) if params.get('min_vote') else None
    except Exception:
        min_vote = None

    try:
        page = int(params.get('page', 1))
    except Exception:
        page = 1

    # ------- API key check -------
    api_key = addon.getSetting('tmdb_api_key')
    if not api_key:
        xbmcgui.Dialog().notification('TMDb chyba', 'Chybí TMDb API klíč v nastavení doplňku.',
                                      xbmcgui.NOTIFICATION_WARNING, 3000)
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    tmdb = TMDb(api_key)

    # ------- Discover volání (tuple check) -------
    sort_by = params.get('sort_by') or 'popularity.desc'
    try:
        result = tmdb.discover_movies_page(
            genre_id=genre_id, year=year, from_year=from_year, to_year=to_year,
            min_vote=min_vote, page=page, language='cs-CZ', sort_by=sort_by
        )
    except Exception as e:
        log.error(f"{e}")
        xbmcgui.Dialog().notification('TMDb chyba', f'Chyba Discover: {e}', xbmcgui.NOTIFICATION_ERROR, 3000)
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    if not result or not isinstance(result, tuple) or len(result) != 3:
        log.error("discover_movies_page() did not return (items, total_pages, page_size)")
        xbmcgui.Dialog().notification('TMDb', 'Interní chyba Discover. Viz log.', xbmcgui.NOTIFICATION_ERROR, 3000)
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    items, total_pages, page_size = result

    if not items:
        log.info(f"EMPTY: genre={genre_id} year={year} from={from_year} to={to_year} vote>={min_vote} page={page}")
        xbmcgui.Dialog().notification('TMDb', 'Žádné výsledky pro zadaný filtr.', xbmcgui.NOTIFICATION_INFO, 2500)
        xbmcplugin.endOfDirectory(handle)
        return

    xbmcplugin.setContent(handle, 'movies')

    # ------- Render položek -------
    for item in items:
        tmdb_id         = item.get('id')
        label_cz_raw    = item.get('label') or ''                  # lokalizace z cs-CZ Discover
        label_origin    = item.get('original_label') or ''
        origin_lang     = (item.get('original_language') or 'en').lower()
        year_txt        = item.get('year')
        rating_val      = item.get('rating') or 0.0
        try:
            rating_val = float(rating_val)
        except Exception:
            rating_val = 0.0

        # EN fallback (načteme dopředu)
        en_title = None
        try:
            en_title = get_english_title('movie', tmdb_id)
        except Exception as e:
            log.warning(f"get_english_title selhalo tmdb_id={tmdb_id}: {e}")

        # --- výběr primárního a sekundárního názvu ---
        label_cz = label_cz_raw.strip() if label_cz_raw else ''
        label_en = (str(en_title).strip() if en_title else '')

        # Primární: CZ, ale jen pokud je latinkou. Jinak EN.
        if label_cz and is_latinish(label_cz):
            primary_label = label_cz
            # Sekundárně zobraz EN pouze, když existuje a liší se (CZ/EN only)
            secondary_label = label_en if (label_en and label_en != primary_label) else None
        else:
            # CZ není latinkou (nebo prázdné) → použij EN
            if label_cz and not is_latinish(label_cz):
                log.info(f"CZ label není latinkou → EN fallback (tmdb_id={tmdb_id}): '{label_cz}'")
            primary_label = label_en or (label_origin if is_latinish(label_origin) else 'Bez názvu')
            # Sekundárně CZ pouze, když je latinkou a liší se; nezobrazuj KO/ZH apod.
            secondary_label = label_cz if (label_cz and is_latinish(label_cz) and label_cz != primary_label) else None

        # Sestavení display labelu (CZ/EN jen)
        display_label = primary_label
        if secondary_label and secondary_label != primary_label:
            display_label += f" [COLOR grey][{secondary_label}][/COLOR]"
        if year_txt:
            display_label += f" ({year_txt})"
        if rating_val > 0.0:
            display_label += f" [COLOR=FFFFFFFF]★[/COLOR][COLOR=FFFFD700]{rating_val:.1f}[/COLOR]"

        li = xbmcgui.ListItem(label=display_label)

        # Artwork
        poster_path   = item.get('poster_path')
        backdrop_path = item.get('backdrop_path')
        if poster_path:
            p = str(poster_path or '').strip()
            if p:
                li.setArt({'icon': IMAGE_BASE_URL + p, 'thumb': IMAGE_BASE_URL + p})
        if backdrop_path:
            b = str(backdrop_path or '').strip()
            if b:
                li.setArt({'fanart': FANART_BASE_URL + b})

        # InfoTagVideo
        info_labels = {
            'title': primary_label,
            'plot': item.get('plot') or '',
            'year': int(year_txt) if isinstance(year_txt, str) and year_txt.isdigit() else None,
            'rating': rating_val,
            'mediatype': 'movie'
        }
        info_labels = {k: v for k, v in info_labels.items() if v not in (None, '')}
        set_video_info(li, info_labels)

        # Zápis do DB (jen CZ/EN; jiné jazyky se do title_* neukládají)
        try:
            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': 'movie',
                    'title_cs': title_cs_for_db,
                    'title_en': title_en_for_db,
                    'year': year_txt or None,
                    'poster_path': poster_path or None,
                    'backdrop_path': backdrop_path or None
                },
                source='tmdb_discover'
            )
        except Exception as e:
            log.warning(f"DB update failed tmdb_id={tmdb_id}: {e}")

        # Klik → show_search_dialog (předávej jen CZ/EN; jiné jazyky vynecháme)
        url = build_url({
            'action': 'show_search_dialog',
            'cz_title': (title_cs_for_db or ''),
            'eng_title': (title_en_for_db or ''),
            'year': year_txt or '',
            'tmdb_id': str(tmdb_id or ''),
            'mediatype': 'movie',
            'return_after_play_url': _current_plugin_folder_url(),
        })
        li.setProperty('IsPlayable', 'false')
        xbmcplugin.addDirectoryItem(handle, url, li, isFolder=False)

    # COMMIT strany
    try:
        db.conn.commit()
    except Exception as e:
        log.error(f"Database COMMIT error: {e}")

    # Paging
    display_total = (str(total_pages) if total_pages else '?')
    has_more = False
    try:
        has_more = int(page) < int(total_pages or 1)
    except Exception:
        try:
            has_more = len(items) >= int(page_size or 20)
        except Exception:
            has_more = len(items) >= 20

    if has_more:
        next_url = build_url({
            'action': 'tmdb_discover_list',
            'genre_id': str(genre_id or ''),
            'year': str(year or ''),
            'from_year': str(from_year or ''),
            'to_year': str(to_year or ''),
            'min_vote': str(min_vote or ''),
            'page': str(page + 1)
        })
        li_next = xbmcgui.ListItem(label=f"Další strana » ({page+1}/{display_total})")
        li_next.setArt({'icon': 'DefaultFolder.png'})
        xbmcplugin.addDirectoryItem(handle, next_url, li_next, isFolder=True)

    # Ukončení containeru (updateListing=True, fallback pro starší Kodi)
    log.info("END: calling endOfDirectory(updateListing=True)")
    try:
        xbmcplugin.endOfDirectory(handle, succeeded=True, cacheToDisc=True, updateListing=True)
    except TypeError:
        xbmcplugin.endOfDirectory(handle)

def tmdb_discover_resume(params=None):
    """
    Načte poslední filtr (SQL app_state -> settings fallback),
    a místo navigace přímo zavolá show_tmdb_discover_list(payload),
    aby se listing vykreslil v aktuálním kontejneru (správné chování Back).
    """
    import sys, json
    handle = int(sys.argv[1])

    # 1) Pokus o SQL load (pokud máš KV tabulku 'app_state')
    data = None
    try:
        row = db.conn.execute("SELECT value FROM app_state WHERE key = ?", ('tmdb_last_filter',)).fetchone()
        if row and row[0]:
            data = json.loads(row[0])
            log.info("Loaded last filter from SQL app_state")
    except Exception as e:
        log.debug(f"SQL load skipped: {e}")

    # 2) Fallback do addon settings
    if not data:
        addon_local = xbmcaddon.Addon('plugin.video.mmirousek_v3')
        data = {
            'genre_id': addon_local.getSetting('tmdb_last_genre_id') or '',
            'year': addon_local.getSetting('tmdb_last_year') or '',
            'from_year': addon_local.getSetting('tmdb_last_from_year') or '',
            'to_year': addon_local.getSetting('tmdb_last_to_year') or '',
            'min_vote': addon_local.getSetting('tmdb_last_min_vote') or '',
            'sort_by': addon_local.getSetting('tmdb_last_sort_by') or 'popularity.desc',
            'media_type': addon_local.getSetting('tmdb_last_media_type') or 'movie'
        }
        log.info("Loaded last filter from addon settings")

    # 3) Pokud je vše prázdné, nic neotvírat
    if not any(str(v).strip() for v in data.values()):
        xbmcgui.Dialog().notification('TMDb', 'Není uložen žádný poslední filtr.', xbmcgui.NOTIFICATION_INFO, 2500)
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    # 4) Normalize payload pro show_tmdb_discover_list()
    payload = {
        'genre_id': str(data.get('genre_id') or ''),
        'year': str(data.get('year') or ''),
        'from_year': str(data.get('from_year') or ''),
        'to_year': str(data.get('to_year') or ''),
        'min_vote': str(data.get('min_vote') or ''),
        'page': '1'
    }

    log.info(f"DIRECT CALL show_tmdb_discover_list with payload={payload}")

    # 5) ŽÁDNÁ NAVIGACE. PŘÍMÉ VYKRESLENÍ:
    try:
        show_tmdb_discover_list(payload)
    except Exception as e:
        log.error(f"DIRECT CALL FAIL: {e}")
        xbmcgui.Dialog().notification('TMDb', 'Nepodařilo se vykreslit poslední filtr.', xbmcgui.NOTIFICATION_ERROR, 3000)
        xbmcplugin.endOfDirectory(handle, succeeded=False)
        return

    # --- extra padding (proti usekávání přenosu) ---
    # (konec tmdb_discover_resume)
    # (EOF-block-padding-1)
    # (EOF-block-padding-2)
