import xbmc
import xbmcgui
import xbmcaddon
import xbmcplugin
import json
import threading
import requests
import os
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):
        """Vrací český label pro typ média."""
        if self.context:
            c_type = self.context.get('type')
            if c_type in ['my_mixed_collection', 'recently_played', 'favorite_actors', 'favorite_directors', 'favorite_writers']:
                return "filmů a seriálů"
            elif c_type in ['my_collections', 'collection']:
                return "filmových kolekcí"
        return "filmů" if self.media_type == "movie" else "seriálů"
    
    def _is_mixed_content(self):
        """Zjistí, jestli jde o mix filmů a seriálů."""
        if self.context:
            c_type = self.context.get('type')
            return c_type in ['my_mixed_collection', 'recently_played', 'favorite_actors', 'favorite_directors', 'favorite_writers']
        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']:
            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):
        """Vrací JSON formát podle typu média."""
        if self._is_mixed_content():
            return '''{
  "message": "Tvá otázka nebo komentář (max 2-3 věty)",
  "buttons": [
    {"label": "Text tlačítka", "value": "Odpověď která se odešle"}
  ],
  "recommendations": [
    {"title": "Český název", "year": "YYYY", "type": "movie nebo tv", "original_title": "Originální název"}
  ]
}'''
        else:
            return '''{
  "message": "Tvá otázka nebo komentář (max 2-3 věty)",
  "buttons": [
    {"label": "Text tlačítka", "value": "Odpověď která se odešle"}
  ],
  "recommendations": [
    {"title": "Český název", "year": "YYYY", "original_title": "Originální název"}
  ]
}'''
    
    def _get_json_type_rule(self):
        """Vrací pravidlo pro type pole (jen pro mix)."""
        if self._is_mixed_content():
            return "- Pole 'type' MUSÍ být buď 'movie' (pro filmy) nebo 'tv' (pro seriály). SPRÁVNĚ URČI TYP.\n"
        return ""
    
    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):
    def __init__(self, xml_filename, addon_path, *args, **kwargs):
        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
        # 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 = {}
        self._last_raw_response = None
        self._last_response_metadata = {}
        
    def onInit(self):
        # Pokud máme kontext, přeskočíme úvodní výběr
        if self.context:
            self.media_type = self.context.get('media_type', 'movie')
            
            # 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]
                # 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:
            input_control = self.getControl(3000)
            text = input_control.getText()
            if text:
                input_control.setText("") # Clear input
                self.handle_user_input(text)
                
        # Recommendations Panel
        elif controlId == 2000:
            self.handle_recommendation_click()
            
        # Preview Action Buttons
        elif controlId == 6100:
            self._handle_preview_action_click()

        # Debug Trigger (Hidden Chat Button)
        elif controlId == 1002:
            self._handle_debug_trigger()
    
    def onAction(self, action):
        """Handle navigation actions for preview updates."""
        action_id = action.getId()
        
        # 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:
                # Po malém zpoždění aktualizovat preview (dát čas na změnu výběru)
                import time
                time.sleep(0.05)
                self._update_preview_for_current_item()
        
        # 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
                     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
        
        # 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í - aktualizovat preview a skrýt akční tlačítka
            self.clearProperty('WCS.AIPreview.ShowActions')
            self.clearProperty('WCS.AIChat.Visible') # [NEW] Skrýt chat při návratu na seznam (Left Arrow)
            self._update_preview_for_current_item()
            
        elif controlId == 6100:
            # Uživatelské akce (Play, Info...) - nemazat nic
            pass
            
        elif controlId in [4000, 3000, 3001, 1001, 1002]:
            # Chat overlay - skrýt akční tlačítka, ALE nechat preview (pozadí)
            self.clearProperty('WCS.AIPreview.ShowActions')
            
        else:
            # Cokoliv jiného - vyčistit vše
            self.clearProperty('WCS.AIPreview.ShowActions')
            self._clear_preview_properties()
    
    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')
        
        def show_buttons_later(current_id):
            import time
            time.sleep(5.0)
            
            # Check 1: Still on same item?
            if self._last_preview_id != current_id:
                return

            # Check 2: Is Chat visible or focused? (Abort if yes)
            try:
                # If focus is NOT on the main list (2000), assume user moved away
                if self.getFocusId() != 2000:
                    return
                # Double check property
                if self.getProperty('WCS.AIChat.Visible'):
                    return
            except:
                pass

            # If safe, show and focus actions
            self.setProperty('WCS.AIPreview.ShowActions', 'true')
            self.setFocusId(6100)
        
        threading.Thread(target=show_buttons_later, args=(tmdb_id,), 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
    
    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', ''))
        
        # 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')
    
    def _populate_preview_actions(self, media_type, tmdb_id, title, year):
        """Naplní akční tlačítka podle typu média."""
        try:
            list_control = self.getControl(6100)
            list_control.reset()
            
            if media_type == 'movie':
                actions = [
                    {'label': 'Přehrát', 'action': 'play'},
                    {'label': 'Detaily', 'action': 'details'},
                    {'label': 'Moje filmy', 'action': 'add_list'},
                    {'label': 'Knihovna', 'action': 'add_library'},
                    {'label': 'Podobné', 'action': 'similar'},
                ]
            elif media_type in ['series', 'tv']:
                actions = [
                    {'label': 'Detaily', 'action': 'details'},
                    {'label': 'Sezóny', 'action': 'seasons'},
                    {'label': 'Moje seriály', 'action': 'add_list'},
                    {'label': 'Knihovna', 'action': 'add_library'},
                    {'label': 'Podobné', 'action': 'similar'},
                ]
            else:  # collection
                actions = [
                    {'label': 'Filmy', 'action': 'collection_movies'},
                    {'label': 'Moje filmy', 'action': 'add_list'},
                    {'label': 'Knihovna', 'action': 'add_library'},
                ]
            
            for act in actions:
                item = xbmcgui.ListItem(label=act['label'])
                item.setProperty('action', act['action'])
                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:
                    # 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':
                # Inspirovat se podobnými - více deskriptivní
                inspiration_text = f"Titul '{title} ({year})' mě zaujal. Chci zažít něco podobného. Nemusí to být stejný žánr, ale ať to má podobnou atmosféru nebo myšlenku."
                # 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í - systémová zpráva
            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()
        
        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']:
                 item_media_type = self.media_type
                 
            # Fetch TMDB
            tmdb_data = self._search_tmdb(title, year, item_media_type)
            
            if tmdb_data:
                # Create ListItem - pro seriály TMDb vrací 'name' místo '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 '')
                item.setProperty('year', tmdb_data.get('release_date', '')[:4] if item_media_type=='movie' else tmdb_data.get('first_air_date', '')[:4])
                item.setProperty('original_title', tmdb_data.get('original_title', tmdb_data.get('original_name','')))
                
                list_control.addItem(item)
                
        # After populating list, check results
        if list_control.size() > 0:
            # Úspěch - máme 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 = dialog.select(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 = dialog.select(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 = dialog.select(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)
