
# -*- coding: utf-8 -*-
# Module: download_queue
# Author: mmirousek
# License: AGPL v3

import os
import sys  
import json
import re
import threading
import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
from datetime import datetime
from urllib.parse import urlencode, parse_qsl
from resources.lib.down.download_manager import download_file
from concurrent.futures import ThreadPoolExecutor, as_completed
from resources.lib.utils.ui_utils import is_cancel_requested, reset_cancel_flags
from resources.lib.utils.log_utils import get_logger

log = get_logger()


ADDON_ID = 'plugin.video.mmirousek_v3'
ADDON = xbmcaddon.Addon(ADDON_ID)
PROFILE_PATH = xbmcvfs.translatePath(ADDON.getAddonInfo('profile'))
QUEUE_FILE = os.path.join(PROFILE_PATH, 'download_queue.json')

# --- URL builder ---
def _build_url(**kwargs):
    base = sys.argv[0]
    return "{}?{}".format(base, urlencode(kwargs, encoding="utf-8"))

# --- Helpers ---


def _extract_url_and_headers(link_raw: str):
    if not link_raw:
        return None, {}
    if '\n' in link_raw:
        url, hdrs = link_raw.split('\n', 1)
        try:
            pairs = dict(parse_qsl(hdrs, encoding='utf-8'))
        except Exception:
            pairs = {}
        return url, pairs
    return link_raw, {}

def _is_cancel_any_requested() -> bool:
    try:
        win = xbmcgui.Window(10000)
        return (win.getProperty('mm_cancel_download') == '1') or (win.getProperty('mm_cancel_download_all') == '1')
    except Exception:
        return False

def _file_size(path: str) -> int:
    try:
        return os.path.getsize(path)
    except Exception:
        try:
            st = xbmcvfs.Stat(path)
            sz = getattr(st, 'st_size', 0)
            return int(sz() if callable(sz) else sz) or 0
        except Exception:
            return 0


_RE_STORAGE_TMDB = re.compile(r"^tmdb_\d+$", re.IGNORECASE)


def _resolve_display_name_for_queue(series_name: str, item_display_name: str = ""):
    """Resolve a human-readable series folder label for queue_add fallback path building."""
    def _usable_title(value) -> str:
        value = (value or "").strip()
        return value if value and not _RE_STORAGE_TMDB.match(value) else ""

    title = _usable_title(item_display_name)
    if title:
        return title, "display_name"

    try:
        from resources.lib.series.series_manager import SeriesManager

        sm = SeriesManager(ADDON, PROFILE_PATH)
        data = sm.load_series_data(series_name) or {}
        if isinstance(data, dict):
            title = _usable_title(data.get("name"))
            if title:
                return title, "db.name"

            tmdb = data.get("tmdb") or {}
            if isinstance(tmdb, dict):
                for key in ("title", "name", "original_title", "original_name"):
                    title = _usable_title(tmdb.get(key))
                    if title:
                        return title, f"db.tmdb.{key}"

            title = _usable_title(data.get("search_query_name"))
            if title:
                return title, "db.search_query_name"

            aliases = data.get("aliases") or []
            if isinstance(aliases, list):
                for alias in aliases:
                    title = _usable_title(alias)
                    if title:
                        return title, "db.aliases"
    except Exception as e:
        log.debug(f"[queue_add] display_name DB resolve failed for {series_name}: {e}")

    return (series_name or "").strip(), "series_name_fallback"

# --- Queue I/O ---
def load_queue():
    try:
        if not xbmcvfs.exists(QUEUE_FILE):
            return []
        with open(QUEUE_FILE, 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception as e:
        log.error(f"Chyba při načítání fronty: {e}")
        return []

def save_queue(queue):
    try:
        tmp_file = QUEUE_FILE + '.tmp'
        with open(tmp_file, 'w', encoding='utf-8') as f:
            json.dump(queue, f, ensure_ascii=False, indent=2)
        os.replace(tmp_file, QUEUE_FILE)
    except Exception as e:
        log.error(f"Chyba při ukládání fronty: {e}")

def _to_int_safe(val):
    try:
        if isinstance(val, bool):
            return 0
        if isinstance(val, (int, float)):
            return int(val)
        if isinstance(val, str):
            s = val.strip()
            if not s:
                return 0
            try:
                return int(s)
            except ValueError:
                return int(float(s))
        return int(val)
    except Exception:
        return 0



def add_to_queue(item: dict):
    """
    Přidá položku do fronty stahování.
    - Pokud target_path chybí a jde o seriál (series_name + season + episode),
      složíme cílovou cestu tak, aby NÁZEV SOUBORU začínal na 'SxxExx '.
    - Pokud target_path už je (offline_manager), nic nepřepisujeme.
    """
    import os
    import xbmc
    import xbmcgui
    import xbmcvfs
    from datetime import datetime
    from resources.lib.utils.ui_utils import clean_filename

    queue = load_queue()
    ident = item.get('ident') or item.get('ws_ident')
    target_path = (item.get('target_path') or '').strip()

    # === Pokud target_path chybí, zkuste ho složit pro seriál ===
    if (not target_path) and all(item.get(k) for k in ('series_name', 'season', 'episode')):
        dfolder = ADDON.getSetting('dfolder') or ''
        if dfolder:
            try:
                base_real = xbmcvfs.translatePath(dfolder)
                if base_real and not base_real.endswith(('/', '\\')):
                    base_real += '/'

                resolved_display_name, name_source = _resolve_display_name_for_queue(
                    item.get('series_name', ''),
                    item.get('display_name', ''),
                )
                series_folder = clean_filename(resolved_display_name or item['series_name'])
                season_folder = f"Sezona {item['season']}"
                log.info(
                    f"[queue_add] folder label source={name_source} "
                    f"resolved={resolved_display_name!r} series_name={item.get('series_name', '')!r}"
                )

                # 1) Rozumná přípona – ber z původního NÁZVU (ne z clean_name), aby zůstalo .mkv/.mp4/…
                orig_name_raw = item.get('name') or ''
                ext = os.path.splitext(orig_name_raw)[1] or '.mp4'

                # 2) Základ názvu – originál bez přípony, ale bezpečně vyčištěný
                base_orig = os.path.splitext(orig_name_raw)[0] or ident or 'download'
                safe_base_orig = clean_filename(base_orig)

                # 3) Prefix SxxExx pro OFFLINE detekci (musí být na ZAČÁTKU názvu)
                try:
                    s = int(item['season']); e = int(item['episode'])
                    sxxexx = f"S{s:02d}E{e:02d}"
                except Exception:
                    sxxexx = f"S{item['season']}E{item['episode']}"

                final_name = f"{sxxexx} {safe_base_orig}{ext}"

                # 4) Sestav finální cestu + vytvoř adresáře přes VFS
                folder_vfs = f"{base_real}{series_folder}/{season_folder}/"
                try:
                    xbmcvfs.mkdirs(folder_vfs)
                except Exception as e:
                    log.warning(f"mkdirs error: {e}")

                target_path = folder_vfs + final_name
            except Exception as e:
                log.warning(f"Path build error: {e}")
                target_path = ''

    # === Dedup (jen pokud máme ident + target_path) ===
    if ident and target_path:
        # 1. Kontrola, zda už soubor reálně nestojí na disku
        if xbmcvfs.exists(target_path):
            try:
                # Malá sanity check, jestli to není jen prázdný zbytek velikosti 0
                f = xbmcvfs.Stat(target_path)
                real_size = f.st_size()
                # Pokud to má víc než 1MB, budeme to brát že to existuje
                if real_size > 1048576:
                    xbmcgui.Dialog().notification('Fronta', 'Položka už je stažená na disku.', xbmcgui.NOTIFICATION_WARNING, 2500)
                    log.info(f"Duplicate physical file ignored: {target_path}")
                    return
            except Exception:
                # Fallback, pokud Stat selže z nějakého důvodu, radši to zablokujem
                xbmcgui.Dialog().notification('Fronta', 'Položka už je stažená na disku.', xbmcgui.NOTIFICATION_WARNING, 2500)
                log.info(f"Duplicate physical file ignored (no stat): {target_path}")
                return

        # 2. Kontrola, zda neleží pending nebo se zrovna nestahuje
        for q in queue:
            if q.get('ident') == ident and q.get('target_path') == target_path and q.get('status') in ('pending', 'downloading'):
                xbmcgui.Dialog().notification('Fronta', 'Položka je již ve frontě.', xbmcgui.NOTIFICATION_INFO, 2000)
                log.info(f"Duplicate in queue ignored ident={ident}")
                return

    # Normalizace velikosti, povinné klíče
    item['size'] = _to_int_safe(item.get('size', 0))
    item['ident'] = ident
    item['target_path'] = target_path or ''   # může zůstat prázdné (worker to označí jako error)
    item['status'] = 'pending'
    item['added_at'] = datetime.now().isoformat()

    queue.append(item)
    save_queue(queue)
    xbmcgui.Dialog().notification('Fronta', 'Přidáno do fronty.', xbmcgui.NOTIFICATION_INFO, 2000)

    # Předtím tu byl Container.Update(id,replace), zřejmě shazuje Kodi, dáme jen tichý pass
    # Pokud se to volá z menu epizod, a dáme Refresh(), tak se reloadne list epizod.
    # Hlavní menu refreshneme radši přes ruční "top banner"
    try:
        xbmc.executebuiltin("Container.Refresh")
    except Exception:
        pass

# def add_to_queue(item: dict):
#     queue = load_queue()
#     ident = item.get('ident') or item.get('ws_ident')
#     target_path = item.get('target_path')

#     # Anti-duplicitní kontrola (pouze když máme cílovou cestu)
#     if ident and target_path:
#         for q in queue:
#             if q.get('ident') == ident and q.get('target_path') == target_path and q.get('status') in ('pending', 'downloading'):
#                 xbmcgui.Dialog().notification('Fronta', 'Položka je již ve frontě.', xbmcgui.NOTIFICATION_INFO, 2000)
#                 log.info(f"Duplicate skipped ident={ident}")
#                 return

#     # 💡 NORMALIZACE VELIKOSTI (z vyhledávání často přichází string)
#     item['size'] = _to_int_safe(item.get('size', 0))

#     item['ident'] = ident
#     item['status'] = 'pending'
#     item['added_at'] = datetime.now().isoformat()

#     queue.append(item)
#     save_queue(queue)

#     xbmcgui.Dialog().notification('Fronta', 'Přidáno do fronty.', xbmcgui.NOTIFICATION_INFO, 2000)

#     # Volitelně: refresh aktuálního seznamu (ne hlavního menu). Necháme jen lehké osvěžení.
#     try:
#         xbmc.executebuiltin("Container.Refresh")
#     except Exception:
#         pass
#     xbmc.executebuiltin("Container.Update(plugin://plugin.video.mmirousek_v3/)")


def remove_from_queue(ident: str):
    try:
        queue = load_queue()
        before = len(queue)
        queue = [i for i in queue if i.get('ident') != ident]
        save_queue(queue)
        after = len(queue)
        if before != after:
            xbmcgui.Dialog().notification("Fronta", "Položka odebrána.", xbmcgui.NOTIFICATION_INFO, 2000)
        else:
            xbmcgui.Dialog().notification("Fronta", "Položka nenalezena.", xbmcgui.NOTIFICATION_WARNING, 2000)
    except Exception as e:
        xbmcgui.Dialog().notification("Fronta", "Chyba při odebrání.", xbmcgui.NOTIFICATION_ERROR, 2000)

def update_status(ident, status):
    queue = load_queue()
    for i in queue:
        if i.get('ident') == ident:
            i['status'] = status
            break
    save_queue(queue)


def clear_done():
    """Odstraní z fronty pouze položky se statusem 'done' a refreshne UI."""
    try:
        q = load_queue()
        before = len(q)
        q = [i for i in q if (i.get('status') or '') != 'done']
        save_queue(q)
        removed = before - len(q)
        log.info(f"clear_done: removed={removed}")
        xbmcgui.Dialog().notification("Fronta", f"Smazáno hotových: {removed}", xbmcgui.NOTIFICATION_INFO, 2500)
        xbmc.executebuiltin("Container.Refresh")
    except Exception as e:
        log.error(f"CHYBA clear_done: {e}")
        xbmcgui.Dialog().notification("Fronta", "Chyba při čištění hotových.", xbmcgui.NOTIFICATION_ERROR, 2500)



def clear_queue():
    """
    Zobrazí výběrový dialog pro 'Vyčistit frontu':
    - Smazat vše
    - Smazat jen dokončené (done)
    """
    try:
        dlg = xbmcgui.Dialog()
        choice = dlg.select(
            "Vyčistit frontu",
            ["Smazat vše", "Smazat jen dokončené (done)"]
        )

        if choice < 0:
            log.info("clear_queue: cancelled by user")
            return

        if choice == 0:
            # Smazat vše
            save_queue([])
            log.info("Fronta vyčištěna (vše)")
            xbmcgui.Dialog().notification("Fronta", "Fronta byla vyčištěna.", xbmcgui.NOTIFICATION_INFO, 2000)
        elif choice == 1:
            # Smazat jen 'done'
            q = load_queue()
            before = len(q)
            q = [i for i in q if (i.get('status') or '') != 'done']
            save_queue(q)
            removed = before - len(q)
            log.info(f"clear_done: removed={removed}")
            xbmcgui.Dialog().notification("Fronta", f"Smazáno hotových: {removed}", xbmcgui.NOTIFICATION_INFO, 2500)

        xbmc.executebuiltin("Container.Refresh")

    except Exception as e:
        log.error(f"CHYBA clear_queue: {e}")
        xbmcgui.Dialog().notification("Fronta", "Chyba při čištění fronty.", xbmcgui.NOTIFICATION_ERROR, 2000)

# --- Worker ---
def _download_worker(item: dict):
    ident = item.get('ident')
    target_path = item.get('target_path')
    expected_size = item.get('size')

    try:
        update_status(ident, 'downloading')

        # Vždy fetchni čerstvý link těsně před stažením — uložený link_raw může být expirovaný
        url, hdrs = '', {}
        try:
            from resources.lib.auth.webshare_auth import WebshareClient
            ws = WebshareClient(ADDON_ID)
            ws.revalidate()
            link_raw = ws.getlink(ident, dtype='download')
            url, hdrs = _extract_url_and_headers(link_raw or '')
        except Exception as e:
            log.error(f"getlink failed ident={ident}: {e}")

        if not url or not target_path:
            update_status(ident, 'error')
            return

        try:
            os.makedirs(os.path.dirname(target_path), exist_ok=True)
        except Exception:
            pass

        from resources.lib.down import download_manager as dm
        orig_headers = dict(dm._session.headers) if hasattr(dm, '_session') else {}

        try:
            if hdrs and hasattr(dm, '_session'):
                dm._session.headers.update(hdrs)

            download_file(url, target_path, expected_size=expected_size)

            # Kontrola zrušení nebo prázdného souboru
            cancelled = _is_cancel_any_requested()
            sz = _file_size(target_path)
            if cancelled:
                update_status(ident, 'pending')  # místo 'cancelled'
            elif sz <= 0:
                update_status(ident, 'error')
            else:
                update_status(ident, 'done')
            # Refresh UI po dokončení
            xbmc.executebuiltin("Container.Refresh")

        except Exception as e:
            update_status(ident, 'error')
            log.error(f"Chyba při stahování ident={ident}: {e}")

        finally:
            if hasattr(dm, '_session'):
                dm._session.headers.clear()
                dm._session.headers.update(orig_headers)

    except Exception as e:
        update_status(ident or '', 'error')
        log.error(f"Worker fatal error ident={ident}: {e}")

# --- Start ---


def start_download(ident: str):
    """Spustí stahování jedné položky z fronty podle ident a refreshne UI."""
    queue = load_queue()
    item = next((i for i in queue if i.get('ident') == ident), None)
    if not item:
        xbmcgui.Dialog().notification('Fronta', 'Položka nenalezena.', xbmcgui.NOTIFICATION_WARNING, 2000)
        return
    try:
        # Info popup před spuštěním
        xbmcgui.Dialog().notification("Fronta", "Stahování zahájeno.", xbmcgui.NOTIFICATION_INFO, 2000)
        threading.Thread(target=_download_worker, args=(item,), daemon=True).start()
        # Refresh UI po spuštění (aby se status změnil na "downloading")
        xbmc.executebuiltin("Container.Refresh")
    except Exception as e:
        log.error(f"CHYBA start_download ident={ident}: {e}")






from concurrent.futures import ThreadPoolExecutor, as_completed
from resources.lib.utils.ui_utils import is_cancel_requested, reset_cancel_flags
from resources.lib.down.download_manager import download_file



def start_all():
    """
    Spustí stahování všech položek ve frontě paralelně (max N vláken).
    Priorita: částečně stažené soubory (partial) před čistě nevyřízenými (FIFO uvnitř skupin).
    Nechává běžet na pozadí (daemon thread), aby se UI neblokovalo.
    """
    try:
        import threading
        from concurrent.futures import ThreadPoolExecutor, as_completed

        # Nastavení notifikací
        notify_enabled = ADDON.getSettingBool('dnotify')

        # Reset globálních cancel flagů
        reset_cancel_flags()

        def _is_partial(item: dict) -> bool:
            """True, pokud target_path existuje a jeho velikost je mezi 0 a očekávanou 'size'."""
            try:
                target_path = item.get('target_path') or ''
                expected_size = int(item.get('size') or 0)
                if not target_path or expected_size <= 0:
                    return False
                cur = _file_size(target_path)
                return (cur > 0) and (cur < expected_size)
            except Exception:
                return False

        def runner():
            try:
                q = load_queue() or []
                pending = [i for i in q if (i or {}).get('status') == 'pending']

                # Priorita partial -> 0, ostatní -> 1
                pending_sorted = sorted(pending, key=lambda it: 0 if _is_partial(it) else 1)

                # Počet paralelních vláken z nastavení, fallback 3
                try:
                    max_workers = int(ADDON.getSetting('max_parallel_downloads') or '3')
                except Exception:
                    max_workers = 3
                if max_workers < 1:
                    max_workers = 1

                log.info(f"start_all: {len(pending_sorted)} položek, paralelně={max_workers}")

                with ThreadPoolExecutor(max_workers=max_workers) as ex:
                    futures = []
                    for item in pending_sorted:
                        if is_cancel_requested():
                            log.info("Cancel detected, zastavuji plánování dalších položek")
                            break
                        futures.append(ex.submit(_download_worker, item))

                    for f in as_completed(futures):
                        try:
                            _ = f.result()
                        except Exception as e:
                            log.error(f"Chyba ve vlákně: {e}")

                # Refresh UI po dokončení
                xbmc.executebuiltin("Container.Refresh")

                # Reset flagů
                reset_cancel_flags()

                # Vyčištění notifikací (trik: prázdná notifikace na 1 ms)
                if notify_enabled:
                    xbmc.executebuiltin("Notification(Clean, , 1)")
                    xbmcgui.Dialog().notification("Fronta", "Stahování dokončeno nebo zrušeno.", xbmcgui.NOTIFICATION_INFO, 2000)

            except Exception as e:
                log.error(f"CHYBA runner: {e}")
                if notify_enabled:
                    xbmcgui.Dialog().notification("Fronta", "Chyba při spouštění stahování.", xbmcgui.NOTIFICATION_ERROR, 2000)

        # Info toast + start na pozadí
        if notify_enabled:
            try:
                max_workers = int(ADDON.getSetting('max_parallel_downloads') or '3')
            except Exception:
                max_workers = 3
            xbmcgui.Dialog().notification("Fronta",
                                          f"Stahování zahájeno (partial mají prioritu, paralelně: {max_workers})",
                                          xbmcgui.NOTIFICATION_INFO, 2500)

        threading.Thread(target=runner, daemon=True).start()

        # Okamžitý refresh UI
        xbmc.executebuiltin("Container.Refresh")

    except Exception as e:
        log.error(f"CHYBA start_all: {e}")
        if notify_enabled:
            xbmcgui.Dialog().notification("Fronta", "Chyba při spouštění stahování.", xbmcgui.NOTIFICATION_ERROR, 2000)

# --- UI ---
def _colorize_status(st: str) -> str:
    colors = {
        'done': 'lime',
        'downloading': 'deepskyblue',
        'pending': 'gray',
        'cancelled': 'orange',
        'error': 'red'
    }
    return f"[COLOR {colors.get(st, 'white')}]{st}[/COLOR]"






from resources.lib.utils.ui_utils import make_static_item

def show_queue_menu():
    import xbmcplugin
    handle = int(sys.argv[1])
    xbmcplugin.setPluginCategory(handle, "Fronta stahování")
    xbmcplugin.setContent(handle, "files")

    # Ovládací položky nahoře (sjednocené přes make_static_item)
    li_start_all = make_static_item(
        label="Spustit vše",
        plot="Spustí stahování všech položek ve frontě.\nPriorita: částečně stažené soubory.",
        addon=ADDON,
        icon_name='play_all.png',
        default_icon='DefaultPlay.png'
    )
    xbmcplugin.addDirectoryItem(handle, _build_url(action='queue_start_all'), li_start_all, isFolder=False)

    li_stop = make_static_item(
        label="Zastavit stahování",
        plot="Okamžitě zastaví všechny probíhající stahovací úlohy.\nPoložky zůstanou ve frontě.",
        addon=ADDON,
        icon_name='stop_download.png',
        default_icon='DefaultStop.png'
    )
    xbmcplugin.addDirectoryItem(handle, _build_url(action='queue_stop_all'), li_stop, isFolder=False)

    li_clear = make_static_item(
        label="Vyčistit frontu",
        plot="Smaže všechny položky z fronty nebo jen dokončené.\nZobrazí volbu v dialogu.",
        addon=ADDON,
        icon_name='clear_queue.png',
        default_icon='DefaultHardDisk.png'
    )
    xbmcplugin.addDirectoryItem(handle, _build_url(action='queue_clear'), li_clear, isFolder=False)

    # Dynamické položky fronty
    queue = load_queue()
    try:
        avg_mbps = float(ADDON.getSetting('avg_download_speed_mbps') or '10')
        if avg_mbps <= 0:
            avg_mbps = 10.0
    except Exception:
        avg_mbps = 10.0

    def _colorize_status(st: str) -> str:
        colors = {'done': 'lime', 'downloading': 'deepskyblue', 'pending': 'gray', 'cancelled': 'orange', 'error': 'red'}
        return f"[COLOR {colors.get(st, 'white')}]{st}[/COLOR]"

    def _fmt_sxxexx(item) -> str:
        s, e = item.get('season'), item.get('episode')
        if s and e and str(s).isdigit() and str(e).isdigit():
            return f"S{int(s):02d}E{int(e):02d}"
        return ""

    def _size_gb(item) -> str:
        sz = _to_int_safe(item.get('size'))
        return f"{round(sz/(1024**3), 2)} GB" if sz > 0 else ""

    def _eta_min(item) -> str:
        sz = _to_int_safe(item.get('size'))
        if sz > 0 and avg_mbps > 0:
            eta_sec = sz / (avg_mbps * 1024 * 1024)
            return f"{int(eta_sec/60)} min"
        return ""

    # Urychlení načítání I/O
    # Jenom pokud je to 'downloading', ptáme se na reálnou velikost disku.
    for item in queue:
        series_name = item.get('series_name', '')
        display_name = item.get('display_name') or series_name
        name = item.get('name', 'Neznámý')
        status = item.get('status', 'pending')
        sxxexx = _fmt_sxxexx(item)
        prefix_series = f"{display_name} · " if display_name else ""
        middle_sxe = f"{sxxexx} · " if sxxexx else ""

        # Částečně stažené info
        partial_info = ""
        try:
            target_path = item.get('target_path', '')
            expected_size = _to_int_safe(item.get('size'))
            # Zjistíme stat, jen když se nestahuje, ale je třeba vědět, zda je "part", nebo když to zrovna stahujeme
            # Ale ne pro všechny 'pending' pořád dokola, pokud není potřeba. (Pro jednoduchost UI necháme cache).
            if target_path and expected_size > 0:
                if status == 'downloading':
                    current_size = _file_size(target_path)
                else:
                    # Rychlejší, pokud se nestahuje - cache in queue list
                    current_size = item.get('_cached_size', _file_size(target_path))
                    item['_cached_size'] = current_size
                
                if 0 < current_size < expected_size:
                    current_mb = round(current_size / (1024 ** 2), 1)
                    total_mb = round(expected_size / (1024 ** 2), 1)
                    partial_info = f"[COLOR orange]ČÁST: {current_mb}/{total_mb} MB[/COLOR] · "
        except Exception:
            pass

        size_txt = _size_gb(item)
        eta_txt = _eta_min(item)
        suffix_meta = " · ".join([t for t in [size_txt, eta_txt] if t])
        suffix_meta = f" · {suffix_meta}" if suffix_meta else ""

        label = f"{partial_info}[{_colorize_status(status)}] {prefix_series}{middle_sxe}{name}{suffix_meta}"
        li = xbmcgui.ListItem(label=label)
        tag = li.getVideoInfoTag()
        tag.setTitle(name)
        li.setArt({'icon': 'DefaultHardDisk.png'})

        # Kontextové menu
        from resources.lib.utils.context_builder import build_context_menu
        item_data = {'queue_ident': item['ident']}
        ctx = build_context_menu(_build_url, item_data, contexts=['download_queue_item'])
        if ctx:
            li.addContextMenuItems(ctx, replaceItems=True)

        xbmcplugin.addDirectoryItem(handle, _build_url(action='noop'), li, isFolder=False)

    xbmcplugin.endOfDirectory(handle)


def stop_all_downloads():
    """
    Nastaví globální cancel flag pro všechny běžící stahování
    a ihned označí položky jako 'pending' místo 'cancelled'.
    """
    try:
        # Nastavit globální STOP flag (pro worker)
        win = xbmcgui.Window(10000)
        win.setProperty('mm_cancel_download_all', '1')

        # Načíst frontu a upravit statusy
        q = load_queue()
        for item in q:
            if item.get('status') in ('downloading', 'pending'):
                item['status'] = 'pending'
        save_queue(q)

        log.info("Globální STOP všech stahování (status reset na pending)")
        xbmcgui.Dialog().notification("Fronta", "Stahování zastaveno (položky vráceny do fronty).", xbmcgui.NOTIFICATION_WARNING, 2500)
        xbmc.executebuiltin("Container.Refresh")
    except Exception as e:
        log.error(f"CHYBA stop_all_downloads: {e}")
        xbmcgui.Dialog().notification("Fronta", "Chyba při zastavení.", xbmcgui.NOTIFICATION_ERROR, 2000)


# def reset_all_cancelled():
#     """Obnoví všechny položky se statusem 'cancelled' na 'pending' a vyčistí globální STOP flag."""
#     q = load_queue()
#     for i in q:
#         if i.get('status') == 'cancelled':
#             i['status'] = 'pending'
#     save_queue(q)
#     try:
#         # Vyčistit globální cancel flag
#         win = xbmcgui.Window(10000)
#         win.setProperty('mm_cancel_download_all', '0')
#     except Exception as e:
#         log.error(f"CHYBA při čištění cancel flagu: {e}")
#     xbmcgui.Dialog().notification("Fronta", "Obnoveno vše zrušené.", xbmcgui.NOTIFICATION_INFO, 2000)
#     xbmc.executebuiltin("Container.Refresh")



def get_queue_stats(avg_speed_mbps=10.0):
    """
    Vrátí statistiky fronty: (pending_count, downloading_count, total_gb, remaining_gb, eta_min).
    avg_speed_mbps = průměrná rychlost stahování (MB/s).
    """
    q = load_queue() or []
    pending_count = sum(1 for i in q if i.get('status') == 'pending')
    downloading_count = sum(1 for i in q if i.get('status') == 'downloading')
    total_bytes = sum(_to_int_safe(i.get('size')) for i in q)
    remaining_bytes = sum(_to_int_safe(i.get('size')) for i in q if i.get('status') in ('pending', 'downloading'))

    total_gb = round(total_bytes / (1024**3), 2)
    remaining_gb = round(remaining_bytes / (1024**3), 2)
    eta_min = None
    if avg_speed_mbps > 0 and remaining_bytes > 0:
        eta_sec = remaining_bytes / (avg_speed_mbps * 1024 * 1024)
        eta_min = int(eta_sec / 60)
    return pending_count, downloading_count, total_gb, remaining_gb, eta_min


def get_queue_summary():
    """
    Vrátí souhrn stavu fronty:
    {
        "downloading": int,
        "pending": int,
        "done": int,
        "error": int,
        "total": int
    }
    """
    try:
        q = load_queue() or []
        summary = {"downloading": 0, "pending": 0, "done": 0, "error": 0, "total": len(q)}
        for item in q:
            st = (item or {}).get("status", "").lower()
            if st == "downloading":
                summary["downloading"] += 1
            elif st == "pending":
                summary["pending"] += 1
            elif st == "done":
                summary["done"] += 1
            elif st == "error":
                summary["error"] += 1
        return summary
    except Exception as e:
        log.error(f"get_queue_summary error: {e}")
        return {"downloading": 0, "pending": 0, "done": 0, "error": 0, "total": 0}

