# -*- coding: utf-8 -*-
import json
import subprocess
import xbmc
import xbmcaddon
import xbmcgui
import os
from resources.lib.utils.log_utils import get_logger
from resources.lib.series.subcaps_cache import has_cz_audio, has_cz_subs, upsert_result, is_probed

log = get_logger()
from resources.lib.series.probe_helpers import parse_ffprobe_streams
from resources.lib.series.http_range_probe import run_http_range_probe, range_probe_result_to_subcaps_meta
from resources.lib.series.subcaps_cache import probe_backend

_CZECH_LANG_CODES = ('cs', 'cz', 'cze', 'ces')



def _get_ffprobe_path():
    """Plná cesta k ffprobe z nastavení doplňku (prázdné = probe vypnut)."""
    raw = (xbmcaddon.Addon().getSetting('ffprobe_path') or '').strip()
    if not raw:
        return ''
    return os.path.normpath(raw)


# --- ZÍSKÁNÍ PŘEHRÁVACÍHO LINKU (Webshare file_link) ------------------------
def _get_stream_url(api_function, token, ident: str, duuid: str) -> str:
    """
    Získá přehrávací URL stejně jako pro playback – Webshare 'file_link'.
    Pokud API nevrátí, vrací prázdný string.
    """
    try:
        resp = api_function('file_link', {
            'ident': ident,
            'wst': token,
            'device_uuid': duuid,
            'download_type': 'video_stream'
        })
        import xml.etree.ElementTree as ET
        xml = ET.fromstring(resp.content)
        status = (xml.findtext('status') or '').upper()
        link = xml.findtext('link') or ''
        # Loguj celou odpověď při neúspěchu pro snazší debugging
        if status != 'OK':
            msg = xml.findtext('message') or xml.findtext('error') or xml.findtext('reason') or ''
            raw_snip = resp.content[:300].decode('utf-8', 'ignore')
            log.info(f"file_link ident={ident} status={status} link_present={bool(link)} msg='{msg}' raw={raw_snip}")
            return ''
        log.info(f"file_link ident={ident} status={status} link_present={bool(link)}")
        return link
    except Exception as e:
        log.debug(f"file_link failed ident={ident}: {e}")
        return ''


# --- FFPROBE: plný JSON (video+audio+subs) + rychlý seznam jazyků SUBS ------
def _probe_with_ffprobe(url: str):
    """
    Spustí ffprobe se -show_streams a vrátí tuple (ffjson, subs_langs_list).
    - ffjson: dict (celý výstup ffprobe -show_streams)
    - subs_langs_list: např. ['cs','en'] z nalezených subtitle streamů
    """
    ffprobe = _get_ffprobe_path()
    if not ffprobe:
        log.info("ffprobe_path není nastaven – probe se přeskočí")
        return {}, []

    args = [
        ffprobe,
        "-v", "error",
        "-print_format", "json",
        "-show_streams",
        url
    ]
    log.info(f"ffprobe ARGS: {args}")

    try:
        # --- OPRAVA PRO PROBLIKÁVÁNÍ OKNA ---
        startupinfo = None
        if os.name == 'nt':  # Pokud běžíme na Windows
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = 0  # Konstanta pro SW_HIDE (skryté okno)
        # ------------------------------------

        p = subprocess.run(
            args, 
            shell=False, 
            stdout=subprocess.PIPE, 
            stderr=subprocess.PIPE, 
            timeout=60,
            startupinfo=startupinfo  # Přidáno sem
        )
        
        if p.returncode != 0:
            stderr = p.stderr.decode('utf-8', 'ignore')
            log.warning(f"ffprobe rc={p.returncode} stderr={stderr}")
            return {}, []

        raw = p.stdout.decode('utf-8', 'ignore') or '{}'
        log.debug(f"ffprobe STDOUT (trunc 512): {raw[:512]}")

        ffjson = json.loads(raw)
        # ... zbytek tvého kódu pro zpracování JSON ...
        
        langs = []
        for s in (ffjson.get('streams') or []):
            if s.get('codec_type') == 'subtitle':
                lang = ((s.get('tags') or {}).get('language') or '').lower()
                if lang:
                    langs.append(lang)

        log.info(f"detekované titulky: {langs}")
        return ffjson, langs

    except Exception as e:
        log.warning(f"ffprobe failed: {e}")
        return {}, []

# --- HLAVNÍ SEZÓNNÍ SKEN ----------------------------------------------------
def scan_season_for_cz_subs(series_manager, series_name: str, season: int, api_function, token,
                            *, max_streams_per_episode=0):
    """
    Projde epizody v sezóně, pro každý (max N; 0=VŠECHNY) stream získá URL a zkusí detekovat CZ titulky.
    NOVĚ: výsledky ffprobe (audio+video+subs) ukládá do subcaps_cache.json (upsert_result).
    Spouštěj POUZE pokud heuristika A nenašla CZ (šetříme čas).
    """
    data = series_manager.load_series_data(series_name) or {}
    season_key = str(season)
    season_dict = (data.get('seasons') or {}).get(season_key) or {}

    dlg = xbmcgui.DialogProgress()
    dlg.create("Detekce CZ titulků", f"{series_name} • Sezóna {season}")
    total_eps = max(1, len(season_dict))
    done = 0

    ffprobe_path = _get_ffprobe_path()
    probe_mode = "ffprobe" if ffprobe_path else "http-range"
    log.info(
        f"START series='{series_name}' season={season} episodes={len(season_dict)} "
        f"max_streams_per_episode={max_streams_per_episode} probe_mode={probe_mode}"
    )

    session = None
    if not ffprobe_path:
        try:
            import requests
            session = requests.Session()
        except Exception as ex:
            log.warning(f"requests Session unavailable for range probe: {ex}")

    found_count = 0
    skipped_cz_already = 0
    probed_total = 0

    try:
        for e_key in sorted(season_dict.keys(), key=lambda x: int(x) if str(x).isdigit() else x):
            if dlg.iscanceled():
                log.info("CANCELLED by user")
                break

            ep = season_dict.get(e_key) or {}
            streams = (ep.get('streams') or [])[:]
            s_tag = f"S{int(season):02d}E{int(e_key):02d}"
            log.info(f"{s_tag} streams_in_episode={len(streams)}")

            if max_streams_per_episode and max_streams_per_episode > 0:
                to_probe = streams[:max_streams_per_episode]
            else:
                to_probe = streams

            log.info(f"{s_tag} probe_count={len(to_probe)}")

            found_cz_for_ep = False

            for idx, st in enumerate(to_probe, start=1):
                ident = st.get('ident')
                if not ident:
                    log.debug(f"{s_tag} stream#{idx}: MISSING ident -> skip")
                    continue

                if is_probed(ident):
                    if has_cz_subs(ident):
                        found_cz_for_ep = True
                    log.info(f"{s_tag} stream#{idx} ident={ident}: cache hit (already probed) -> skip")
                    continue

                duuid = series_manager.addon.getSetting('duuid')
                url = _get_stream_url(api_function, token, ident, duuid)
                if not url:
                    log.debug(f"{s_tag} stream#{idx} ident={ident}: NO url -> skip")
                    continue

                probed_total += 1
                langs = []
                try:
                    if ffprobe_path:
                        ffjson, langs = _probe_with_ffprobe(url)
                        meta = parse_ffprobe_streams(ffjson)
                        if langs:
                            norm = [x.lower() for x in langs]
                            meta["cz_subs"] = any(x in _CZECH_LANG_CODES for x in norm)
                            meta["subs_langs"] = norm
                        upsert_result(ident, meta, ffprobe_version="windows-ffprobe", series_name=series_name)
                    else:
                        pr = run_http_range_probe(url, session=session)
                        meta = range_probe_result_to_subcaps_meta(pr)
                        langs = list(meta.get("subs_langs") or [])
                        if langs:
                            norm = [x.lower() for x in langs]
                            meta["cz_subs"] = any(x in _CZECH_LANG_CODES for x in norm)
                            meta["subs_langs"] = norm
                        upsert_result(ident, meta, ffprobe_version="http-range-v1", series_name=series_name)
                    log.info(f"CACHE UPDATED ident={ident} → {meta}")
                except Exception as ex:
                    log.warning(f"cache upsert failed ident={ident}: {ex}")

                is_cz = False
                if langs:
                    norm = [x.lower() for x in langs]
                    is_cz = any(x in _CZECH_LANG_CODES for x in norm)

                if is_cz:
                    found_cz_for_ep = True
                else:
                    log.debug(f"{s_tag} stream#{idx} ident={ident}: CZ titulky nenalezeny")

            if found_cz_for_ep:
                found_count += 1
                try:
                    dlg.update(int(done/total_eps*100), f"{s_tag} • CZ titulky nalezeny")
                except Exception:
                    pass
            else:
                try:
                    dlg.update(int(done/total_eps*100), f"{s_tag} • CZ titulky nenalezeny")
                except Exception:
                    pass

            done += 1
    finally:
        if session is not None:
            try:
                session.close()
            except Exception:
                pass

    try:
        dlg.close()
    except Exception:
        pass

    # Write-back features podle subcaps cache: pro každý stream, který už
    # byl proben (is_probed), přepíšeme CZdab/CZtit ve features podle
    # autoritativního výsledku probe. Tím se vyčistí filename-derived FP
    # (např. `EN.cz.titulky.mkv` s SK audiem už nebude označen jako CZdab).
    try:
        changed = _writeback_features_from_cache(data, season_key)
        if changed > 0:
            series_manager._save_series_data(series_name, data)
            log.info(
                f"FEATURES WRITE-BACK series='{series_name}' season={season} "
                f"streams_changed={changed}"
            )
    except Exception as ex:
        log.warning(f"features write-back failed: {ex}")

    log.info(
        f"DONE series='{series_name}' season={season} "
        f"found_cz_eps={found_count} skipped_already_cz={skipped_cz_already} probed_streams={probed_total}"
    )


def scan_episode_for_cz_tags(series_manager, series_name: str, season: int, episode: int,
                             api_function, token, *, max_streams=0):
    """
    Projde pouze streamy jedné epizody a uloží autoritativní CZdab/CZtit výsledek
    do subcaps_cache + features epizody.
    """
    data = series_manager.load_series_data(series_name) or {}
    season_key = str(season)
    episode_key = str(episode)
    ep = ((data.get('seasons') or {}).get(season_key) or {}).get(episode_key) or {}
    streams = (ep.get('streams') or [])[:]

    if max_streams and max_streams > 0:
        to_probe = streams[:max_streams]
    else:
        to_probe = streams

    dlg = xbmcgui.DialogProgress()
    dlg.create("Detekce CZdab/CZtit", f"{series_name} • S{int(season):02d}E{int(episode):02d}")

    ffprobe_path = _get_ffprobe_path()
    session = None
    if not ffprobe_path:
        try:
            import requests
            session = requests.Session()
        except Exception as ex:
            log.warning(f"requests Session unavailable for range probe: {ex}")

    found_dab = False
    found_tit = False
    probed_total = 0
    cache_hits = 0

    try:
        total = max(1, len(to_probe))
        for idx, st in enumerate(to_probe, start=1):
            if dlg.iscanceled():
                log.info("CANCELLED by user")
                break

            ident = (st or {}).get('ident')
            pct = int((idx - 1) / total * 100)
            try:
                dlg.update(pct, f"S{int(season):02d}E{int(episode):02d} • stream {idx}/{total}")
            except Exception:
                pass

            if not ident:
                continue

            probed = is_probed(ident)
            if probed:
                cache_hits += 1
                backend = probe_backend(ident)
                found_dab = found_dab or has_cz_audio(ident)
                found_tit = found_tit or has_cz_subs(ident)
                log.info(
                    f"S{int(season):02d}E{int(episode):02d} ident={ident}: "
                    f"cache hit backend={backend} found_dab={found_dab} found_tit={found_tit}"
                )
                continue
            else:
                log.debug(
                    f"S{int(season):02d}E{int(episode):02d} ident={ident}: "
                    "cache miss (is_probed=False), running probe"
                )

            duuid = series_manager.addon.getSetting('duuid')
            url = _get_stream_url(api_function, token, ident, duuid)
            if not url:
                log.debug(f"S{int(season):02d}E{int(episode):02d} ident={ident}: NO url -> skip")
                continue

            probed_total += 1
            try:
                if ffprobe_path:
                    ffjson, langs = _probe_with_ffprobe(url)
                    meta = parse_ffprobe_streams(ffjson)
                    if langs:
                        norm = [x.lower() for x in langs]
                        meta["cz_subs"] = any(x in _CZECH_LANG_CODES for x in norm)
                        meta["subs_langs"] = norm
                    upsert_result(ident, meta, ffprobe_version="windows-ffprobe", series_name=series_name)
                else:
                    pr = run_http_range_probe(url, session=session)
                    meta = range_probe_result_to_subcaps_meta(pr)
                    langs = list(meta.get("subs_langs") or [])
                    if langs:
                        norm = [x.lower() for x in langs]
                        meta["cz_subs"] = any(x in _CZECH_LANG_CODES for x in norm)
                        meta["subs_langs"] = norm
                    upsert_result(ident, meta, ffprobe_version="http-range-v1", series_name=series_name)

                found_dab = found_dab or bool(meta.get("has_cz_audio"))
                found_tit = found_tit or bool(meta.get("cz_subs"))
                log.info(f"EPISODE CACHE UPDATED ident={ident} → {meta}")
            except Exception as ex:
                log.warning(f"episode probe failed ident={ident}: {ex}")
    finally:
        if session is not None:
            try:
                session.close()
            except Exception:
                pass
        try:
            dlg.close()
        except Exception:
            pass

    changed = 0
    try:
        changed = _writeback_features_from_cache(data, season_key, episode_key=episode_key)
        if changed > 0:
            series_manager._save_series_data(series_name, data)
            log.info(
                f"EPISODE FEATURES WRITE-BACK series='{series_name}' "
                f"S{int(season):02d}E{int(episode):02d} streams_changed={changed}"
            )
    except Exception as ex:
        log.warning(f"episode features write-back failed: {ex}")

    return {
        "found_dab": found_dab,
        "found_tit": found_tit,
        "probed": probed_total,
        "cache_hits": cache_hits,
        "changed": changed,
        "streams": len(to_probe),
    }


def _writeback_features_from_cache(data: dict, season_key: str, episode_key: str = None) -> int:
    """Přepíše `st['features']` u všech streamů v dané sezóně podle cache.

    Pravidla pro stream s `is_probed(ident) == True`:
      - odstraní všechny CZ jazykové tokeny (CZdab/CZtit + legacy CZ/CZsub/
        CZaudio + kombinace `CZ-…`),
      - přidá `CZdab` pokud `subcaps_cache.has_cz_audio(ident)`,
      - přidá `CZtit` pokud `subcaps_cache.has_cz_subs(ident)`,
      - ostatní featury (HDR/5.1/HEVC/2160p/…) zůstávají beze změny.
    Stream bez probe (`is_probed == False`) se nemění — pro tyto zůstává
    původní filename heuristika z `extract_features` jako fallback.

    Vrací počet skutečně změněných streamů (idempotence — žádný zbytečný save).
    """
    if not isinstance(data, dict):
        return 0
    seasons = data.get('seasons') or {}
    season = seasons.get(str(season_key)) or {}
    if not isinstance(season, dict):
        return 0

    from resources.lib.series.series_helpers import (
        CZ_DAB,
        CZ_TIT,
        normalize_language_features,
    )
    from resources.lib.series import subcaps_cache

    changed_streams = 0
    episode_keys = [str(episode_key)] if episode_key is not None else list(season.keys())
    for e_key in episode_keys:
        ep = season.get(e_key) or {}
        if not isinstance(ep, dict):
            continue
        for st in (ep.get('streams') or []):
            if not isinstance(st, dict):
                continue
            ident = st.get('ident')
            if not ident:
                continue
            if not subcaps_cache.is_probed(ident):
                continue

            old_feats = list(st.get('features') or [])
            # Normalizuj (CZ→CZdab, CZsub→CZtit, rozklad CZ-…) a vyhoď CZ tagy.
            non_cz = [
                f for f in normalize_language_features(old_feats)
                if f not in (CZ_DAB, CZ_TIT)
            ]
            new_feats = list(non_cz)
            if subcaps_cache.has_cz_audio(ident):
                new_feats.insert(0, CZ_DAB)
            if subcaps_cache.has_cz_subs(ident):
                insert_at = 1 if (new_feats and new_feats[0] == CZ_DAB) else 0
                new_feats.insert(insert_at, CZ_TIT)

            if new_feats != old_feats:
                st['features'] = new_feats
                # label se odvozuje od features — udržme ho v sync.
                st['label'] = ', '.join(new_feats) if new_feats else (
                    (st.get('quality') or '').strip()
                    or (st.get('name') or '').strip()
                    or 'Stream'
                )
                changed_streams += 1
    return changed_streams