# -*- coding: utf-8 -*-
# resources/lib/history/scrobble.py
# Scrobbling do Trakt + lokální historie
# - jednotný log prefix [SCROBBLE]
# - TMDb parsování epizod přes tmdb_utils
# - ukládání stream_url do lokální historie
# - žádné přímé čtení Trakt client_id/token (jen get_trakt_headers())

import json
import requests
import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
from datetime import datetime
import time  # používá se v progress_scrobble()
from resources.lib.history import local_history
from resources.lib.tmdb import tmdb_utils
from resources.lib.trakt.trakt_service import get_trakt_headers, TraktAPI  # headers + refresh
from resources.lib.database.metadata_db import db # 🚨 NOVÝ IMPORT: Předpokládáme tuto cestu k databázi metadat
from resources.lib.utils.log_utils import get_logger
from typing import Any, Dict, Optional

log = get_logger()
PREFIX = "[SCROBBLE]"
ADDON_ID = 'plugin.video.mmirousek_v3'
TRAKT_API_SCROBBLE_URL = 'https://api.trakt.tv/scrobble'

# stav (info/debug)
_last_known_progress = 0.0
_progress_updated = False
_ADDON_INSTANCE = None


# ============================== TRAKT POST HELPER ===========================

def _trakt_scrobble_post(endpoint: str, data: dict, headers: dict) -> Optional[requests.Response]:
    """
    POST na Trakt /scrobble/* s auto-refresh tokenem při 401.
    Vrací Response nebo None při fatální chybě.
    """
    url = f"{TRAKT_API_SCROBBLE_URL}/{endpoint}"
    try:
        # Debug log pro diagnostiku 401
        log.info(f'{PREFIX}[API] POST {url}')
        log.debug(f'{PREFIX}[API] Headers keys: {list(headers.keys())}')
        log.debug(f'{PREFIX}[API] Payload keys: {list(data.keys())}')
        resp = requests.post(url, json=data, headers=headers, timeout=10)
        log.info(f'{PREFIX}[API] Response: HTTP {resp.status_code}')
        if resp.status_code == 401:
            log.warning(f'{PREFIX}[API] 401 – pokouším se obnovit token...')
            try:
                api = TraktAPI()
                if api.refresh_token_if_needed(force=True, notify_on_success=False):
                    new_headers = get_trakt_headers()
                    if new_headers:
                        resp = requests.post(url, json=data, headers=new_headers, timeout=10)
                        log.info(f'{PREFIX}[API] Retry po refreshi: HTTP {resp.status_code}')
            except Exception as e:
                log.error(f'{PREFIX}[API] Refresh selhal: {e}')
        return resp
    except Exception as e:
        log.error(f'{PREFIX}[API] Síťová chyba: {e}')
        return None


# ============================== UTIL / COMMON ===============================

def _get_addon_instance():
    """Lazy načtení Addon instance s ošetřením chyb."""
    global _ADDON_INSTANCE
    if _ADDON_INSTANCE is None:
        try:
            _ADDON_INSTANCE = xbmcaddon.Addon(ADDON_ID)
        except Exception as e:
            log.error(f'{PREFIX}[UTIL] CHYBA inicializace ADDON: {e}')
            _ADDON_INSTANCE = False
    return _ADDON_INSTANCE if _ADDON_INSTANCE is not False else None


def _get_log_tag():
    """Sestaví čitelný tag s názvem addonu."""
    ADDON = _get_addon_instance()
    name = ADDON.getAddonInfo("name") if ADDON else "StreamManager"
    return f'{PREFIX}[{name}]'

# resources/lib/history/scrobble.py

# ... (na konci souboru, nebo nad _clean_settings)

def build_payload_from_id(tmdb_id: str, mediatype: str) -> Optional[Dict[str, Any]]:
    """
    Sestaví Trakt payload z TMDB ID. Data získá z Kodi ListItem (nejrychlejší) 
    nebo lokální DB (fallback).
    """
    try:
        if not tmdb_id:
            return None
        
        tmdb_id = str(tmdb_id)
        
        # 1. Získání informací z Kodi ListItem (nejrychlejší)
        # Zde jsou data, která nastavuje vaše listovací funkce (Trakt/TMDb moduly)
        title = xbmc.getInfoLabel('ListItem.Title')
        year = xbmc.getInfoLabel('ListItem.Year')

        # 2. Fallback: z lokální DB
        if not title or title in ('', 'ListItem.Title', '0'):
            # Použijeme lokální DB pro získání CZ/ENG názvů a roku
            metadata = db.get_metadata_by_tmdb_id(tmdb_id)
            if metadata:
                title = metadata.get('eng_title') or metadata.get('cz_title')
                year = metadata.get('year')

        if not title:
            log.warning(f'{PREFIX} Nenalezen název pro ID {tmdb_id}. Payload nelze sestavit.')
            return None

        # 3. Sestavení payloadu (Filmy)
        if mediatype == 'movie':
            payload = {
                'movie': {
                    'title': title,
                    'year': int(year) if year and str(year).isdigit() else None,
                    'ids': {'tmdb': int(tmdb_id)}
                }
            }
            return payload

        # 4. Sestavení payloadu (Seriály/Epizody) - Pro budoucí implementaci
        elif mediatype == 'episode':
            # Potřebujeme S/E, které musí být uloženo ve vlastnostech ListItem!
            season_num = xbmc.getInfoLabel('ListItem.Season')
            episode_num = xbmc.getInfoLabel('ListItem.Episode')
            show_title = xbmc.getInfoLabel('ListItem.TVShowTitle')

            if not season_num or not episode_num:
                log.error(f'{PREFIX} Chyba: Pro epizodu {tmdb_id} chybí S/E v ListItem.')
                return None
            
            payload = {
                'show': {
                    'title': show_title or title, # Titul seriálu
                    'year': int(year) if year and str(year).isdigit() else None,
                    'ids': {'tmdb': int(tmdb_id)} # Zde by mělo být TMDB ID seriálu, nikoli epizody
                },
                'episode': {
                    'season': int(season_num),
                    'number': int(episode_num),
                    'ids': {'tmdb': int(tmdb_id)} # Zde by mělo být TMDB ID epizody
                }
            }
            # POZN: Pro serialy se musí TMDB ID epizody a serialu získat ze dvou různých ListItem.Property, 
            # ale toto je základ pro refactoring. Vratme se k tomu, až vyřešíme filmy.
            return payload

        return None
    
    except Exception as e:
        log.error(f'{PREFIX} CHYBA při sestavování payloadu z ID: {e}')
        return None


# --- NOVÉ: jednotné uzamknutí payloadu do settings ---
def lock_payload(payload: dict, last_stream_url: Optional[str] = None, init_progress: float = 0.0) -> None:
    """
    Uloží (uzamkne) Trakt payload do settings + volitelně zapíše URL streamu.
    Používá čerstvou Addon instanci, aby se promítly změny i pro jiné čtenáře.
    """
    try:
        addon = xbmcaddon.Addon(ADDON_ID)
        addon.setSetting('trakt_last_payload', json.dumps(payload))
        addon.setSetting('trakt_payload_locked', 'true')
        addon.setSetting('trakt_last_progress', f'{init_progress:.1f}')
        addon.setSetting('trakt_progress_updated', 'true')
        if last_stream_url:
            addon.setSetting('last_stream_url', last_stream_url)
        log.info(f'{PREFIX}[LOCK] Payload uzamknut, init_progress={init_progress:.1f}')
    except Exception as e:
        log.error(f'{PREFIX}[LOCK] CHYBA: {e}')

# --- NOVÉ: builder payloadu pro EPIZODU z dodaných ID ---
def build_episode_payload(show_tmdb_id: int,
                          episode_tmdb_id: Optional[int],
                          season_num: int,
                          episode_num: int,
                          show_title: Optional[str] = None,
                          show_year: Optional[int] = None,
                          episode_title: Optional[str] = None) -> Optional[Dict[str, Any]]:
    """
    Sestaví Trakt payload pro epizodu. Vloží TMDb ID seriálu i epizody na správná místa.
    """
    try:
        if not (show_tmdb_id and season_num and episode_num):
            return None
        payload = {
            "show": {
                "title": show_title or "",
                "year": int(show_year) if show_year and str(show_year).isdigit() else None,
                "ids": {"tmdb": int(show_tmdb_id)}
            },
            "episode": {
                "season": int(season_num),
                "number": int(episode_num),
                "title": episode_title or "",
            }
        }
        if episode_tmdb_id and str(episode_tmdb_id).isdigit():
            payload["episode"]["ids"] = {"tmdb": int(episode_tmdb_id)}
        return payload
    except Exception as e:
        log.error(f'{PREFIX}[EP PAYLOAD] CHYBA: {e}')
        return None

# --- NOVÉ: builder payloadu pro EPIZODU přímo z SeriesManager JSONu ---
def build_episode_payload_from_db(series_name: str, season_num: int, episode_num: int) -> Optional[Dict[str, Any]]:
    """
    Načte data ze SeriesManager cache a sestaví přesný Trakt payload pro epizodu.
    Použije tmdb.id (show) a tmdb.episode_id (epizoda), názvy, rok (pokud dohledatelný).
    """
    try:
        from resources.lib.series import series_manager
        ADDON = xbmcaddon.Addon(ADDON_ID)
        profile_path = xbmcvfs.translatePath(ADDON.getAddonInfo('profile'))
        sm = series_manager.SeriesManager(ADDON, profile_path)
        data = sm.load_series_data(series_name)
        if not data:
            log.warning(f'{PREFIX}[EP PAYLOAD] Data seriálu nenalezena: {series_name}')
            return None

        show_tmdb_id = (data.get('tmdb') or {}).get('id')
        show_title = (data.get('tmdb') or {}).get('title') or series_name
        show_year = (data.get('tmdb') or {}).get('year')

        season = (data.get('seasons') or {}).get(str(season_num), {})
        ep = season.get(str(episode_num), {}) if isinstance(season, dict) else {}
        tm = ep.get('tmdb', {}) if isinstance(ep, dict) else {}

        episode_tmdb_id = (
            tm.get('episode_id')  # preferovaný klíč
            or tm.get('id')       # fallback: některé verze ukládají `id`
            or tm.get('tmdb_id')  # fallback: explicitní tmdb_id
        )
        episode_title = tm.get('title') or ep.get('name') or ep.get('title') or ''

        if not show_tmdb_id:
            log.warning(f'{PREFIX}[EP PAYLOAD] Chybí TMDb ID seriálu | show={show_tmdb_id}, ep={episode_tmdb_id}')
            return None

        if not episode_tmdb_id:
            # Trakt payload pro epizodu funguje i bez `episode.ids`, když máme show+S/E.
            log.info(f'{PREFIX}[EP PAYLOAD] episode_tmdb_id chybí, používám show+season+number fallback | show={show_tmdb_id}')

        return build_episode_payload(show_tmdb_id, episode_tmdb_id, int(season_num), int(episode_num),
                                     show_title=show_title, show_year=show_year, episode_title=episode_title)
    except Exception as e:
        log.error(f'{PREFIX}[EP PAYLOAD] CHYBA build from DB: {e}')
        return None


def _clean_settings():
    """Vyčistí dočasné položky scrobble stavu v settings + odemkne lock."""
    try:
        addon = xbmcaddon.Addon(ADDON_ID)
        addon.setSetting('trakt_payload_locked', 'false')
        addon.setSetting('trakt_last_payload', '')
        addon.setSetting('trakt_last_progress', '')
        addon.setSetting('trakt_progress_updated', '')
    except Exception as e:
        log.error(f'{PREFIX}[UTIL] Chyba při čištění nastavení: {e}')


# ============================= DATA O PŘEHRÁVANÉM ===========================

def get_item_data() -> Optional[Dict[str, Any]]:
    """
    Načte scrobble payload. Primárně se spoléhá na předem uzamčený payload 
    uložený modulem scrobble_delay.py, který získal spolehlivé ID z ListItem.
    """
    addon = xbmcaddon.Addon(ADDON_ID)
    locked_payload_json = addon.getSetting('trakt_last_payload')
    is_locked = addon.getSetting('trakt_payload_locked') == 'true'

    if locked_payload_json and is_locked:
        try:
            payload = json.loads(locked_payload_json)
            # Logování pro potvrzení úspěšného načtení
            log.info(f'{PREFIX} Načítám uzamčený payload z nastavení. Typ: {payload.get("movie", {}).get("title") or payload.get("show", {}).get("title")}')
            return payload
        except Exception as e:
            log.error(f'{PREFIX} Chyba parsování uzamčeného payloadu: {e}')

    # Fallback pro případ, že scrobble_delay selhal nebo není spuštěn
    # Nyní zde už nemáme nespolehlivý regex, takže se vrátí None
    log.warning(f'{PREFIX} Uzamčený payload nenalezen. Přehrávání NEBUDE scroblováno na Trakt.')
    return None
# ================================== TIK =====================================


def trakt_progress_scrobble(payload: dict, progress: float):
    """
    Odešle periodický progress update na Trakt API.
    Používá /scrobble/start s aktuálním progress – Trakt to zpracuje jako update.
    """
    try:
        addon = _get_addon_instance()
        if not addon or not addon.getSettingBool('trakt_enabled'):
            return

        headers = get_trakt_headers(for_post=True)
        if not headers:
            return

        # Očistit payload od lokálních klíčů
        trakt_data = payload.copy()
        for k in ('stream_url', 'name', 'tmdb_id', 'trakt_payload', 'media_type', 'watched_at'):
            trakt_data.pop(k, None)

        data = {"progress": min(progress, 99.9), **trakt_data}
        resp = _trakt_scrobble_post('start', data, headers)
        if resp and resp.status_code in (201, 409):
            log.debug(f'{PREFIX}[PROGRESS] Trakt progress update odeslán: {progress:.1f}%')
        elif resp:
            log.error(f'{PREFIX}[PROGRESS] Trakt progress update chyba HTTP {resp.status_code}')
    except Exception as e:
        log.error(f'{PREFIX}[PROGRESS] Chyba: {e}')


def progress_scrobble():
    """
    Volá se z hlavní smyčky service.py (každou sekundu).
    - Ukládá aktuální progress do settings + in-memory.
    - Pokud je lock aktivní a uplynul interval (60s nebo >1 % posun),
      odešle progress update na Trakt.
    DŮLEŽITÉ: Vytváříme čerstvou Addon instanci, protože lock nastavuje
    play_handler z jiného kontextu a cached singleton by neviděl změnu.
    """
    try:
        addon = xbmcaddon.Addon(ADDON_ID)
    except Exception:
        return

    # Kontrola zámku – bez locku není co scrobblovat
    lock_val = addon.getSetting('trakt_payload_locked')
    if lock_val != 'true':
        return

    # Uložit aktuální progress (lokálně)
    player = xbmc.Player()
    if not player.isPlaying():
        return

    try:
        current_time = player.getTime()
        total_time = player.getTotalTime()
        if total_time <= 0:
            return

        progress = (current_time / total_time) * 100.0

        # Zápis do settings (pro scrobble_stop)
        addon.setSetting('trakt_last_progress', f'{progress:.1f}')
        addon.setSetting('trakt_progress_updated', 'true')

        # In-memory záloha
        global _last_known_progress, _progress_updated
        _last_known_progress = progress
        _progress_updated = True

        # --- Periodické odesílání na Trakt ---
        try:
            last_scrobble_progress_str = addon.getSetting('trakt_last_scrobble_progress') or '0'
            last_scrobble_progress = float(last_scrobble_progress_str)
        except (ValueError, TypeError):
            last_scrobble_progress = 0.0

        progress_diff = abs(progress - last_scrobble_progress)

        try:
            last_scrobble_time_str = addon.getSetting('trakt_last_scrobble_time') or '0'
            last_scrobble_time = int(last_scrobble_time_str)
        except (ValueError, TypeError):
            last_scrobble_time = 0

        elapsed_time = time.time() - last_scrobble_time

        send_scrobble = elapsed_time > 60 or progress_diff >= 1.0

        if send_scrobble:
            item = get_item_data()
            if item:
                trakt_progress_scrobble(item, progress)
                addon.setSetting('trakt_last_scrobble_progress', f'{progress:.1f}')
                addon.setSetting('trakt_last_scrobble_time', str(int(time.time())))

    except Exception as e:
        log.warning(f'{PREFIX}[TIK] Chyba progress smyčky: {e}')

# ================================= START ====================================

def scrobble_start(item=None, fallback_title=None):
    """
    Připraví payload a pošle Trakt /start.
    Pokud je item předán jako parametr (z play_handler), použije ho přímo.
    Jinak čte uzamčený payload ze settings.
    """
    LOG = _get_log_tag()
    addon = xbmcaddon.Addon(ADDON_ID)  # vždy čerstvá instance

    # 1) Item přímo předaný (z play_handler) – nejspolehlivější
    if item is not None:
        log.info(f'{LOG} [START] Používám přímo předaný payload.')
    else:
        # 2) Start z lokální historie („Naposledy sledované")
        history_item_json = addon.getSetting('trakt_launch_history_item')
        if history_item_json:
            addon.setSetting('trakt_launch_history_item', '')
            history_item = json.loads(history_item_json)
            trakt_payload = history_item.get('trakt_payload')
            if trakt_payload:
                item = history_item.copy()
                item.update(trakt_payload)
                log.info(f'{LOG} [START] Rekonstruován Trakt payload z lokální historie.')
            else:
                log.warning(f'{LOG} [START] Chybí trakt_payload v lokální historii.')

        # 3) Fallback: čtení uzamčeného payloadu ze settings (čerstvá instance)
        if not item:
            payload_json = addon.getSetting('trakt_last_payload')
            if payload_json and payload_json.strip():
                try:
                    item = json.loads(payload_json)
                    log.info(f'{LOG} [START] Payload načten ze settings.')
                except Exception as e:
                    log.error(f'{LOG} [START] Chyba parsování payloadu: {e}')

        if not item:
            log.warning(f'{LOG} [START] Žádná data – přeskočeno.')
            return

    # Získat stream URL pro lokální historii
    try:
        player = xbmc.Player()
        if player.isPlaying():
            stream_url = player.getPlayingFile()
            if stream_url:
                item['stream_url'] = stream_url
    except Exception:
        pass

    # Uložit payload do settings (pro stop/progress) – čerstvá instance
    try:
        addon.setSetting('trakt_last_payload', json.dumps(item))
        addon.setSetting('trakt_last_progress', '0.0')
        addon.setSetting('trakt_progress_updated', 'true')
        log.info(f'{LOG} [START] Payload a progress init uložen.')
    except Exception as e:
        log.error(f'{LOG} [START] Chyba uložení payloadu: {e}')

    # Odeslat START na Trakt
    if not addon.getSettingBool('trakt_enabled'):
        log.info(f'{LOG} [START] Trakt je zakázán.')
        return

    headers = get_trakt_headers(for_post=True)
    if not headers:
        log.info(f'{LOG} [START] Trakt není autorizován.')
        return

    # Očistit payload pro Trakt
    trakt_payload = item.copy()
    for k in ('stream_url', 'name', 'tmdb_id', 'trakt_payload', 'media_type', 'watched_at'):
        trakt_payload.pop(k, None)

    data = {"progress": 0.1, **trakt_payload}
    r = _trakt_scrobble_post('start', data, headers)
    if r and r.status_code in (201, 409):
        log.info(f'{LOG} [START] Úspěch: {item.get("movie", {}).get("title") or item.get("show", {}).get("title")}')
        # Krátká notifikace při startu
        title = item.get("movie", {}).get("title") or item.get("show", {}).get("title") or "Titul"
        xbmcgui.Dialog().notification("Trakt.tv", f"Start: {title}", xbmcgui.NOTIFICATION_INFO, 2000)
    elif r:
        log.error(f'{LOG} [START] CHYBA HTTP {r.status_code}')


# ================================= PAUSE ====================================

def scrobble_pause():
    """Odešle Trakt /pause (pokud povoleno a autorizováno)."""
    LOG = _get_log_tag()

    # Načíst uložený payload
    item = None
    try:
        addon = xbmcaddon.Addon(ADDON_ID)
        payload_json = addon.getSetting('trakt_last_payload')
        if payload_json:
            item = json.loads(payload_json)
            log.info(f'{LOG} [PAUSE] Payload načten.')
    except Exception as e:
        log.warning(f'{LOG} [PAUSE] Chyba načtení payloadu: {e}')

    if not item:
        log.warning(f'{LOG} [PAUSE] Žádný payload – přeskočeno.')
        return

    addon = _get_addon_instance()
    if addon is None or not addon.getSettingBool('trakt_enabled'):
        log.info(f'{LOG} [PAUSE] Trakt je zakázán – ignoruji.')
        return

    headers = get_trakt_headers(for_post=True)
    if not headers:
        log.info(f'{LOG} [PAUSE] Trakt není autorizován – ignoruji.')
        return

    trakt_payload = item.copy()
    # Ponechává jen Trakt-specifické klíče (movie/show/episode)
    for k in ('stream_url', 'name', 'tmdb_id', 'trakt_payload', 'media_type', 'watched_at'):
        trakt_payload.pop(k, None)    
    
    data = {"progress": 0.0, **trakt_payload}

    try:
        r = _trakt_scrobble_post('pause', data, headers)
        if r and r.status_code in (201, 409):
            log.info(f'{LOG} [PAUSE] ÚSPĚCH')
            # Krátká notifikace při pauze
            title = item.get("movie", {}).get("title") or item.get("show", {}).get("title") or "Titul"
            xbmcgui.Dialog().notification("Trakt.tv", f"Pozastaveno: {title}", xbmcgui.NOTIFICATION_INFO, 2000)
        elif r:
            log.error(f'{LOG} [PAUSE] CHYBA HTTP {r.status_code}')
        else:
            log.error(f'{LOG} [PAUSE] Žádná odpověď (síť?)')
    except Exception as e:
        log.error(f'{LOG} [PAUSE] Chyba: {e}')


# ================================= STOP =====================================

def scrobble_stop():
    """
    Při stopu:
    - Načte payload + progress z settings.
    - progress < 90 % → uloží jako rozkoukáno (pokud >5 %), VŽDY uloží do lokální historie.
    - progress >= 90 % → pošle Trakt /stop (pokud povoleno) a uloží zhlédnuto do lokální historie.
    - epizody: uloží progress do SeriesManager + pošle Trakt STOP.
    - vyčistí dočasné hodnoty a odemkne lock.
    """
    LOG = _get_log_tag()
    log.info(f'{LOG} [STOP] Spouštím...')

    fresh_addon = xbmcaddon.Addon(ADDON_ID)  # Jedna čerstvá instance pro celý stop
    if fresh_addon is None:
        log.error(f'{LOG} [STOP] Nelze získat instanci Addonu – konec.')
        return

    # --- Payload z settings ---
    item = None
    try:
        payload_json = fresh_addon.getSetting('trakt_last_payload')
        log.debug(f'{PREFIX}[STOP] Načteno z disku: {payload_json}')
        if payload_json and payload_json.strip():
            item = json.loads(payload_json)
            log.info(f'{PREFIX}[STOP] Payload načten.')
        else:
            log.warning(f'{PREFIX}[STOP] Payload prázdný – přeskočeno.')
    except Exception as e:
        log.error(f'{PREFIX}[STOP] Chyba načítání payloadu: {e}')

    # 🚨 ZMĚNA: ODSTRANĚN NESPELJIVÝ FALLBACK
    if not item:
        log.error(f'{PREFIX}[STOP] Žádná data (Trakt payload nebyl nalezen). Čistím settings a končím.')
        # Nyní MUSÍME vyčistit settings
        _clean_settings()
        try:
            # Explicitně odemkneme, aby se systém nerozbil
            fresh_addon.setSetting('trakt_payload_locked', 'false')
            fresh_addon.setSetting('trakt_last_payload', '')
            fresh_addon.setSetting('trakt_last_progress', '0.0')
            fresh_addon.setSetting('trakt_progress_updated', 'false')
        except Exception:
            pass
        return

    # --- Progress --- (Logika zůstává stejná, jen používá fresh_addon)
    progress = 0.0
    updated = False
    try:
        xbmc.sleep(200)  # malá prodleva, aby se stihl uložit poslední tik
        prog_str = fresh_addon.getSetting('trakt_last_progress')
        upd_str = fresh_addon.getSetting('trakt_progress_updated')
        if prog_str and upd_str == 'true':
            progress = float(prog_str)
            updated = True
            log.info(f'{PREFIX}[STOP] Progress načten (settings): {progress:.1f}%')
    except Exception as e:
        log.warning(f'{PREFIX}[STOP] Chyba čtení progressu: {e}')

    global _last_known_progress, _progress_updated
    if not updated and _progress_updated:
        progress = _last_known_progress
        updated = True
        log.info(f'{PREFIX}[STOP] Progress převzat z in-memory: {progress:.1f}%')

    if not updated:
        try:
            p = xbmc.Player()
            if p.isPlaying():
                current = p.getTime()
                total = p.getTotalTime()
                if total > 0:
                    progress = (current / total) * 100.0
                    updated = True
                    log.info(f'{PREFIX}[STOP] Progress dopočítán z Playeru: {progress:.1f}%')
        except Exception as e:
            log.warning(f'{PREFIX}[STOP] Chyba fallbacku z Playeru: {e}')

    if not updated:
        log.warning(f'{PREFIX}[STOP] Progress nebyl aktualizován – STOP přeskočen.')
        _clean_settings()
        fresh_addon.setSetting('trakt_payload_locked', 'false') # Odblokovat
        return

    # --- Připrav payload pro lokální DB / Trakt ---
    
    # NOVÝ ZPŮSOB: item je Trakt payload, neparsujeme!
    item_for_local_db = item.copy()
    item_for_local_db['progress'] = progress

    # Sestavit Trakt payload (čištění lokálních klíčů, které Trakt nechce)
    trakt_payload = item.copy()
    for k in ('stream_url', 'name', 'tmdb_id', 'trakt_payload', 'media_type', 'watched_at'):
        trakt_payload.pop(k, None) 
    
    # Doplníme čistý trakt_payload do položky pro lokální DB
    item_for_local_db['trakt_payload'] = trakt_payload
    
    # Získání typu média (movie/episode)
    mt = (item_for_local_db.get('media_type') 
          or ('episode' if ('show' in item_for_local_db and 'episode' in item_for_local_db) else None)
          or ('movie' if 'movie' in item_for_local_db else 'movie'))

    # 🚨 ZMĚNA: ZAJIŠTĚNÍ TMDB ID pro lokální DB z payloadu
    tmdb_id_for_db = None
    if mt == 'movie':
        tmdb_id_for_db = trakt_payload.get('movie', {}).get('ids', {}).get('tmdb')
    elif mt == 'episode':
        # TMDb často nemá vlastní ID pro epizodu → fallback na ID seriálu (show)
        tmdb_id_for_db = (
            trakt_payload.get('episode', {}).get('ids', {}).get('tmdb')
            or trakt_payload.get('show', {}).get('ids', {}).get('tmdb')
        )
        
    if tmdb_id_for_db:
        # Použijeme string, protože DB to obvykle ukládá jako string
        item_for_local_db['tmdb_id'] = str(tmdb_id_for_db)
    else:
        # Fallback na '0' pro lokální DB, pokud ID nemáme (nemělo by se stát)
        item_for_local_db['tmdb_id'] = '0'
        log.error(f'{PREFIX}[STOP] CHYBA: TMDB ID pro lokální DB nenalezeno v payloadu. Ukládám s ID=0.')
    
    # 🚨 ZMĚNA: ODSTRANĚN CELÝ PŮVODNÍ BLOK ID EXTRACT!

    # ✅ EPIZODY: SeriesManager + Trakt STOP
    if mt == 'episode':
        try:
            # Nový payload nemá 'name', musíme použít show title
            series_title = item.get('show', {}).get('title') or 'Neznámý seriál'
            season = item.get('episode', {}).get('season')
            episode = item.get('episode', {}).get('number')
            # TMDB ID seriálu (show) – spolehlivější klíč pro dohledání v lokální DB než text názvu
            show_tmdb_id = item.get('show', {}).get('ids', {}).get('tmdb')
            
            if season and episode:
                from datetime import datetime as dt
                current_time_iso = dt.utcnow().strftime('%Y-%m-%dT%H:%M:%S')
                log.info(f"{PREFIX}[DEBUG] Připraven k předání Watched_At: {current_time_iso}")
                from resources.lib.series import series_manager
                import xbmcvfs # pro translatePath
                profile_path = xbmcvfs.translatePath(fresh_addon.getAddonInfo('profile'))
                sm = series_manager.SeriesManager(fresh_addon, profile_path)
                sm.mark_episode_progress(series_title, int(season), int(episode), progress, set_by='auto', watched_at=current_time_iso, tmdb_id=show_tmdb_id)
                log.info(f"{PREFIX}[STOP] Progress uložen do SeriesManager: {series_title} S{season}E{episode} -> {progress}%")

                # Nově přidáno: cílený sync, aby se ihned propsalo do SQLite cache pro UI
                try:
                    sync_name = sm._safe_filename(series_title, show_tmdb_id) if show_tmdb_id else series_title
                    sm.sync_series_with_sql(series_name=sync_name)
                    # ✅ FIX: Pokud jsme v seznamu "Další epizody", vyvoláme refresh, 
                    # aby se seznam hned aktualizoval (odstraní starou a přidá novou epizodu)
                    path = xbmc.getInfoLabel('Container.FolderPath')
                    if 'action=series_next_episodes' in path:
                        log.info(f"{PREFIX}[STOP] Vyvolávám refresh kontejneru pro Next Up (path: {path})")
                        xbmc.executebuiltin('Container.Refresh')
                except Exception as ex:
                    log.warning(f"{PREFIX}[STOP] Chyba pri cíleném sync_series_with_sql/refresh: {ex}")

                # Trakt STOP pro epizodu (pokud povoleno)
                if fresh_addon.getSettingBool('trakt_enabled'):
                    headers = get_trakt_headers(for_post=True)
                    if headers:
                        ep_payload = trakt_payload.copy() # Použijeme již očištěný
                        data = {"progress": min(progress, 99.9), **ep_payload}
                        resp = _trakt_scrobble_post('stop', data, headers)
                        if resp and resp.status_code in (201, 409):
                            log.info(f"{PREFIX}[STOP] Trakt STOP úspěšně odeslán pro epizodu.")
                            # Krátka notifikace při dokončení epizody
                            xbmcgui.Dialog().notification("Trakt.tv", f"Sledování dokončeno: {series_title}", xbmcgui.NOTIFICATION_INFO, 2000)
                        elif resp:
                            log.error(f"{PREFIX}[STOP] Trakt STOP chyba HTTP {resp.status_code}")
                    else:
                        _has_rt = bool(fresh_addon.getSetting('trakt_refresh_token') or '')
                        log.warning(
                            f"{PREFIX}[STOP] Trakt STOP (epizoda) přeskočen – prázdný access token; "
                            f"refresh_token={'ano' if _has_rt else 'ne'}. Ověřte přihlášení Trakt v Nastavení."
                        )
            else:
                log.warning(f"{PREFIX}[STOP] Chybí S/E pro epizodu – progress nezapsán.")
        except Exception as e:
            log.error(f"{PREFIX}[STOP] CHYBA zápisu progressu do SeriesManager: {e}")

    # FILMY: tři stavy podle skutečného progressu
    #   progress >= 90   → [dokončeno] progress=100, odešleme Trakt STOP (pokud zapnutý)
    #   5 < progress <90 → [rozkoukáno] uložíme reálné %, Trakt STOP NEposíláme
    #   progress <= 5    → [nový] progress=0, Trakt STOP NEposíláme
    # trakt_ok:  None = nevyhodnoceno, 1 = Trakt vrátil 201/409, 0 = neodesláno / chyba
    if mt == 'movie':
        from resources.lib.history import local_history
        from datetime import datetime

        # 1) rozhodnutí o kategorii
        if progress >= 90.0:
            state = 'done'
            final_progress = 100.0
        elif progress > 5.0:
            state = 'rozkoukano'
            final_progress = round(progress, 2)
        else:
            state = 'novy'
            final_progress = 0.0

        # 2) Trakt STOP jen pro dokončený film
        trakt_ok: Optional[int] = 0
        if state == 'done':
            try:
                if fresh_addon.getSettingBool('trakt_enabled'):
                    headers = get_trakt_headers(for_post=True)
                else:
                    headers = None

                movie_payload = trakt_payload.copy()  # {'movie': {...}}

                if headers:
                    data = {"progress": min(progress, 99.9), **movie_payload}
                    resp = _trakt_scrobble_post('stop', data, headers)
                    if resp and resp.status_code in (201, 409):
                        trakt_ok = 1
                        log.info(f"{PREFIX}[STOP] Trakt STOP úspěšně odeslán pro film.")
                        title = movie_payload.get("movie", {}).get("title") or "Film"
                        xbmcgui.Dialog().notification("Trakt.tv", f"Sledování dokončeno: {title}", xbmcgui.NOTIFICATION_INFO, 2000)
                    elif resp:
                        log.error(f"{PREFIX}[STOP] Trakt STOP chyba HTTP {resp.status_code}")
                else:
                    if not fresh_addon.getSettingBool('trakt_enabled'):
                        log.info(f"{PREFIX}[STOP] Trakt vypnut – STOP (film) přeskočen.")
                    else:
                        _has_rt_m = bool(fresh_addon.getSetting('trakt_refresh_token') or '')
                        log.warning(
                            f"{PREFIX}[STOP] Trakt STOP (film) přeskočen – prázdný access token; "
                            f"refresh_token={'ano' if _has_rt_m else 'ne'}. Ověřte přihlášení Trakt v Nastavení."
                        )
            except Exception as e:
                log.error(f"{PREFIX}[STOP] Chyba při přípravě Trakt STOP (film): {e}")

        # 3) zápis do lokální historie – jediná cesta pro všechny tři stavy
        try:
            original_payload = item
            clean_title = (
                original_payload.get('movie', {}).get('title')
                or original_payload.get('show', {}).get('title')
                or 'Neznámý film'
            )
            last_url = ""
            try:
                last_url = fresh_addon.getSetting('last_stream_url') or ""
            except Exception:
                pass

            local_db_item = {
                "name": clean_title,
                "title": clean_title,
                "media_type": "movie",
                "tmdb_id": str(tmdb_id_for_db or 0),
                "progress": final_progress,
                "watched_at": datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S'),
                "stream_url": last_url,
                "trakt_payload": trakt_payload,
                "movie": original_payload.get('movie'),
                "trakt_ok": trakt_ok,
            }

            local_history.store_item(local_db_item)
            log.info(
                f"{PREFIX}[STOP] Historie uložena: {clean_title} "
                f"[{state}] progress={final_progress:.1f}% raw={progress:.1f}% "
                f"trakt_ok={trakt_ok}"
            )
        except Exception as e:
            log.error(f'{PREFIX}[STOP] Chyba při ukládání historie filmu: {e}')

    # --- Vyčistit nastavení + odemknout (vše řeší _clean_settings) ---
    _clean_settings()
    log.info(f'{PREFIX}[STOP] Zámek a dočasná data vyčištěna.')
