from wcs.library2.compat import select_action as compat_select
import xbmc
import xbmcgui
import xbmcaddon
import xbmcplugin
import json
import threading
import requests
import os
import time
from datetime import datetime
from wcs.ai import AIHandler as ai_handler
from wcs.metadata.TMDbClient import get_tmdb_api_key
from wcs import utils
from wcs.ui import UI
from wcs.ui.dialogs.DialogWcsSearch import SearchDialog
from wcs import user_data
import urllib.parse
from wcs.webshare import WebshareClient as webshare_api
from wcs.ai.DialogAIDebugRequest import show_api_request_debug, show_api_response_debug


class AIRecommendationPromptBuilder:
    """
    Helper třída pro skládání system a user promptů pro AI chat doporučování.
    System prompt je session-stable (nastaví se na začátku a nemění se).
    User message se mění s každou inferencí.
    """
    
    def __init__(self, media_type="movie", context=None):
        self.addon = xbmcaddon.Addon()
        self.media_type = media_type
        self.context = context
        self._system_prompt = None  # Cache pro session-stable system prompt
        
    def _load_template(self, filename):
        """Načte šablonu promptu ze souboru."""
        try:
            addon_dir = self.addon.getAddonInfo('path')
            prompt_file = os.path.join(addon_dir, 'resources', 'prompts', 'ai_recommendations', filename)
            if os.path.exists(prompt_file):
                with open(prompt_file, 'r', encoding='utf-8') as f:
                    return f.read()
            xbmc.log(f"[plugin.video.milionar] Prompt file not found: {prompt_file}", level=xbmc.LOGWARNING)
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Error loading prompt file: {e}", level=xbmc.LOGERROR)
        return None
        
    def _get_media_label(self):
        """Vraci cesky label pro typ media."""
        if self.context:
            c_type = self.context.get('type')
            if c_type in ['my_mixed_collection', 'recently_played', 'favorite_actors', 'favorite_directors', 'favorite_writers', 'simple_mix']:
                return "filmu a serialu"
            elif c_type in ['my_collections', 'collection']:
                return "filmovych kolekci"
        return "filmu" if self.media_type == "movie" else "serialu"
    
    def _is_mixed_content(self):
        """Zjisti, jestli jde o mix filmu a serialu."""
        if self.context:
            c_type = self.context.get('type')
            return c_type in ['my_mixed_collection', 'recently_played', 'favorite_actors', 'favorite_directors', 'favorite_writers', 'simple_mix']
        return False
    
    def _get_media_type_instruction(self):
        """Vrací instrukce pro typ média."""
        if self.context and self.context.get('type') in ['my_collections', 'collection', 'simple_collections']:
            return (
                "REŽIM KOLEKCE:\n"
                "Doporučuješ FILMOVÉ KOLEKCE a SÁGY (trilogie, franšízy), ne jednotlivé filmy.\n"
                "Zaměř se na ucelené série jako 'Pán prstenů kolekce', 'Marvel kolekce', atd."
            )
        elif self._is_mixed_content():
            return (
                "REŽIM MIX:\n"
                "Doporučuješ FILMY i SERIÁLY současně. Nabízej rozmanitý mix podle vkusu uživatele."
            )
        elif self.media_type == "series":
            return (
                "REŽIM SERIÁLY:\n"
                "Doporučuješ výhradně SERIÁLY."
            )
        else:
            return (
                "REŽIM FILMY:\n"
                "Doporučuješ výhradně FILMY."
            )
    
    def _get_json_format(self):
        """Vraci JSON format podle typu media."""
        # VZDY vyzadovat pole 'type' pro spravne vyhledavani na TMDb
        if self._is_mixed_content():
            return '''{
  "message": "Tva otazka nebo komentar (max 2-3 vety)",
  "buttons": [
    {"label": "Text tlacitka", "value": "Odpoved ktera se odesle"}
  ],
  "recommendations": [
    {"title": "Cesky nazev", "year": "YYYY", "type": "movie nebo tv", "original_title": "Originalni nazev"}
  ]
}'''
        elif self.media_type == "series":
            return '''{
  "message": "Tva otazka nebo komentar (max 2-3 vety)",
  "buttons": [
    {"label": "Text tlacitka", "value": "Odpoved ktera se odesle"}
  ],
  "recommendations": [
    {"title": "Cesky nazev", "year": "YYYY", "type": "tv", "original_title": "Originalni nazev"}
  ]
}'''
        elif self.media_type == "collection":
            return '''{
  "message": "Tva otazka nebo komentar (max 2-3 vety)",
  "buttons": [
    {"label": "Text tlacitka", "value": "Odpoved ktera se odesle"}
  ],
  "recommendations": [
    {"title": "Nazev kolekce", "type": "collection", "original_title": "Originalni nazev kolekce"}
  ]
}'''
        else:  # movie
            return '''{
  "message": "Tva otazka nebo komentar (max 2-3 vety)",
  "buttons": [
    {"label": "Text tlacitka", "value": "Odpoved ktera se odesle"}
  ],
  "recommendations": [
    {"title": "Cesky nazev", "year": "YYYY", "type": "movie", "original_title": "Originalni nazev"}
  ]
}'''
    
    def _get_json_type_rule(self):
        """Vraci pravidlo pro type pole - VZDY, pro vsechny varianty."""
        if self._is_mixed_content():
            return "- Pole 'type' MUSI byt bud 'movie' (pro filmy) nebo 'tv' (pro serialy). SPRAVNE URCI TYP.\n"
        elif self.media_type == "series":
            return "- Pole 'type' MUSI byt vzdy 'tv' (pro serialy).\n"
        elif self.media_type == "collection":
            return "- Pole 'type' MUSI byt vzdy 'collection' (pro filmove kolekce a sagy).\n"
        else:  # movie
            return "- Pole 'type' MUSI byt vzdy 'movie' (pro filmy).\n"
    
    def build_system_prompt(self, force_rebuild=False):
        """
        Sestaví session-stable system prompt.
        Obsahuje: role definition, časový kontext, temporální inteligenci, pravidla dialogu.
        """
        if self._system_prompt and not force_rebuild:
            return self._system_prompt
            
        template = self._load_template('system_prompt.txt')
        if not template:
            # Fallback
            self._system_prompt = f"Jsi asistent pro {self._get_media_label()}. Odpověz v JSON formátu."
            return self._system_prompt
        
        recomm_count = int(self.addon.getSetting('ai_recommendation_count') or 11)
        media_label = self._get_media_label()
        media_type_instruction = self._get_media_type_instruction()
        json_format = self._get_json_format()
        json_type_rule = self._get_json_type_rule()
        
        # Časový kontext pro temporální inteligenci
        now = datetime.now()
        current_time_str = now.strftime("%d.%m.%Y %H:%M (%A)")
        
        # Region a jazyk
        try:
            user_lang = xbmc.getLanguage(xbmc.ISO_639_1)
        except:
            user_lang = 'cs'
        try:
            if hasattr(xbmc, 'ISO_3166_1'):
                user_region = xbmc.getRegion(xbmc.ISO_3166_1)
            else:
                user_region = user_lang.upper()
        except:
            user_region = 'CZ'
        
        # Nahradit {json_format} markerem (obsahuje {})
        prompt = template.replace('{json_format}', '<<<JSON_FORMAT>>>')
        prompt = prompt.format(
            current_time_str=current_time_str,
            user_region=user_region,
            user_lang=user_lang,
            media_label=media_label,
            media_type_instruction=media_type_instruction,
            recomm_count=recomm_count,
            json_type_rule=json_type_rule
        )
        prompt = prompt.replace('<<<JSON_FORMAT>>>', json_format)
        
        self._system_prompt = prompt
        return self._system_prompt
    
    def build_messages(self, chat_history, context_instruction=""):
        """
        Sestaví pole messages pro multi-turn API.
        Vrací nativní formát zpráv pro LLM API.
        
        Args:
            chat_history: Historie konverzace [{"role": "user/assistant", "content": "..."}]
            context_instruction: Volitelná kontextová instrukce
            
        Returns:
            list: Pole messages [{"role": "user/assistant", "content": "..."}]
        """
        messages = []
        
        # Error handling pro prázdnou historii
        if not chat_history:
            return messages
        
        # Přidat celou historii konverzace
        for i, msg in enumerate(chat_history):
            # Normalizovat role name pro API (assistant místo AI)
            role = "assistant" if msg['role'] == "assistant" else "user"
            content = msg['content']
            
            # Pokud je context instruction a toto je PRVNÍ user zpráva,
            # NAHRADÍME obsah context instruction (ne přidáváme k němu)
            if i == 0 and role == "user" and context_instruction:
                content = context_instruction
            
            messages.append({
                "role": role,
                "content": content
            })
        
        return messages

class AIChatRecommendationDialog(xbmcgui.WindowXMLDialog):
    # Navigační položky pro sidebar (sdílené mezi všemi dialogy)
    NAV_ITEMS = [
        {'id': 'continue_watching', 'label': 'Pokracovat ve sledovani', 'icon': 'DefaultVideoPlaylists.png'},
        {'id': 'my_tv', 'label': 'MyTV - televize budoucnosti', 'icon': 'DefaultAddonPVRClient.png'},
        {'id': 'my_cinema', 'label': 'MyCinema - kino budoucnosti', 'icon': 'DefaultMovies.png'},
        {'id': 'my_movies', 'label': 'Moje filmy', 'icon': 'DefaultMovies.png'},
        {'id': 'my_series', 'label': 'Moje serialy', 'icon': 'DefaultTVShows.png'},
        {'id': 'ai_recommend', 'label': 'AI Doporuceni', 'icon': 'DefaultMusicSearch.png'},
        {'id': 'settings', 'label': 'Nastaveni', 'icon': 'DefaultAddonProgram.png'},
        {'id': 'close', 'label': 'Zavrit', 'icon': 'DefaultFolderBack.png'},
    ]
    
    # Mapování nav_id -> router action (pro _switch_to_dialog)
    NAV_ACTIONS = {
        'continue_watching': 'continue_watching_dialog',
        'my_movies': 'my_movies_dialog',
        'my_series': 'my_series_dialog',
        'my_tv': 'my_tv_dialog',
        'my_cinema': 'my_cinema_dialog',
        'ai_recommend': 'ai_chat_recommendation',
    }

    
    def __init__(self, xml_filename, addon_path, *args, **kwargs):
        # Vyčistit parametry pro nav sidebar (používají child dialogy)
        self._show_nav_on_init = kwargs.pop('show_nav_sidebar', False)
        self._nav_position = kwargs.pop('nav_position', 0)
        kwargs.pop('context', None)  # Vyčistit context aby se nepředával dál
        
        super(AIChatRecommendationDialog, self).__init__(xml_filename, addon_path)
        self.addon = xbmcaddon.Addon()
        self.history = [] # List of {"role": "...", "content": "..."}
        self.media_type = None # "movie" or "series"
        self.tmdb_results = []
        self._thinking_stop = threading.Event()
        self._thinking_thread = None
        self._dot_count = 0
        self.context = kwargs.get('context', None)
        # Cache pro preview detaily (klíč = tmdb_id)
        self._preview_cache = {}
        self._last_preview_id = None
        # Globální token pro invalidaci timerů - inkrementuje se při jakékoliv akci
        self._action_token = 0
        # Prompt builder pro sestavení system a user promptů
        self._prompt_builder = None  # Inicializuje se v onInit po nastavení media_type
        # Cache pro poslední odpověď AI (pro debug)
        self._last_response_data = None
        self._last_response_usage = {}
        # Identifikátor aktivní sekce pro navigační sidebar (přetíženo v child třídách)
        self._nav_section_id = 'ai_recommend'
        # Sledování předchozí pozice v seznamu (pro správné otevírání nav sidebaru)
        self._last_list_position = 0
        # Flag: právě jsme se vrátili z chatu -> NEotevírat nav sidebar
        self._returning_from_chat = False
        # Sledování předchozího focus ID
        self._last_focus_id = None

        self._last_raw_response = None
        self._last_response_metadata = {}
    
    # ==================================================================================
    # UTILITY METHODS (pro child třídy)
    # ==================================================================================
    
    def _reset_state(self):
        """Resetuje proměnné na výchozí stav. Volat na začátku onInit() v child třídách."""
        self._last_preview_id = None
        self._preview_cache = {}
        self._action_token = 0
        self.history = []
        self._prompt_builder = None
        self._returning_from_chat = False
        self._last_focus_id = None
        self._last_list_position = 0
    
    def _delayed_focus(self, control_id, delay=0.5):
        """Nastaví focus na control s krátkým zpožděním (thread-safe).
        
        Args:
            control_id: ID controlu na který se má nastavit focus
            delay: Zpoždění v sekundách (default 0.5s)
        """
        def set_focus():
            time.sleep(delay)
            try:
                self.setFocusId(control_id)
            except:
                pass
        threading.Thread(target=set_focus, daemon=True).start()
        
    def onInit(self):
        from wcs.skin.context import capture_dialog
        capture_dialog(self)
        # Pokud máme kontext, přeskočíme úvodní výběr
        if self.context:
            # Normalizace media_type: 'tv' -> 'series' (pro konzistenci s prompt builderem)
            raw_media_type = self.context.get('media_type', 'movie')
            self.media_type = 'series' if raw_media_type == 'tv' else raw_media_type
            
            # Zobrazit uživatelskou "tichou" zprávu nebo uvítání
            welcome_msg = "Analyzuji váš výběr..."
            self.append_chat_message("AI", welcome_msg)
            
            # Context instruction (z _get_context_instruction) nahradí tuto zprávu v build_messages()
            # Toto je jen placeholder pro historii - skutečný obsah dodá context instruction
            c_type = self.context.get('type')
            
            # Speciální případ: section_root = start simple conversation
            if c_type == 'section_root':
                self._start_simple_media_conversation()
                return
            
            # Inicializovat prompt builder pro tento kontext NEJDŘÍV (potřebujeme context_instruction)
            self._prompt_builder = AIRecommendationPromptBuilder(self.media_type, self.context)
            
            # Získat skutečný dotaz z context instruction
            context_instruction = self._get_context_instruction()
            initial_user_msg = context_instruction or self.context.get('initial_message') or "Potřebuji doporučení."
            self.history.append({"role": "user", "content": initial_user_msg})
            
            # Zobrazit sidebar overlay NEJDŘÍV
            self.setProperty('WCS.AIChat.Visible', 'true')
            
            # Zobrazit uživatelovu zprávu v chatu
            self.append_chat_message("Ty", initial_user_msg)
            
            # Fokus na sidebar (tlačítka/animace)
            self.setFocusId(4000)
            
            # Spustit načítací animaci (Matrix efekt) - stejně jako při kliknutí na tlačítko
            self._start_button_loading("Analyzuji...")
            
            # Start AI conversation
            self._start_ai_conversation(is_initial_context=True)
            return

        # Standardní flow bez kontextu
        # Initial Welcome Message
        welcome_msg = "Co chcete doporučit?"
        self.append_chat_message("AI", welcome_msg)
        
        # Initial Buttons - CHOOSE TYPE FIRST
        self.update_buttons([
            {"label": "Filmy", "value": "set_mode:movie"},
            {"label": "Seriály", "value": "set_mode:series"}
        ])
        
        # Clear recommendations
        self.getControl(2000).reset()
        
        # Ensure Input is hidden initially
        self.clearProperty('WCS.AIChat.ShowInput')
        
        # [NEW] Set Chat Overlay Visible initially (we need to choose mode)
        self.setProperty('WCS.AIChat.Visible', 'true')
        
        # Set focus on buttons list in the overlay
        self.setFocusId(4000)

    def append_chat_message(self, sender, text):
        """Append message to chat history textbox."""
        history_control = self.getControl(1000)
        current_text = history_control.getText()
        
        # Format: Sender: Message
        # Using colors
        if sender == "AI":
            line = f"[COLOR blue]AI:[/COLOR] {text}\n"
        else:
            line = f"[COLOR orange]Ty:[/COLOR] {text}\n"
            
        new_text = current_text + line
        history_control.setText(new_text)
        
        # Force scroll to bottom immediately after delay
        threading.Thread(target=self._auto_scroll_text_box).start()
    
    def append_system_message(self, text):
        """Append system message to chat history (grey, italic)."""
        history_control = self.getControl(1000)
        current_text = history_control.getText()
        
        # System message: red prefix, grey italic text
        line = f"[COLOR red]System:[/COLOR] [COLOR grey][I]{text}[/I][/COLOR]\n"
            
        new_text = current_text + line
        history_control.setText(new_text)
        
        # Force scroll to bottom immediately after delay
        threading.Thread(target=self._auto_scroll_text_box).start()

    def _auto_scroll_text_box(self):
        """Helper to scroll text box to bottom with delay."""
        import time
        time.sleep(0.2) # Wait for text to render
        try:
            history_control = self.getControl(1000)
            # Scroll down sufficiently to ensure bottom
            history_control.scroll(20000)
        except:
            pass

    def update_buttons(self, buttons):
        """Update dynamic buttons list."""
        # buttons: list of dicts {"label": str, "value": str}
        list_control = self.getControl(4000)
        list_control.reset()
        
        for btn in buttons:
            item = xbmcgui.ListItem(label=btn['label'])
            item.setProperty('value', btn['value'])
            list_control.addItem(item)
            
    def _start_button_loading(self, label):
        """Starts the Matrix Deconstruction animation."""
        import random
        
        self._buttons_ready = False # Reset flag
        self.setProperty('WCS.AIChat.IsLoadingButtons', 'true')
        self.setProperty('WCS.AIChat.GlitchText', label)
        
        # Randomize HUD positions (Slots A, B, C)
        slots = ['A', 'B', 'C']
        self.setProperty('WCS.AIChat.Slot.Provider', random.choice(slots))
        self.setProperty('WCS.AIChat.Slot.Model', random.choice(slots))
        self.setProperty('WCS.AIChat.Slot.Load', random.choice(slots))
        self.setProperty('WCS.AIChat.Slot.Params', random.choice(slots))
        self.setProperty('WCS.AIChat.Slot.Time', random.choice(slots))
        
        # Populate HUD Params (Initial Values)
        params = self._get_ai_display_values()
        self.setProperty('WCS.AIChat.Provider', params['provider'])
        self.setProperty('WCS.AIChat.Model', params['model'])
        self.setProperty('WCS.AIChat.Temp', f"{params['temp']}")
        self.setProperty('WCS.AIChat.TopP', f"{params['top_p']}")
        self.setProperty('WCS.AIChat.NetLoad', "0%")
        
        # Focus trap (Dummy Button) to prevent sidebar close/focus loss
        # Must be called AFTER setting property that makes Group 5500 visible
        self.setFocusId(5501)
        
        threading.Thread(target=self._matrix_animation_worker, args=(label, params)).start()
        
    def _get_ai_display_values(self):
        """Fetches all AI settings for HUD display."""
        provider = self.addon.getSetting('ai_provider')
        p_key = provider # prefix
        
        # Default values
        temp = "0.7"
        top_p = "0.9"
        tokens = "2048"
        model = "Unknown"
        
        try:
            if provider == 'openai':
                model = self.addon.getSetting('openai_model')
                temp = self.addon.getSetting('openai_temperature')
                top_p = self.addon.getSetting('openai_top_p')
                tokens = self.addon.getSetting('openai_max_output_tokens')
            elif provider == 'anthropic':
                model = self.addon.getSetting('anthropic_model')
                temp = self.addon.getSetting('anthropic_temperature')
                top_p = self.addon.getSetting('anthropic_top_p')
                tokens = self.addon.getSetting('anthropic_max_tokens')
            elif provider == 'google':
                model = self.addon.getSetting('google_model')
                temp = self.addon.getSetting('google_temperature')
                top_p = self.addon.getSetting('google_top_p')
                tokens = self.addon.getSetting('google_max_tokens')
            elif provider == 'mistral':
                model = self.addon.getSetting('mistral_model')
                temp = self.addon.getSetting('mistral_temperature')
                top_p = self.addon.getSetting('mistral_top_p')
                tokens = self.addon.getSetting('mistral_max_tokens')
            elif provider == 'groq':
                model = self.addon.getSetting('groq_model')
                temp = self.addon.getSetting('groq_temperature')
                top_p = self.addon.getSetting('groq_top_p')
                tokens = self.addon.getSetting('groq_max_tokens')
        except:
            pass
            
        return {
            "provider": provider.upper(),
            "model": model, 
            "temp": temp,
            "top_p": top_p,
            "tokens": tokens
        }

    def _matrix_animation_worker(self, original_text, params):
        """Animating text dissolving into matrix code."""
        import time
        import random
        
        chars = "010101010100100101#@?&%<>/[]"
        length = len(original_text)
        
        # Helper function for matrix scramble
        def scramble(text, ratio=0.5):
            result = list(text)
            for _ in range(max(1, int(len(text) * ratio))):
                idx = random.randint(0, len(text) - 1)
                result[idx] = random.choice(chars)
            return "".join(result)
        
        # Phase 1: Deconstruction (Provider + Model scramble) - 1.5s
        for i in range(15):
            # 1. Text Glitch (Button text)
            glitch_list = list(original_text)
            for _ in range(max(1, length // 3)):
                idx = random.randint(0, length - 1)
                glitch_list[idx] = random.choice(chars)
            self.setProperty('WCS.AIChat.GlitchText', "".join(glitch_list))
            
            # 2. Provider + Model Scramble (Matrix Decryption!)
            self.setProperty('WCS.AIChat.Provider', scramble(params['provider']))
            self.setProperty('WCS.AIChat.Model', scramble(params['model'], 0.3))
            
            # Fun status updates
            if i == 0: self.setProperty('WCS.AIChat.FunStatus', "Deconstructing...")
            elif i == 5: self.setProperty('WCS.AIChat.FunStatus', "Analyzing Neural Paths...")
            elif i == 10: self.setProperty('WCS.AIChat.FunStatus', "Parsing Context...")
            
            time.sleep(0.1)
        
        # Phase 2: Wait for AI
        start_wait = time.time()
        
        # Set REAL values (Provider, Model settle)
        self.setProperty('WCS.AIChat.Provider', params['provider'])
        self.setProperty('WCS.AIChat.Model', params['model'])
        
        while not getattr(self, '_buttons_ready', False):
            # Dynamic loading dots
            dots = "." * (int(time.time() * 2) % 4)
            self.setProperty('WCS.AIChat.GlitchText', f"GENERATING{dots}")
            self.setProperty('WCS.AIChat.FunStatus', "Constructing Response...")
            
            # Occasional glitch on Provider/Model (10% / 5% chance)
            if random.random() < 0.1:
                self.setProperty('WCS.AIChat.Provider', scramble(params['provider'], 0.3))
            else:
                self.setProperty('WCS.AIChat.Provider', params['provider'])
            
            if random.random() < 0.05:
                self.setProperty('WCS.AIChat.Model', scramble(params['model'], 0.2))
            else:
                self.setProperty('WCS.AIChat.Model', params['model'])
                
            time.sleep(0.1)
            
            # Safety timeout (10s)
            if time.time() - start_wait > 10:
                break
                
        # Reveal
        self.setProperty('WCS.AIChat.IsLoadingButtons', '')
        self.setProperty('WCS.AIChat.GlitchText', '')
        self.setProperty('WCS.AIChat.FunStatus', '')
        
        # Restore focus to buttons list
        self.setFocusId(4000)

    def onClick(self, controlId):
        # Action Buttons (Dynamic)
        if controlId == 4000:
            list_control = self.getControl(4000)
            item = list_control.getSelectedItem()
            if not item: return
            
            val = item.getProperty('value')
            label = item.getLabel()
            
            # Handle Local Mode Switching
            if val.startswith("set_mode:"):
                self.media_type = val.split(":")[1]
                # RESET stavu pri prepnuti rezimu - dulezite pro spravne slozeni promptu!
                # Vymazat stary kontext a historii, aby se chovalo jako cista konverzace
                self.context = None
                self.history = []
                self._prompt_builder = None
                # Vymazat chat UI
                try:
                    history_control = self.getControl(1000)
                    history_control.setText("")
                except:
                    pass
                # Trigger Matrix Animation for intro too
                self._start_button_loading(label)
                self._start_simple_media_conversation()
            else:
                # Trigger Matrix Animation
                self._start_button_loading(label)
                
                # Send button value (if exists) or label as user text
                # Value obsahuje specifickou instrukci pro LLM, label je jen pro uživatele
                text_for_llm = val if val else label
                # V UI zobrazit label, do LLM poslat value
                # V UI zobrazit label, do LLM poslat value
                self.handle_user_input(text_for_llm, display_text=label)

        # Send Text Button
        elif controlId == 3001:
            try:
                input_control = self.getControl(3000)
                if isinstance(input_control, xbmcgui.ControlEdit):
                    text = input_control.getText()
                    if text:
                        input_control.setText("") # Clear input
                        self.handle_user_input(text)
                else:
                    # Fallback for when getControl returns a generic Control but it is actually an edit
                    # This happens sometimes in Kodi Python API wrapper
                    # But if it fails, we catch exception
                    text = input_control.getText()
                    if text:
                        input_control.setText("")
                        self.handle_user_input(text)
            except AttributeError:
                xbmc.log("[plugin.video.milionar] Error: Control 3000 does not support getText() or is not an expected type.", xbmc.LOGWARNING)
            except Exception as e:
                xbmc.log(f"[plugin.video.milionar] Error handling Send button click: {e}", xbmc.LOGERROR)
                
        # Recommendations Panel
        elif controlId == 2000:
            self.handle_recommendation_click()
            
        # Preview Action Buttons
        elif controlId == 6100:
            self._handle_preview_action_click()
        
        # Navigation Sidebar
        elif controlId == 7100:
            self._handle_nav_click()
            
        # Icon Bar (Shortcut buttons) - same logic as vertical menu
        elif controlId == 7200:
            self._handle_icon_bar_click()
    
    def onAction(self, action):
        """Handle navigation actions for preview updates."""
        action_id = action.getId()
        if action_id == xbmcgui.ACTION_CONTEXT_MENU and self.getFocusId() == 2000:
            from wcs.skin.context import dialog_context
            if dialog_context(self):
                return
        
        # INVALIDACE TIMERU: Jakákoliv akce resetuje timer pro automatické zobrazení tlačítek
        self._action_token += 1
        self.clearProperty('WCS.AIPreview.ShowActions')
        
        # Navigace v seznamu doporučení - aktualizovat preview
        if action_id in (xbmcgui.ACTION_MOVE_LEFT, xbmcgui.ACTION_MOVE_RIGHT):
            focused_id = self.getFocusId()
            if focused_id == 2000:
                # Zjistit pozici v listu
                list_control = self.getControl(2000)
                current_pos = list_control.getSelectedPosition()
                
                # LEFT na první položce -> otevřít navigační sidebar
                # ALE POUZE pokud jsme NEPŘIŠLI z chatu!
                if action_id == xbmcgui.ACTION_MOVE_LEFT and self._last_list_position == 0:
                    if not self._returning_from_chat:
                        self._show_nav_sidebar()
                        return
                    else:
                        # Resetovat flag - příště už můžeme otevřít
                        self._returning_from_chat = False
                
                # Uložit aktuální pozici pro příští akci
                self._last_list_position = current_pos
                
                # Po malém zpoždění aktualizovat preview (dát čas na změnu výběru)
                time.sleep(0.05)
                self._update_preview_for_current_item()
            
            # RIGHT z navigačního sidebaru -> zpět na seznam
            elif focused_id == 7100 and action_id == xbmcgui.ACTION_MOVE_RIGHT:
                self._hide_nav_sidebar()
                self.setFocusId(2000)
                return
        
        # Navigation Up/Down from Recommendations (Custom Handling for Hidden Overlays)
        elif action_id in (xbmcgui.ACTION_MOVE_UP, xbmcgui.ACTION_MOVE_DOWN):
            if self.getFocusId() == 2000:
                if action_id == xbmcgui.ACTION_MOVE_DOWN:
                     # Down -> Show Chat Overlay & Focus It
                     self.setProperty('WCS.AIChat.Visible', 'true')
                     self.setFocusId(4000)
                elif action_id == xbmcgui.ACTION_MOVE_UP:
                     # Up -> Show Action Overlay & Focus It
                     # ALE: Pokud se vracíme z chatu, jen zůstat na seznamu
                     if self._returning_from_chat:
                         self._returning_from_chat = False
                         return
                     self.setProperty('WCS.AIPreview.ShowActions', 'true')
                     self.setFocusId(6100)
            
            # [NEW] Down from Action Buttons (6100) -> Back to List (2000)
            # Explicitly handled to prevent fallback to 2000 logic that might open Chat
            elif self.getFocusId() == 6100:
                if action_id == xbmcgui.ACTION_MOVE_DOWN:
                    self.setFocusId(2000)
                    return # Consume event
            
            # [NEW] Down from textbox (3000) -> Open debug dialog
            # ALE: Jen pokud uz jsme byli na textboxu (ne pri presunu NA textbox)
            elif self.getFocusId() == 3000:
                if action_id == xbmcgui.ACTION_MOVE_DOWN:
                    # Pokud jsme prave prisli na textbox, neotvirat debug
                    if getattr(self, '_just_focused_textbox', False):
                        self._just_focused_textbox = False
                        return
                    self._handle_debug_trigger()
                    return




        
        # Zavření dialogu nebo skrytí chatu
        elif action_id in (xbmcgui.ACTION_PREVIOUS_MENU, xbmcgui.ACTION_NAV_BACK, xbmcgui.ACTION_PARENT_DIR):
            # 1. Pokud je focus na preview actions (6100), zpět na seznam (2000)
            if self.getFocusId() == 6100:
                self.setFocusId(2000)
                return
            
            # 2. Pokud je focus v chatu (3000, 3001, 4000) NEBO je chat viditelný...
            # A zároveň máme nějaká doporučení (list 2000 není prázdný) -> Skrýt chat, focus na seznam
            list_control = self.getControl(2000)
            if (self.getProperty('WCS.AIChat.Visible') == 'true' or self.getFocusId() in [3000, 3001, 4000]) and list_control.size() > 0:
                self.clearProperty('WCS.AIChat.Visible')
                self.setFocusId(2000)
                return

            # Jinak zavřít dialog
            self._clear_preview_properties()
            self.close()
    
    def onFocus(self, controlId):
        """Handle focus changes - populate preview on recommendation hover."""
        if controlId == 2000:
            # Focus na panel doporučení
            # Pokud přicházíme z chatu, nastavit flag (zabrání otevření nav sidebar)
            if self._last_focus_id in [4000, 4001, 3000, 3001, 1001, 1002]: # [UPDATED] Added 4001
                self._returning_from_chat = True
            
            self.clearProperty('WCS.AIPreview.ShowActions')
            self.clearProperty('WCS.AIChat.Visible')
            self.clearProperty('WCS.NavSidebar.Visible')
            self._update_preview_for_current_item()
            
        elif controlId == 6100:
            # Uživatelské akce (Play, Info...) - nemazat nic
            pass
        
        elif controlId in [7100, 7200]:
            # Navigační sidebar (Menu nebo IconBar) - skrýt ostatní overlays
            self.clearProperty('WCS.AIPreview.ShowActions')
            self.clearProperty('WCS.AIChat.Visible')
            
        elif controlId in [4000, 3001, 1001, 1002]:
            # Chat overlay - skrýt akční tlačítka, ALE nechat preview (pozadí)
            self.clearProperty('WCS.AIPreview.ShowActions')
        
        elif controlId == 3000:
            # Textbox - nastavit flag ze jsme prave prisli (pro debug dialog)
            self._just_focused_textbox = True
            self.clearProperty('WCS.AIPreview.ShowActions')
        
        # [NEW] Hidden Button Focus - Safe Close
        elif controlId == 4001:
             # 1. Tagle flag ze se vracime (pro jistotu)
             self._returning_from_chat = True
             # 2. Skryt sidebar
             self.clearProperty('WCS.AIChat.Visible')
             # 3. Focus na main list (a tim se to cele uzavre/prekresli)
             self.setFocusId(2000)
             self.clearProperty('WCS.AIPreview.ShowActions')
        
        else:
            # Cokoliv jiného - vyčistit vše
            self.clearProperty('WCS.AIPreview.ShowActions')
            self._clear_preview_properties()
        
        # Uložit aktuální focus ID pro příští kontrolu
        self._last_focus_id = controlId

    
    def _update_preview_for_current_item(self):
        """Načte a zobrazí preview pro aktuálně focusovaný titul."""
        try:
            list_control = self.getControl(2000)
            item = list_control.getSelectedItem()
            if not item:
                self._clear_preview_properties()
                return
            
            tmdb_id = item.getProperty('tmdb_id')
            if not tmdb_id:
                self._clear_preview_properties()
                return
                
            # Pokud je to stejný titul, nic nedělej
            if tmdb_id == self._last_preview_id:
                return
            
            # Při změně titulu: nejdřív skrýt overlay (slide dolů), pak zobrazit nová data (slide nahoru)
            is_title_change = self._last_preview_id is not None
            
            # Uložit data pro použití po animaci
            media_type = item.getProperty('media_type') or self.media_type or 'movie'
            title = item.getProperty('title') or item.getLabel()
            year = item.getProperty('year') or ''
            poster = item.getArt('poster') or ''
            
            if is_title_change:
                # Skrýt overlay (animace dolů)
                self.clearProperty('WCS.AIPreview.State')
                
                def delayed_update():
                    import time
                    time.sleep(0.25)
                    self._clear_preview_properties()
                    self._apply_new_preview(tmdb_id, media_type, title, year, poster)
                
                threading.Thread(target=delayed_update, daemon=True).start()
                
            else:
                # První zobrazení - rovnou nastavit
                self._apply_new_preview(tmdb_id, media_type, title, year, poster)
                
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Preview update error: {e}", xbmc.LOGERROR)
    
    def _apply_new_preview(self, tmdb_id, media_type, title, year, poster):
        """Nastaví nová preview data a spustí načtení z TMDb."""
        self._last_preview_id = tmdb_id
        
        # Nastavit základní properties (spustí Visible slide animaci)
        self.setProperty('WCS.AIPreview.Title', title)
        self.setProperty('WCS.AIPreview.Year', year)
        self.setProperty('WCS.AIPreview.Poster', poster)
        self.setProperty('WCS.AIPreview.MediaTypeLabel', 'Film' if media_type == 'movie' else 'Seriál')
        
        # Reset Actions visibility & Start Timer
        self.clearProperty('WCS.AIPreview.ShowActions')
        
        # Inkrementovat action token (zruší všechny běžící timery)
        self._action_token += 1
        current_token = self._action_token
        
        def show_buttons_later(token):
            import time
            time.sleep(5.0)
            
            # Check 1: Token se nezměnil? (= žádná akce za 5 sekund)
            if self._action_token != token:
                return
            
            # Check 2: Stále na stejné položce?
            if self._last_preview_id != tmdb_id:
                return

            # Check 3: Je Chat viditelný nebo fokusovaný? (Zrušit pokud ano)
            try:
                # Pokud fokus NENÍ na hlavním seznamu (2000), uživatel se pohnul jinam
                if self.getFocusId() != 2000:
                    return
                # Kontrola property
                if self.getProperty('WCS.AIChat.Visible'):
                    return
            except:
                pass

            # Bezpečné - zobrazit a fokusovat akce
            self.setProperty('WCS.AIPreview.ShowActions', 'true')
            self.setFocusId(6100)
        
        threading.Thread(target=show_buttons_later, args=(current_token,), daemon=True).start()
        
        # Naplnit akční tlačítka
        self._populate_preview_actions(media_type, tmdb_id, title, year)
        
        # Načíst rozšířená data z cache nebo API
        if tmdb_id in self._preview_cache:
            self._apply_cached_preview_data(self._preview_cache[tmdb_id])
        else:
            # Vymazat staré rozšířené properties
            self.clearProperty('WCS.AIPreview.State') # Skrýt overlay
            self.clearProperty('WCS.AIPreview.Fanart')
            self.clearProperty('WCS.AIPreview.InfoBlock')
            self.clearProperty('WCS.AIPreview.IsCompact')
            self.clearProperty('WCS.AIPreview.Plot')
            # Načíst data v pozadí
            threading.Thread(target=self._fetch_preview_details, args=(tmdb_id, media_type)).start()
    
    def _clear_preview_properties(self):
        """Vymaže všechny preview properties."""
        props = ['Title', 'Year', 'Poster', 'Fanart', 'InfoBlock', 'Plot', 'IsCompact', 'State']
        for prop in props:
            self.clearProperty(f'WCS.AIPreview.{prop}')
        self._last_preview_id = None
    
    # ==================================================================================
    # NAVIGATION SIDEBAR METHODS
    # ==================================================================================
    
    def _show_nav_sidebar(self, animate=True):
        """Zobrazí navigační sidebar a naplní položky.
        
        Args:
            animate: Pokud False, potlačí slide animaci (pro plynulé přepínání dialogů)
        """
        # Potlačit animaci pokud je to přepnutí z jiného dialogu
        if not animate:
            self.setProperty('WCS.NavSidebar.NoAnimation', 'true')
        else:
            self.clearProperty('WCS.NavSidebar.NoAnimation')
        
        self._populate_nav_sidebar()
        self._populate_icon_bar()
        self.setProperty('WCS.NavSidebar.Visible', 'true')
        # Nastavit label aktivní sekce
        for item in self.NAV_ITEMS:
            if item['id'] == self._nav_section_id:
                self.setProperty('WCS.NavSidebar.ActiveLabel', item['label'])
                break
        self.setFocusId(7100)
        
        # Fokusovat na správnou pozici v navigačním menu (pro plynulejší přechod)
        nav_pos = getattr(self, '_nav_position', 0)
        if nav_pos > 0:
            try:
                list_control = self.getControl(7100)
                list_control.selectItem(nav_pos)
            except:
                pass
        
        # Pokud byla animace potlačena, povolit ji zpět po otevření
        # (aby zavírací animace fungovala)
        if not animate:
            def enable_animation_later():
                time.sleep(0.5)  # Počkat až se sidebar zobrazí
                try:
                    self.clearProperty('WCS.NavSidebar.NoAnimation')
                except:
                    pass
            threading.Thread(target=enable_animation_later, daemon=True).start()


    
    def _hide_nav_sidebar(self):
        """Skryje navigační sidebar."""
        self.clearProperty('WCS.NavSidebar.Visible')
    
    def _populate_nav_sidebar(self):
        """Naplní navigační sidebar položkami."""
        try:
            list_control = self.getControl(7100)
            list_control.reset()
            
            for nav_item in self.NAV_ITEMS:
                item = xbmcgui.ListItem(label=nav_item['label'])
                item.setArt({'icon': nav_item['icon']})
                item.setProperty('nav_id', nav_item['id'])
                
                # Označit aktivní položku
                if nav_item['id'] == self._nav_section_id:
                    item.setProperty('IsActive', 'true')
                
                list_control.addItem(item)
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Nav sidebar populate error: {e}", xbmc.LOGERROR)
    
    def _populate_icon_bar(self):
        """Naplní horizontální lištu ikon (7200)."""
        try:
            list_control = self.getControl(7200)
            list_control.reset()
            
            icon_items = [
                {'id': 'close', 'label': 'Zavrit', 'icon': 'DefaultFolderBack.png'},
                {'id': 'settings', 'label': 'Nastaveni', 'icon': 'DefaultAddonProgram.png'},
                {'id': 'ai_recommend', 'label': 'AI', 'icon': 'DefaultMusicSearch.png'},
            ]
            
            for icon_item in icon_items:
                item = xbmcgui.ListItem(label=icon_item['label'])
                item.setArt({'icon': icon_item['icon']})
                item.setProperty('nav_id', icon_item['id'])
                list_control.addItem(item)
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Icon bar populate error: {e}", xbmc.LOGERROR)
    
    def _handle_nav_click(self):
        """Zpracuje klik na položku v navigačním sidebaru."""
        try:
            list_control = self.getControl(7100)
            item = list_control.getSelectedItem()
            if not item:
                return
            
            nav_id = item.getProperty('nav_id')
            nav_position = list_control.getSelectedPosition()
            
            # Pokud je to aktivní sekce, jen zavřít sidebar
            if nav_id == self._nav_section_id:
                self._hide_nav_sidebar()
                self.setFocusId(2000)
                return
            
            # Zvláštní akce
            if nav_id == 'close':
                # Jen zavřít sidebar, ne celý dialog
                self._hide_nav_sidebar()
                self.setFocusId(2000)
                return
            
            if nav_id == 'settings':
                # Otevřít nastavení bez zavření dialogu
                self.addon.openSettings()
                return
            
            # Přepnout na jiný dialog (předat pozici v nav menu)
            self._switch_to_dialog(nav_id, nav_position)
            
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Nav click error: {e}", xbmc.LOGERROR)
    
    def _handle_icon_bar_click(self):
        """Zpracuje klik na položku v horizontální liště ikon (7200)."""
        try:
            list_control = self.getControl(7200)
            item = list_control.getSelectedItem()
            if not item:
                return
            
            nav_id = item.getProperty('nav_id')
            
            # Zvláštní akce
            if nav_id == 'close':
                self._hide_nav_sidebar()
                self.setFocusId(2000)
                return
            
            if nav_id == 'settings':
                self.addon.openSettings()
                return
            
            # Najit pozici nav_id ve vertikalnim menu pro spravny fokus v novem dialogu
            nav_position = 0
            for i, nav_item in enumerate(self.NAV_ITEMS):
                if nav_item['id'] == nav_id:
                    nav_position = i
                    break
            
            # Prepnout na jiny dialog s fokusem na spravnou polozku
            self._switch_to_dialog(nav_id, nav_position)
            
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Icon bar click error: {e}", xbmc.LOGERROR)
    
    def _switch_to_dialog(self, nav_id, nav_position=0):
        """Zavře aktuální dialog a otevře nový podle nav_id s otevřeným sidebarem."""
        action = self.NAV_ACTIONS.get(nav_id)
        if not action:
            return
        
        self._clear_preview_properties()
        self.close()
        
        # Spustit nový dialog s parametry pro nav sidebar
        addon_id = self.addon.getAddonInfo('id')
        xbmc.executebuiltin(f"RunPlugin(plugin://{addon_id}?action={action}&show_nav_sidebar=1&nav_position={nav_position})")


    
    def _fetch_preview_details(self, tmdb_id, media_type):
        """Načte rozšířené detaily z TMDb API."""
        try:
            api_key = get_tmdb_api_key()
            endpoint = 'movie' if media_type == 'movie' else 'tv'
            url = f'https://api.themoviedb.org/3/{endpoint}/{tmdb_id}'
            params = {'api_key': api_key, 'language': 'cs-CZ', 'append_to_response': 'credits'}
            
            resp = requests.get(url, params=params, timeout=5)
            if resp.status_code == 200:
                data = resp.json()
                
                # Připravit cache data
                cache_data = {
                    'rating': f"{data.get('vote_average', 0):.1f}/10" if data.get('vote_average') else '',
                    'runtime': f"{data.get('runtime', 0)} min" if media_type == 'movie' and data.get('runtime') else '',
                    'genre': ', '.join([g['name'] for g in data.get('genres', [])[:3]]),
                    'plot': data.get('overview', ''),
                    'poster': f"https://image.tmdb.org/t/p/w500{data.get('poster_path')}" if data.get('poster_path') else '',
                    'fanart': f"https://image.tmdb.org/t/p/w1280{data.get('backdrop_path')}" if data.get('backdrop_path') else '',
                    'tagline': data.get('tagline', ''),
                }
                
                # Pro seriály: počet sezón a STATUS
                if media_type != 'movie':
                    num_seasons = data.get('number_of_seasons', 0)
                    runtime_parts = []
                    if num_seasons:
                        runtime_parts.append(f"{num_seasons} sezón")
                    
                    # Status seriálu
                    status = data.get('status', '')
                    if status == 'Returning Series':
                        runtime_parts.append("[COLOR green]Pokračuje[/COLOR]")
                    elif status in ['Ended', 'Canceled']:
                        runtime_parts.append("[COLOR red]Ukončen[/COLOR]")
                        
                    cache_data['runtime'] = "  |  ".join(runtime_parts)
                
                # Režie/Tvůrci
                credits = data.get('credits', {})
                if media_type == 'movie':
                    directors = [c['name'] for c in credits.get('crew', []) if c.get('job') == 'Director'][:2]
                    cache_data['director'] = f"Režie: {', '.join(directors)}" if directors else ''
                else:
                    creators = [c['name'] for c in data.get('created_by', [])][:2]
                    cache_data['director'] = f"Tvůrci: {', '.join(creators)}" if creators else ''
                
                # === BUILD SEPARATE INFO PROPERTIES (for dynamic XML grouplist) ===
                # Tagline
                if cache_data.get('tagline'):
                    cache_data['fmt_tagline'] = f"[I][COLOR FFB0B0B0]{cache_data['tagline']}[/COLOR][/I]"
                else:
                    cache_data['fmt_tagline'] = ""

                # formátované Metadata: Year • Rating • Runtime
                meta_parts = []
                year = data.get('release_date', data.get('first_air_date', ''))[:4]
                if year:
                    meta_parts.append(year)
                if cache_data['rating']:
                    meta_parts.append(cache_data['rating'])
                if cache_data['runtime']:
                    meta_parts.append(cache_data['runtime'])
                
                if meta_parts:
                    cache_data['fmt_metadata'] = "[COLOR grey]" + "  •  ".join(meta_parts) + "[/COLOR]"
                else:
                    cache_data['fmt_metadata'] = ""
                
                # formátované Extended: Genre • Director
                ext_parts = []
                if cache_data['genre']:
                    ext_parts.append(cache_data['genre'])
                if cache_data['director']:
                    ext_parts.append(cache_data['director'])
                
                if ext_parts:
                    cache_data['fmt_extended'] = "[COLOR FFAAAAAA]" + "  •  ".join(ext_parts) + "[/COLOR]"
                else:
                    cache_data['fmt_extended'] = ""

                # Uložit do cache
                self._preview_cache[tmdb_id] = cache_data
                
                # Flag pro kompaktní layout
                cache_data['is_compact'] = "true" if not cache_data.get('tagline') else ""
                
                # Aplikovat pokud je stále správný titul
                if self._last_preview_id == tmdb_id:
                    self._apply_cached_preview_data(cache_data)
                    
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Preview fetch error: {e}", xbmc.LOGERROR)
    
    def _apply_cached_preview_data(self, data):
        """Aplikuje cache data na preview properties."""
        # Set formatted text properties for Grouplist
        self.setProperty('WCS.AIPreview.Tagline', data.get('fmt_tagline', ''))
        self.setProperty('WCS.AIPreview.Metadata', data.get('fmt_metadata', ''))
        self.setProperty('WCS.AIPreview.Extended', data.get('fmt_extended', ''))
        self.setProperty('WCS.AIPreview.Plot', data.get('plot', ''))
        
        # Vyčistit EpisodeInfo (bude nastaveno pouze v přetížené metodě pro Continue Watching)
        self.clearProperty('WCS.AIPreview.EpisodeInfo')
        
        # Raw properties (might be used elsewhere or legacy)
        self.setProperty('WCS.AIPreview.Rating', data.get('rating', ''))
        
        if data.get('poster'):
            self.setProperty('WCS.AIPreview.Poster', data['poster'])
        if data.get('fanart'):
            self.setProperty('WCS.AIPreview.Fanart', data['fanart'])
        
        # Nastavit compact mode property (pro XML animaci)
        if data.get('is_compact') == "true":
            self.setProperty('WCS.AIPreview.IsCompact', "true")
        else:
            self.clearProperty('WCS.AIPreview.IsCompact')
            
        # Zobrazit overlay (spustí Slide Up animaci)
        self.setProperty('WCS.AIPreview.State', 'Show')
    
    # === UNIFIED ACTION BUTTONS CONFIGURATION ===
    
    ACTIONS_REGISTRY = {
        'play':         {'label': 'Přehrát', 'action': 'play'},
        'play_again':   {'label': 'Přehrát znovu', 'action': 'play'},
        'continue':     {'label': 'Pokračovat', 'action': 'next_episode'},
        'play_episode': {'label': 'Přehrát znovu', 'action': 'play_episode'},
        'details':      {'label': 'Detaily', 'action': 'details'},
        'similar':      {'label': 'Podobné', 'action': 'similar'},
        'seasons':      {'label': 'Sezóny', 'action': 'seasons'},
        'add_list_mov': {'label': 'Moje filmy', 'action': 'add_list'},
        'add_list_ser': {'label': 'Moje seriály', 'action': 'add_list'},
        'add_library':  {'label': 'Knihovna', 'action': 'add_library'},
        'remove_hist':  {'label': 'Smazat', 'action': 'remove_from_history'},
        'remove_list':  {'label': 'Smazat', 'action': 'remove_from_list'},
        'col_movies':   {'label': 'Filmy', 'action': 'collection_movies'},
        'start_series': {'label': 'Začít sledovat', 'action': 'continue'}, # Alias pro next_episode (pokud je nový, spustí S01E01)
    }
    
    ACTION_SETS = {
        'ai_movie': ['play', 'details', 'similar', 'add_list_mov', 'add_library'],
        'ai_series': ['play', 'seasons', 'details', 'similar', 'add_list_ser', 'add_library'],
        'ai_col': ['col_movies'],
        'col_movie': ['play', 'details', 'similar', 'remove_list'], # My Movies
        'col_series': ['seasons', 'details', 'similar', 'remove_list'], # Legacy My Series (bez kontroly)
        'col_series_start': ['start_series', 'seasons', 'details', 'similar', 'remove_list'], # Smart Start
        'col_series_continue': ['continue', 'seasons', 'details', 'similar', 'remove_list'], # Smart Continue
        'hist_movie': ['play_again', 'details', 'similar', 'remove_hist'],
        'hist_series': ['continue', 'play_episode', 'details', 'similar', 'remove_hist'],
        'hist_other': ['remove_hist'],
    }
    
    CONTEXT_REGISTRY = {
        # Hub Scénáře
        'hub_movies':       {'type': 'simple_mix', 'media_type': 'movie'},
        'hub_series':       {'type': 'simple_mix', 'media_type': 'series'},
        'hub_mix':          {'type': 'simple_mix', 'media_type': 'mixed'},
        'hub_col':          {'type': 'simple_collections', 'media_type': 'collection'}, # Hub Collections (simple) je specificky
        'hub_my_col':       {'type': 'my_collections', 'media_type': 'collection'}, # Podle sbírky (Hub)
        
        # Historie (Continue Watching)
        'hist_movie':       {'type': 'recently_played', 'media_type': 'movie'},
        'hist_series':      {'type': 'recently_played', 'media_type': 'series'},
        'hist_mixed':       {'type': 'recently_played', 'media_type': 'mixed'},
        
        # Sbírky (MyMovies/MySeries)
        'my_movies':        {'type': 'my_collections', 'media_type': 'movie'},
        'my_series':        {'type': 'my_collections', 'media_type': 'series'},
        'my_mixed_collection': {'type': 'my_mixed_collection', 'media_type': 'mixed'}, # Pro Mixed Context
        
        # Detail / Podobné
        'similar_mov':      {'type': 'similar', 'media_type': 'movie'}, # Type none -> bude se chovat jako cisty dotaz bez kontextu dat, jen s inspiraci
        'similar_ser':      {'type': 'similar', 'media_type': 'series'},
        
        # Ostatní specifické
        'fav_actors':       {'type': 'favorite_actors', 'media_type': 'movie'}, # Default movie, ale v builderu se to bere jako mix
        'fav_directors':    {'type': 'favorite_directors', 'media_type': 'movie'},
        'fav_writers':      {'type': 'favorite_writers', 'media_type': 'movie'},
        'col_detail':       {'type': 'collection', 'media_type': 'movie'}, # Konkretni kolekce
        
        # Default (bez datoveho kontextu)
        'default_movie':    {'type': None, 'media_type': 'movie'},
        'default_series':   {'type': None, 'media_type': 'series'},
    }

    def start_ai_with_context(self, context_key, data=None, user_text=None, is_initial=True):
        """
        Unified metoda pro start AI konverzace s definovaným kontextem.
        Nahrazuje ruční vytváření context slovníku a builderu.
        
        Args:
            context_key (str): Klíč do CONTEXT_REGISTRY (např. 'hist_movie')
            data (list|dict): Data pro kontext (seznam filmů, položky historie atd.)
            user_text (str): Volitelný text uživatele (pokud je None, použije se defaultní flow)
            is_initial (bool): Zda jde o počáteční kontext (default True)
        """
        try:
            config = self.CONTEXT_REGISTRY.get(context_key)
            if not config:
                xbmc.log(f"[plugin.video.milionar] Uknown context key: {context_key}, falling back to movie", xbmc.LOGWARNING)
                config = {'type': None, 'media_type': 'movie'}
            
            # 1. Nastavit interní stav
            self.media_type = config['media_type']
            
            # Pokud je typ 'similar', tak kontext mazeme (to je specifika _handle_similar)
            # Ale pro konzistenci zde vytvorime kontextovou strukturu, kterou builder pochopi
            # V soucasne implementaci builderu: pokud context.type neni znamy, chova se jako obecny dotaz
            
            self.context = {
                'type': config['type'],
                'media_type': self.media_type,
                'data': data
            }
            
            # 2. Inicializovat builder
            self._prompt_builder = AIRecommendationPromptBuilder(self.media_type, self.context)
            
            # 3. Případně přidat user message do historie hned (pokud je zadána explicitně)
            # V původním kódu se to dělalo ručně před voláním start_ai
            if user_text:
                self.history.append({"role": "user", "content": user_text})
                self.append_chat_message("Ty", user_text)
                
            # 4. Spustit
            self._start_ai_conversation(is_initial_context=is_initial)
            
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] start_ai_with_context error: {e}", xbmc.LOGERROR)

    def build_action_items(self, context_key):
        """Vytvoří seznam ListItemů pro daný kontext akcí."""
        items = []
        from wcs.library2.compat import drop_library_button
        action_keys = drop_library_button(self.ACTION_SETS.get(context_key, []))      # skin.milionar: no Kodi library
        for key in action_keys:
            config = self.ACTIONS_REGISTRY.get(key)
            if config:
                item = xbmcgui.ListItem(label=config['label'])
                item.setProperty('action', config['action'])
                if 'icon' in config:
                    item.setArt({'icon': config['icon']})
                items.append(item)
        return items

    def _populate_preview_actions(self, media_type, tmdb_id, title, year):
        """Naplní akční tlačítka podle typu média (používá Unified Configuration)."""
        try:
            list_control = self.getControl(6100)
            list_control.reset()
            
            # Určit kontext (pro base class je to vždy AI doporučení)
            if media_type == 'movie':
                context = 'ai_movie'
            elif media_type in ['series', 'tv']:
                context = 'ai_series'
            else:
                context = 'ai_col'
                
            # Získat itemy z konfigurace
            items = self.build_action_items(context)
            
            # Obohatit itemy o metadata (to je společné pro všechny)
            for item in items:
                item.setProperty('tmdb_id', str(tmdb_id))
                item.setProperty('title', title)
                item.setProperty('year', year)
                item.setProperty('media_type', media_type)
                list_control.addItem(item)
                
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Preview actions error: {e}", xbmc.LOGERROR)
    
    def _handle_preview_action_click(self):
        """Zpracuje kliknutí na akční tlačítko v preview."""
        try:
            list_control = self.getControl(6100)
            item = list_control.getSelectedItem()
            if not item:
                return
            
            action = item.getProperty('action')
            tmdb_id = item.getProperty('tmdb_id')
            title = item.getProperty('title')
            year = item.getProperty('year')
            media_type = item.getProperty('media_type')
            
            if action == 'play':
                # Přehrát film
                # Přehrát film s Inline Stream Pickerem
                cache_data = self._preview_cache.get(tmdb_id, {})
                plot = cache_data.get('plot', '')
                poster = self.getProperty('WCS.AIPreview.Poster')
                
                # Sestavení dotazu a metadata
                query = f"{title} {year}"
                
                # Instantiace SearchDialogu s naším novým XML (Inline Picker)
                # Používáme 'custom_xml' pokud by to SearchDialog podporoval, ale on bere xml v __init__
                # Vytvoříme dialog přímo
                addon_path = self.addon.getAddonInfo('path')
                search_dialog = SearchDialog(
                    "ai/DialogAIChatSearch.xml", 
                    addon_path, 
                    "Default", 
                    "1080i",
                    search_query=query,
                    movie_title=title,
                    movie_year=year,
                    poster_url=poster,
                    plot=plot,
                    media_type='movie',
                    is_episode=False,
                    from_library=False,
                    search_results=[]
                )
                
                # Zobrazit jako modální dialog (traps focus)
                search_dialog.doModal()
                
                # Získat výsledek
                selected_file = getattr(search_dialog, 'selected_result', None)
                del search_dialog  # Uvolnit dialog
                
                if selected_file and 'ident' in selected_file:
                    # Tracked play: canonical resolver gets the chosen source (docs/plan/milionar-library 040)
                    if str(tmdb_id or '').isdigit():
                        from wcs.library2 import play as library_play
                        library_play.play_movie(int(tmdb_id), pick=selected_file)
                        return
                    # Play Logic (zkopírováno/adaptováno z wcs/utils.py)
                    ident = selected_file['ident']
                    name = selected_file.get('name', '')
                    
                    # Uložit do historie
                    try:
                         item_data = {
                             'title': title,
                             'year': year,
                             'poster': poster,
                             'rating': cache_data.get('rating', ''),
                             'media_type': 'movie',
                             'tmdb_id': tmdb_id,
                             'plot': plot,
                             'genre': cache_data.get('genre', '')  # Cache to asi nemá, ale nevadí
                         }
                         user_data.add_recently_played_item(self.addon, item_data)
                    except Exception as e:
                        xbmc.log(f"[plugin.video.milionar] Error saving recently played: {e}", xbmc.LOGERROR)

                    # Spustit přehrávání
                    play_url = (f"plugin://{self.addon.getAddonInfo('id')}?action=play"
                               f"&ident={ident}"
                               f"&name={urllib.parse.quote_plus(name)}"
                               f"&tmdb_title={urllib.parse.quote_plus(title)}"
                               f"&tmdb_year={urllib.parse.quote_plus(year) if year else ''}")
                    
                    # Zavřít AI Chat dialog před přehráváním? 
                    # Uživatel chtěl "Inline". Pokud nezavřeme, video se možná spustí na pozadí?
                    # Obvykle PlayMedia na videu zavře dialogy nebo spustí přehrávač v popředí.
                    # Ale pro čistotu a návrat.. moment.
                    # Pokud zavřeme, ztratíme kontext AI chatu.
                    # Uživatel: "uživatel bude mít pocit že je pořád v tom fullscreenu".
                    # Přehrávání Kodi VŽDY fullscreen (kromě windowed režimu).
                    # Až skončí video, vrátí se sem?
                    # Pokud použijeme xbmc.Player().play(), dialog zůstane otevřený pod videem (pokud video overlay).
                    
                    # Fix: Concurrent busydialogs crash
                    # Používáme ověřenou metodu z wcs/utils.py -> play_movie_from_addon
                    # Musíme zavřít všechny dialogy (včetně AI Chat), aby nedošlo ke kolizi busydialogů při startu přehrávače
                    xbmc.executebuiltin('Dialog.Close(all, true)')
                    xbmc.executebuiltin(f'PlayMedia({play_url})')
                
            elif action == 'details':
                # Zobrazit WCS detaily
                m_type = 'tv' if media_type in ['series', 'tv'] else 'movie'
                url = f"plugin://{self.addon.getAddonInfo('id')}?action=show_wcs_details&media_type={m_type}&tmdb_id={tmdb_id}"
                xbmc.executebuiltin(f"RunPlugin({url})")
                
            elif action == 'seasons':
                # Zobrazit sezóny seriálu
                from urllib.parse import quote_plus
                url = f"plugin://{self.addon.getAddonInfo('id')}?action=display_series_seasons&series_id={tmdb_id}&name={quote_plus(title)}"
                self.close()
                xbmc.executebuiltin(f'AlarmClock(OpenSeasons,Container.Update({url}),00:01,silent)')
                
            elif action == 'add_list':
                # Přidat do seznamu
                if media_type == 'movie':
                    from wcs.ui.UI import add_movie_item_to_list
                    add_movie_item_to_list(self.addon, tmdb_id, title)
                else:
                    from wcs.ui.UI import add_series_item_to_list
                    add_series_item_to_list(self.addon, tmdb_id, title)
                    
            elif action == 'add_library':
                # Přidat do knihovny Kodi
                from wcs.library import LibraryManager as library
                lib_media_type = 'movie' if media_type == 'movie' else 'series'
                id_key = 'movie_id' if media_type == 'movie' else 'series_id'
                library.check_settings_and_add_item({'media_type': lib_media_type, id_key: tmdb_id}, self.addon)
                
            elif action == 'similar':
                # KRITICKÉ: Nastavit správný media_type podle aktuálního titulu
                # Tím se zajistí správný system prompt (filmy vs seriály)
                if media_type in ['series', 'tv']:
                    self.media_type = 'series'
                    inspiration_text = f"Seriál '{title}' ({year}) mě nadchl. Hledám podobný seriál - může mít jinou atmosféru, ale ať mě to vtáhne stejně."
                else:
                    self.media_type = 'movie'
                    inspiration_text = f"Film '{title}' ({year}) mě zaujal. Chci zažít něco podobného. Nemusí to být stejný žánr, ale ať to má podobnou atmosféru."
                
                # Vymazat starý kontext (jde o prostý dotaz bez kontextu)
                self.context = None
                
                # Reinicializovat prompt builder s novým media_type a bez kontextu
                self._prompt_builder = AIRecommendationPromptBuilder(self.media_type, self.context)
                
                # Skrýt overlay a fokus na chat, aby byla vidět odpověď
                self._clear_preview_properties()
                self.setFocusId(4000)
                # Spustit načítací animaci
                self._start_button_loading("Hledám podobné...")
                self.handle_user_input(inspiration_text)
                
            elif action == 'collection_movies':
                # Zobrazit filmy v kolekci
                from urllib.parse import quote_plus
                url = f"plugin://{self.addon.getAddonInfo('id')}?action=list_collection_movies&collection_id={tmdb_id}&collection_name={quote_plus(title)}"
                xbmc.executebuiltin(f"Container.Update({url})")
                
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Preview action error: {e}", xbmc.LOGERROR)
    
    def _start_simple_media_conversation(self):
        """
        Spustí jednoduchou konverzaci pro doporučení filmů/seriálů bez kontextu.
        Volá se z:
          - set_mode button (uživatel vybral Filmy/Seriály v AI chat rootu)
          - section_root kontext (uživatel dal "Poradit se s AI" v sekci Filmy/Seriály)
        """
        user_msg = "Mám chuť na skvělý film. Ukaž mi, co máš." if self.media_type == "movie" else "Hledám seriál, který mě nepustí. Nějaké tipy?"
        
        self.append_chat_message("Ty", user_msg)
        self.history.append({"role": "user", "content": user_msg})
        
        # Inicializovat prompt builder
        self._prompt_builder = AIRecommendationPromptBuilder(self.media_type, self.context)
        
        # Start AI conversation
        self._start_ai_conversation()
    
    def _start_ai_conversation(self, is_initial_context=False):
        """Start AI conversation immediately after media type selection or on init with context."""
        # Start animated thinking indicator
        self._start_thinking_animation()
        
        # Call AI in thread
        # Pokud jde o initial context, posíláme prázdný user_text, protože zpráva je už v historii v onInit
        user_text = "" 
        t = threading.Thread(target=self._ai_worker, args=(user_text,))
        t.start()
    
    def _start_thinking_animation(self):
        """Start the animated dots animation."""
        self._thinking_stop.clear()
        self._dot_count = 0
        self.setProperty('WCS.AIChat.StatusText', 'Odesílám...')
        self.setProperty('WCS.AIChat.Timer', '')
        self._thinking_thread = threading.Thread(target=self._animate_dots)
        self._thinking_thread.start()
    
    def _stop_thinking_animation(self):
        """Stop the animated dots animation."""
        self._thinking_stop.set()
        if self._thinking_thread:
            self._thinking_thread.join(timeout=1)
            self._thinking_thread = None
        
        # Show final time and ready state
        try:
            final_time = self.getProperty('WCS.AIChat.Timer').replace('s', '')
            if final_time:
                self.setProperty('WCS.AIChat.Timer', f"za {final_time}s")
                self.setProperty('WCS.AIChat.StatusText', 'Připraven')
        except:
            self.setProperty('WCS.AIChat.StatusText', 'Připraven')
    
    def _animate_dots(self):
        """Animate status text and timer."""
        import time
        start_time = time.time()
        while not self._thinking_stop.is_set():
            # Update Timer every 0.1s
            elapsed = time.time() - start_time
            self.setProperty('WCS.AIChat.Timer', f"{elapsed:.1f}s")
            
            # Switch text after 2 seconds
            if elapsed > 2.0:
                 current_status = "Přijímám"
            else:
                 current_status = "Odesílám"

            # Update Dots every 0.5s
            num_dots = (self._dot_count // 5) % 4
            dots = "." * num_dots
            
            self.setProperty('WCS.AIChat.StatusText', f"{current_status}{dots}")
            
            self._dot_count += 1
            time.sleep(0.1)
            
    def handle_user_input(self, user_text, display_text=None):
        """Process user input, update UI, and call AI.
        
        Args:
            user_text: Text který se pošle do LLM
            display_text: Volitelný text pro zobrazení v UI (pokud None, použije se user_text)
        """
        if not self.media_type:
            # Should not happen if UI is correct, but fallback
            self.media_type = "movie"
            
        # 1. Update Chat UI - zobrazit display_text nebo user_text
        ui_text = display_text if display_text else user_text
        self.append_chat_message("Ty", ui_text)
        
        # [NEW] Ensure Chat is visible when user interacts
        self.setProperty('WCS.AIChat.Visible', 'true')
        
        # 2. Add to history - uložit plný text pro LLM kontext
        self.history.append({"role": "user", "content": user_text})
        
        # 3. Start animated thinking indicator
        self._start_thinking_animation()
        
        # 4. Call AI in thread
        t = threading.Thread(target=self._ai_worker, args=(user_text,))
        t.start()
    
    def _get_context_instruction(self):
        """
        Získá kontextovou instrukci pro AI na základě aktivního kontextu.
        Generuje specifické instrukce pro různé typy kontextu (my_movies, genre, person, atd.).
        """
        if not self.context:
            return ""
        
        ctx_type = self.context.get('type')
        ctx_val = self.context.get('value')
        ctx_data = self.context.get('data', [])
        
        # Načtení limitu pro kontext (počet titulů k analýze z knihovny)
        try:
            limit_setting = self.addon.getSetting('ai_context_history_limit')
            limit = int(limit_setting) if limit_setting else 20
        except:
            limit = 20
        
        media_label = "filmů" if self.media_type == "movie" else "seriálů"
        
        # === MY MOVIES / MY SERIES ===
        if ctx_type in ['my_movies', 'my_series'] and ctx_data:
            titles = [f"{item.get('title', item.get('name'))} ({item.get('year')})" for item in ctx_data[:limit]]
            titles_str = ", ".join(titles)
            return f"Uživatel má v knihovně tyto {media_label}: {titles_str}. Toto je jeho 'DNA vkusu'. Analyzuj to a najdi skryté klenoty, které by mu neměly uniknout. Nechci jen prostou podobnost, chci, abys vystihl atmosféru jeho sbírky."
        
        # === MY MIXED COLLECTION ===
        elif ctx_type == 'my_mixed_collection' and ctx_data:
            movies = ctx_data.get('movies', [])
            series = ctx_data.get('series', [])
            
            m_count, s_count = len(movies), len(series)
            target_m, target_s = limit // 2, limit - limit // 2
            
            if m_count < target_m:
                target_s += (target_m - m_count)
                target_m = m_count
            elif s_count < target_s:
                target_m += (target_s - s_count)
                target_s = s_count
            
            m_titles = [f"{m.get('title')} ({m.get('year')})" for m in movies[:target_m]]
            s_titles = [f"{s.get('name')} ({s.get('year')})" for s in series[:target_s]]
            all_str = ", ".join(m_titles + s_titles)
            
            return f"Uživatelova osobní videotéka obsahuje: {all_str}. Tvým úkolem je najít průniky v tomto výběru a navrhnout další tituly, které perfektně zapadnou do této mozaiky."
        
        # === RECENTLY PLAYED ===
        elif ctx_type == 'recently_played' and ctx_data:
            titles = []
            for item in ctx_data[:limit]:
                title = item.get('title', '')
                year = item.get('year', '')
                
                if item.get('media_type') == 'series':
                    ep_title = item.get('episode_title', '')
                    season, episode = item.get('season'), item.get('episode')
                    if season is not None and episode is not None:
                        ep_str = f"S{int(season):02d}E{int(episode):02d}"
                        title += f" ({ep_str} {ep_title or ''})"
                        
                if year:
                    title += f" ({year})"
                titles.append(title)
            
            titles_str = ", ".join(titles)
            return f"Naposledy sledované tituly: {titles_str}. Na základě tohoto 'čerstvého' zážitku doporuč, co si pustit dál. Udrž vlnu, nebo nabídni zajímavý kontrast."
        
        # === MY COLLECTIONS ===
        elif ctx_type == 'my_collections' and ctx_data:
            c_names = [c.get('name', 'Neznámá kolekce') for c in ctx_data[:50]]
            colls_str = ", ".join(c_names)
            return f"Uživatel sbírá tyto ságy: {colls_str}. Má rád komplexní světy a dlouhé příběhy. Doporuč mu další epické filmové série nebo univerza."
        
        # === FAVORITE ACTORS/DIRECTORS/WRITERS ===
        elif ctx_type in ['favorite_actors', 'favorite_directors', 'favorite_writers'] and ctx_data:
            names = [item.get('name') for item in ctx_data]
            names_str = ", ".join(names)
            what_map = {'favorite_actors': 'herce', 'favorite_directors': 'režiséry', 'favorite_writers': 'scenáristy'}
            what = what_map.get(ctx_type, 'osobnosti')
            return f"Uživatelovy ikony jsou: {names_str}. Najdi jejich nejlepší, možná i méně známá díla, a vysvětli, proč v nich tito {what} excelují."
        
        # === GENRE ===
        elif ctx_type == 'genre':
            return f"Uživatel má chuť na žánr: {ctx_val}. Vyber absolutní špičku tohoto žánru, ale zkus překvapit i něčím netradičním."
        
        # === PERSON ===
        elif ctx_type == 'person':
            p_type = self.context.get('person_type', 'osoba')
            return f"Středem zájmu je {p_type}: {ctx_val}. Doporuč díla, která definují kariéru této osobnosti."
        
        # === COUNTRY ===
        elif ctx_type == 'country':
            return f"Cestujeme do země: {ctx_val}. Vyber to nejlepší, co tamní kinematografie nabízí - od klasik po současné hity."
        
        # === YEAR ===
        elif ctx_type == 'year':
            return f"Stroj času nastaven na rok: {ctx_val}. Co byly tehdejší hity a co obstálo ve zkoušce času?"
        
        # === COLLECTION ===
        elif ctx_type == 'collection':
            return f"Zajímá nás sága: {ctx_val}. Pokud se uživateli líbí toto univerzum, co dalšího by ho mohlo pohltit?"
        
        # === CATEGORY ===
        elif ctx_type == 'category':
            return f"Uživatel prohlíží kategorii: {ctx_val}. Doporuč tituly, které tuto kategorii nejlépe reprezentují."
        
        return ""
        
    def _ai_worker(self, user_text):
        # Zajistit, že máme prompt builder
        if not self._prompt_builder:
            self._prompt_builder = AIRecommendationPromptBuilder(self.media_type, self.context)
        
        # Sestavit system prompt
        system_prompt = self._prompt_builder.build_system_prompt()
        
        # Získat context_instruction z kontextu (pokud existuje)
        context_instruction = self._get_context_instruction()
        
        # Sestavit messages pole pro multi-turn API
        messages = self._prompt_builder.build_messages(
            self.history, 
            context_instruction=context_instruction
        )
        
        # Extrahovat context_titles z Kodi knihovny pro debug dialog
        context_titles = []
        context_limit = 20
        try:
            context_limit = int(self.addon.getSetting('ai_context_history_limit') or 20)
        except:
            pass
            
        if self.context and self.context.get('data'):
            ctx_data = self.context.get('data')
            if isinstance(ctx_data, list):
                for item in ctx_data[:context_limit]:
                    t = item.get('title') or item.get('name')
                    if t: context_titles.append(t)
            elif isinstance(ctx_data, dict):  # Mixed collection
                for m in ctx_data.get('movies', [])[:context_limit//2]:
                    context_titles.append(f"[Movie] {m.get('title')}")
                for s in ctx_data.get('series', [])[:context_limit//2]:
                    context_titles.append(f"[Series] {s.get('name')}")
        
        # Volat funkci v AIHandler s nativním messages polem
        data, usage, raw_response, latency = ai_handler.send_chat_recommendation(
            system_prompt, 
            messages,
            context_titles=context_titles,
            context_limit=context_limit
        )

        # Uložit odpověď pro debug (poslední přijmutá data)
        self._last_response_data = data
        self._last_response_usage = usage
        self._last_raw_response = raw_response
        ai_params = self._get_ai_display_values()
        self._last_response_metadata = {
            'provider': ai_params.get('provider', 'Unknown'),
            'model': ai_params.get('model', 'Unknown'),
            'latency': latency
        }
        
        # Stop the thinking animation
        self._stop_thinking_animation()
        
        if not data:
            self.setProperty('WCS.AIChat.Status', 'Chyba komunikace')
            return

        # Update History with COMPLETE AI response (celý JSON včetně recommendations)
        # Model tak bude vědět jaké tituly už doporučil
        import json
        full_response_json = json.dumps(data, ensure_ascii=False)
        self.history.append({"role": "assistant", "content": full_response_json})
        
        # Pro UI zobrazit jen message
        ai_msg = data.get('message', '')
        
        # Schedule UI Update on main thread logic (using xbmc operations is mostly safe, but let's be careful)
        # We can call methods directly as they mostly use Kodi GUI calls which are thread-safe enough for property/control setting usually
        self.append_chat_message("AI", ai_msg)
        self.setProperty('WCS.AIChat.Status', 'Připraven')
        
        # SHOW INPUT FIELD after first AI response
        self.setProperty('WCS.AIChat.ShowInput', 'true')
        
        # Načíst buttons a recommendations jednou
        buttons = data.get('buttons', [])
        recs = data.get('recommendations', [])
        
        # Pokud máme doporučení, přidej tlačítko pro další várku
        if recs:
            recomm_count = int(self.addon.getSetting('ai_recommendation_count') or 11)
            more_button = {
                "label": f"Chci další dávku inspirace ({recomm_count})",
                "value": f"To bylo dobré, ale chci víc. Ponoř se hlouběji do filmových archivů a vytáhni dalších {recomm_count} NOVÝCH a UNIKÁTNÍCH titulů. Překvap mě něčím nečekaným, co jsem v seznamu ještě neměl."
            }
            buttons.insert(0, more_button)
        
        # Signal animation thread that data is ready
        self._buttons_ready = True
        self.update_buttons(buttons)
        
        # Handle Recommendations (používáme již načtené recs)
        if recs:
            self.fetch_and_display_recommendations(recs)
        else:
            # Model nevrátil žádná doporučení - volitelné přetížení v child třídách
            self._handle_no_recommendations()
    
    def _handle_no_recommendations(self):
        """Zpracuje případ kdy AI nevrátí žádné tituly. Může být přetíženo v child třídách."""
        self.append_system_message("Model nedoporučil žádné tituly.")

            
    def fetch_and_display_recommendations(self, recommendations):
        """Fetch TMDB data and populate list."""
        # Use a worker to fetch TMDB to avoid freezing
        t = threading.Thread(target=self._tmdb_worker, args=(recommendations,))
        t.start()
        
    def _tmdb_worker(self, recommendations):
        list_control = self.getControl(2000)
        list_control.reset()
        matched = []  # movie/tv matches for the skin's AI history widget (docs/plan/skin-milionar 060 §6)
        
        for rec in recommendations:
            title = rec.get('title')
            year = rec.get('year')
            # Determine media type for this specific item (mixed content support)
            # Default to self.media_type if not specified by AI
            item_media_type = rec.get('type')
            if item_media_type not in ['movie', 'tv', 'collection']:
                 item_media_type = self.media_type
                 
            # Fetch TMDB
            tmdb_data = self._search_tmdb(title, year, item_media_type)
            
            if tmdb_data:
                # Create ListItem - pro serialy a kolekce TMDb vraci 'name' misto 'title'
                display_title = tmdb_data.get('title') or tmdb_data.get('name') or title
                item = xbmcgui.ListItem(label=display_title)
                poster = tmdb_data.get('poster_path')
                if poster:
                    item.setArt({'poster': f'https://image.tmdb.org/t/p/w500{poster}'})
                    
                # Store metadata for actions
                item.setProperty('tmdb_id', str(tmdb_data.get('id')))
                item.setProperty('media_type', item_media_type)
                item.setProperty('title', tmdb_data.get('title') or tmdb_data.get('name') or '')
                # Year: kolekce nemaji release_date
                if item_media_type == 'movie':
                    item.setProperty('year', tmdb_data.get('release_date', '')[:4])
                elif item_media_type == 'tv':
                    item.setProperty('year', tmdb_data.get('first_air_date', '')[:4])
                else:  # collection - nema rok
                    item.setProperty('year', '')
                item.setProperty('original_title', tmdb_data.get('original_title', tmdb_data.get('original_name','')))
                
                list_control.addItem(item)
                if item_media_type in ('movie', 'tv'):
                    matched.append({
                        'tmdb_id': tmdb_data.get('id'), 'media_type': item_media_type,
                        'title': display_title, 'year': item.getProperty('year'),
                        'poster': f'https://image.tmdb.org/t/p/w500{poster}' if poster else '',
                        'fanart': (f"https://image.tmdb.org/t/p/w1280{tmdb_data['backdrop_path']}"
                                   if tmdb_data.get('backdrop_path') else ''),
                        'plot': tmdb_data.get('overview', '')})

        if matched:
            try:
                from wcs.ai import RecommendationHistory
                RecommendationHistory.add(matched)
            except Exception as exc:  # the history must never break the dialog
                xbmc.log(f"[Milionář Skin] AI history not saved: {exc}", xbmc.LOGWARNING)
                
        # After populating list, check results
        if list_control.size() > 0:
            # Uspech - mame tituly, HIDE chat and FOCUS list
            import time
            time.sleep(0.2)
            self.clearProperty('WCS.AIChat.Visible')  # Hide Chat Overlay
            self.setFocusId(2000)  # Focus Content
        else:
            # TMDb nic nenašel - systémová zpráva, zůstat v chatu
            self.append_system_message("Model doporučil neexistující tituly.")
                
    def _search_tmdb(self, title, year, media_type):
        """Identify movie/series/collection in TMDB."""
        try:
            api_key = get_tmdb_api_key()
            if media_type == "movie":
                endpoint = "search/movie"
            elif media_type == "collection":
                endpoint = "search/collection"
            else:
                endpoint = "search/tv"

            params = {'api_key': api_key, 'language': 'cs-CZ', 'query': title}
            if year and media_type != "collection": # Collections dont handle year filter well in search
                if media_type == "movie":
                     params['year'] = year
                else:
                     params['first_air_date_year'] = year
                    
            resp = requests.get(f'https://api.themoviedb.org/3/{endpoint}', params=params, timeout=5)
            if resp.status_code == 200:
                results = resp.json().get('results', [])
                if results:
                    return results[0]
        except Exception as e:
            xbmc.log(f"TMDB Error: {e}", xbmc.LOGERROR)
        return None

    def _handle_debug_trigger(self):
        """Manually trigger AI Debug Dialog with current context or last received data."""
        try:
            # === Varianta A: Zobrazit poslední PŘIJMUTÁ data (Response) ===
            # Uživatel explicitně žádal "otevřel s těma posledníma přijmutýma datama"
            if self._last_raw_response:
                provider = self._last_response_metadata.get('provider', 'Unknown')
                model = self._last_response_metadata.get('model', 'Unknown')
                latency = self._last_response_metadata.get('latency', 0)

                # Zobrazit response debug (FORCE MODE) s raw daty přímo
                show_api_response_debug(
                    provider, 
                    model, 
                    self._last_raw_response,  # Raw API data jako hlavní zdroj
                    latency=latency,
                    force=True
                )
                return

            # === Varianta B: Fallback na Request (pokud ještě není response) ===
            if not self._prompt_builder:
                return

            # 1. Gather AI Params
            ai_params = self._get_ai_display_values()
            provider = ai_params.get('provider', 'Unknown')
            model = ai_params.get('model', 'Unknown')
            
            # 2. Reconstruct System Prompt & Messages
            system_prompt = self._prompt_builder.build_system_prompt()
            context_instr = self._get_context_instruction()
            messages = self._prompt_builder.build_messages(self.history, context_instruction=context_instr)
            
            # 3. Prepare Data
            request_data = {
                'system': system_prompt,
                'messages': messages,
                'model': model
            }
            
            # 4. Context Titles (Optional)
            context_titles = []
            if self.context and self.context.get('data'):
                limit = 20
                ctx_data = self.context.get('data')
                if isinstance(ctx_data, list):
                    for item in ctx_data[:limit]:
                        t = item.get('title') or item.get('name')
                        if t: context_titles.append(t)
                elif isinstance(ctx_data, dict): # Mixed collection
                    for m in ctx_data.get('movies', [])[:10]:
                        context_titles.append(f"[Movie] {m.get('title')}")
                    for s in ctx_data.get('series', [])[:10]:
                        context_titles.append(f"[Series] {s.get('name')}")

            # 5. Open Dialog (FORCE MODE)
            show_api_request_debug(provider, model, request_data, context_titles=context_titles, force=True)
            
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Debug trigger error: {e}", xbmc.LOGERROR)

    def handle_recommendation_click(self):
        """Handle click on recommendation card."""
        list_control = self.getControl(2000)
        item = list_control.getSelectedItem()
        if not item: return
        
        tmdb_id = item.getProperty('tmdb_id')
        title = item.getProperty('title')
        year = item.getProperty('year')
        media_type = item.getProperty('media_type')
        
        # Načíst preferovanou akci z nastavení
        # Values: "Vyhledat na Webshare", "Vyhledat a přehrát", "Zobrazit WCS detaily", "Zobrazit menu akcí"
        click_action = self.addon.getSetting('ai_click_action')
        
        # Pro kolekce vždy zobrazíme menu, nebo pokud uživatel explicitně chce menu
        if media_type == "collection" or click_action == "Zobrazit menu akcí":
            self._show_action_menu(tmdb_id, title, year, media_type, item)
            return

        # Rychlé akce pro Film a Seriál
        if click_action == "Vyhledat na Webshare":
            # Varianta 1: Rovnou vyhledá na webshare (textové hledání)
            if media_type == "movie":
                # Pro filmy se chová stejně jako akce "Přehrát" - tj. vyhledá soubory a nabídne výběr (process play)
                plot = item.getProperty('plot') or ''
                poster = item.getArt('poster') or ''
                from wcs.utils import search_and_play_movie
                search_and_play_movie(title, year, plot, poster, '', '', '', '', '', self.addon)
                # NEZAVÍRAT self.close() - zavře se samo pokud začne přehrávání (Dialog.Close(all)), jinak zůstane otevřeno
            else:
                # Pro seriály/kolekce použijeme fallback na menu
                self._show_action_menu(tmdb_id, title, year, media_type, item)

        elif click_action == "Vyhledat a přehrát":
            # Varianta 2: Rovnou vyhledá na webshare a automaticky přehraje (pokud to jde, jinak search & play)
            if media_type == "movie":
                plot = item.getProperty('plot') or ''
                poster = item.getArt('poster') or ''
                from wcs.utils import search_and_play_movie
                # search_and_play_movie zahájí proces výběru a přehrání s vynuceným autoplay
                search_and_play_movie(title, year, plot, poster, '', '', '', '', '', self.addon, autoplay=True)
                # NEZAVÍRAT self.close()
            else:
                # Pro seriály/kolekce použijeme fallback na menu
                self._show_action_menu(tmdb_id, title, year, media_type, item)
                
        elif click_action == "Zobrazit WCS detaily":
            # Varianta 3: Místo otevření dialogu přesuneme fokus na preview tlačítka (uživatel si detaily/přehrání vybere tam)
            self.setFocusId(6100)

    def _show_action_menu(self, tmdb_id, title, year, media_type, item):
        """Standardní modální dialog s akcemi (původní chování)."""
        dialog = xbmcgui.Dialog()
        
        if media_type == "collection":
            # KOLEKCE
            opts = [
                "Zobrazit filmy v kolekci",
                f"Inspiruj se: {title}",
                "Přidat do Moje filmy",
                "Přidat do knihovny Kodi"
            ]
            sel = compat_select(dialog, f"Akce: {title}", opts)
            
            if sel == 0:
                # Zobrazit kolekci 
                from urllib.parse import quote_plus
                url = f"plugin://{self.addon.getAddonInfo('id')}?action=list_collection_movies&collection_id={tmdb_id}&collection_name={quote_plus(title)}"
                xbmc.executebuiltin(f"Container.Update({url})")
            elif sel == 1:
                inspiration_text = f"Kolekce '{title}' mě baví. Doporuč mi jiné filmové ságy nebo světy, do kterých se mohu ponořit."
                self.handle_user_input(inspiration_text)
            elif sel == 2:
                # Přidat do Moje filmy
                url = f"plugin://{self.addon.getAddonInfo('id')}?action=add_collection_to_my_movies&collection_id={tmdb_id}"
                xbmc.executebuiltin(f"RunPlugin({url})")
            elif sel == 3:
                # Přidat do knihovny Kodi
                url = f"plugin://{self.addon.getAddonInfo('id')}?action=add_collection_to_library&collection_id={tmdb_id}"
                xbmc.executebuiltin(f"RunPlugin({url})")
                
        elif media_type == "series" or media_type == "tv":
            # SERIÁL
            opts = [
                "Zobrazit detaily",
                "Zobrazit sezóny",
                f"Inspiruj se: {title}",
                "Přidat do Moje seriály",
                "Přidat do knihovny Kodi"
            ]
            sel = compat_select(dialog, f"Akce: {title} ({year})", opts)
            
            if sel == 0:
                detail_url = f"plugin://{self.addon.getAddonInfo('id')}?action=show_wcs_details&media_type=tv&tmdb_id={tmdb_id}"
                xbmc.executebuiltin(f"RunPlugin({detail_url})")
            elif sel == 1:
                from urllib.parse import quote_plus
                seasons_url = f"plugin://{self.addon.getAddonInfo('id')}?action=display_series_seasons&series_id={tmdb_id}&name={quote_plus(title)}"
                self.close()
                xbmc.executebuiltin(f'AlarmClock(OpenSeasons,Container.Update({seasons_url}),00:01,silent)')
            elif sel == 2:
                inspiration_text = f"Seriál '{title} ({year})' se mi líbí. Hledám další seriál, který mě chytne a nepustí (binge-watch potenciál)."
                self.handle_user_input(inspiration_text)
            elif sel == 3:
                from wcs.ui.UI import add_series_item_to_list
                add_series_item_to_list(self.addon, tmdb_id, title)
            elif sel == 4:
                from wcs.library import LibraryManager as library
                library.check_settings_and_add_item({'media_type': 'series', 'series_id': tmdb_id}, self.addon)
        else:
            # FILM
            opts = [
                "Zobrazit detaily",
                "Přehrát",
                f"Inspiruj se: {title}",
                "Přidat do Moje filmy",
                "Přidat do knihovny Kodi"
            ]
            sel = compat_select(dialog, f"Akce: {title} ({year})", opts)
            
            if sel == 0:
                detail_url = f"plugin://{self.addon.getAddonInfo('id')}?action=show_wcs_details&media_type=movie&tmdb_id={tmdb_id}"
                xbmc.executebuiltin(f"RunPlugin({detail_url})")
            elif sel == 1:
                plot = item.getProperty('plot') or ''
                poster = item.getArt('poster') or ''
                from wcs.utils import search_and_play_movie
                search_and_play_movie(title, year, plot, poster, '', '', '', '', '', self.addon)
                self.close()
            elif sel == 2:
                inspiration_text = f"Film '{title} ({year})' byl skvělý zážitek. Doporuč mi něco dalšího, co ve mě zanechá podobný dojem."
                self.handle_user_input(inspiration_text)
            elif sel == 3:
                from wcs.ui.UI import add_movie_item_to_list
                add_movie_item_to_list(self.addon, tmdb_id, title)
            elif sel == 4:
                from wcs.library import LibraryManager as library
                library.check_settings_and_add_item({'media_type': 'movie', 'movie_id': tmdb_id}, self.addon)
