# wcs/ai/DialogMyTV.py
import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
import os
from urllib.parse import quote_plus
from wcs.ai.DialogAIChatRecommendation import AIChatRecommendationDialog, AIRecommendationPromptBuilder
from wcs.ai.MyTVChannelManager import MyTVChannelManager
from wcs.ai.DialogAddSeries import AddSeriesDialog, create_add_series_controller
from wcs import user_data, utils
from wcs.caching import get_cache_manager

# PIL import with fallback
try:
    from PIL import Image, ImageFilter, ImageDraw
    PIL_AVAILABLE = True
except ImportError:
    PIL_AVAILABLE = False
    xbmc.log("[MyTV] PIL not available - collage/effects disabled", xbmc.LOGWARNING)

# Recently played cache (lightweight, OK to keep in memory)
_recently_played_cache = None


class MyTVDialog(AIChatRecommendationDialog):
    # Background mode constants
    BG_STATIC = 0
    BG_FANART = 1
    BG_STILL = 2
    BG_COLLAGE_POSTERS = 3
    BG_COLLAGE_STILLS = 4
    BG_COLLAGE_MIX = 5
    
    def __init__(self, xml_filename, addon_path, *args, **kwargs):
        super(MyTVDialog, self).__init__(xml_filename, addon_path, *args, **kwargs)
        self._nav_section_id = 'my_tv'
        self.channel_manager = MyTVChannelManager(self.addon)
        self.current_channel_index = 0
        
        # Use centralized cache manager
        self._cache = get_cache_manager()
        
        # Run cache maintenance on startup (background, throttled)
        self._cache.maintenance()
        
        # Toast notification state
        self._toast_timer = None

    def _show_toast(self, message, toast_type='info', duration=3.0):
        """
        Zobrazí vlastní toast notifikaci v pravém horním rohu.
        
        Args:
            message: Text zprávy
            toast_type: 'info', 'success', 'warning', 'error'
            duration: Doba zobrazení v sekundách (default 3.0)
        """
        import threading
        
        # Cancel existing timer
        if self._toast_timer:
            self._toast_timer.cancel()
        
        # Set properties for XML
        self.setProperty('WCS.Toast.Message', message)
        self.setProperty('WCS.Toast.Type', toast_type)
        self.setProperty('WCS.Toast.Visible', 'true')
        
        # Auto-hide after duration
        self._toast_timer = threading.Timer(duration, self._hide_toast)
        self._toast_timer.daemon = True
        self._toast_timer.start()
    
    def _hide_toast(self):
        """Skryje toast notifikaci."""
        self.clearProperty('WCS.Toast.Visible')
        # Keep message/type for fade-out animation, clear after animation completes
        import threading
        def clear_toast_data():
            self.clearProperty('WCS.Toast.Message')
            self.clearProperty('WCS.Toast.Type')
        threading.Timer(0.3, clear_toast_data).start()


    def onInit(self):
        self._reset_state()
        self.setProperty('WCS.AIChat.Title', 'MyTV - televize budoucnosti')
        self.setProperty('WCS.AIChat.Status', 'Připraven')
        
        # Always start with the first channel
        self.current_channel_index = 0
        
        # CLEAR ALL BACKGROUND PROPERTIES - Start fresh with matte layer
        self.clearProperty('WCS.MyTV.Background.Custom')
        self.clearProperty('WCS.MyTV.Background.Ready')
        
        # Clear any leftover confirmation dialogs from previous session
        self.clearProperty('WCS.MyTV.DeleteSeriesConfirm')
        self.clearProperty('WCS.MyTV.DeleteConfirm')
        
        # RESET PROGRAM LIST IMMEDIATELY - Clear cached items that cause Fanart/Still flash
        try:
            program_list = self.getControl(9000)
            program_list.reset()
        except:
            pass
        
        # Set background mode and animation properties
        self._setup_background_properties()
        
        # Static mode: Only set Ready if no processing needed
        # If effects enabled, wait for PIL to generate blurred version
        if self._bg_mode == self.BG_STATIC and not self._is_background_processing_needed():
            self.setProperty('WCS.MyTV.Background.Ready', 'true')
        
        # Poster collage: Start background generation IMMEDIATELY (uses channel data, not program)
        if self._bg_mode == self.BG_COLLAGE_POSTERS:
            self._update_background()
        
        # Static mode with effects: Generate blurred version
        if self._bg_mode == self.BG_STATIC and self._is_background_processing_needed():
            self._update_background()
        
        # Load View
        self.refresh_channels_view(set_focus=True)
        
        # Trigger background generation for other modes (after program loads)
        if self._bg_mode not in (self.BG_STATIC, self.BG_COLLAGE_POSTERS):
            self._update_background()
        
        # Ensure sidebar is hidden initially
        if self._show_nav_on_init:
            self._show_nav_sidebar(animate=False)
        else:
            self.clearProperty('WCS.AIChat.Visible')

    def refresh_channels_view(self, set_focus=False):
        """Populate the bottom list with channels."""
        channels = self.channel_manager.get_all_channels()
        list_control = self.getControl(2000)
        list_control.reset()
        
        for ch in channels:
            item = xbmcgui.ListItem(label=ch.name)
            # Use channel icon or fallback
            icon = ch.icon if ch.icon else 'special://home/addons/plugin.video.milionar/resources/media/placeholder_tv_card.png'
            item.setArt({'poster': icon})
            item.setProperty('channel_id', ch.id)
            list_control.addItem(item)
            
        # Add "Create New Channel" item
        create_item = xbmcgui.ListItem(label="Vytvořit nový kanál")
        create_item.setArt({'poster': 'special://home/addons/plugin.video.milionar/resources/media/plus_card.png'}) # Fallback to placeholder if not exists
        create_item.setProperty('is_create_button', 'true')
        list_control.addItem(create_item)
            
        # Select first if available (and reset index if out of bounds)
        if self.current_channel_index >= list_control.size():
            self.current_channel_index = 0
            
        if list_control.size() > 0:
            list_control.selectItem(self.current_channel_index)
            self.update_editor_view()
            self.refresh_program_view()
            # Focus on Channel List after 0.5s (only if requested, e.g. on init)
            if set_focus:
                self._delayed_focus(2000, delay=0.5)

    def update_editor_view(self):
        """Update grid 3000 based on selected channel."""
        list_control = self.getControl(2000)
        idx = list_control.getSelectedPosition()
        if idx < 0:
            return
            
        item = list_control.getListItem(idx)
        
        # If "Create New" is selected, clear editor and return
        if item.getProperty('is_create_button') == 'true':
            self.getControl(9100).reset()
            # self.getControl(9000).reset() # Necháme program list být, ať to nebliká
            return
            
        channel_id = item.getProperty('channel_id')
        channel = self.channel_manager.get_channel_by_id(channel_id)
        
        grid_control = self.getControl(9100)
        grid_control.reset()
        
        if channel:
            # Add existing series
            for series in channel.series_list:
                s_item = xbmcgui.ListItem(label=series.get('name'))
                poster = series.get('poster_path') or series.get('poster')
                if poster and not poster.startswith('http'):
                     poster = f"https://image.tmdb.org/t/p/w500{poster}"
                elif not poster:
                     poster = 'special://home/addons/plugin.video.milionar/resources/media/placeholder_tv_card.png'
                
                s_item.setArt({'poster': poster})
                s_item.setProperty('series_id', str(series.get('id')))
                s_item.setProperty('channel_id', channel_id)
                grid_control.addItem(s_item)

            # Always add "Add Series" card at the end of the grid
            add_item = xbmcgui.ListItem(label="Přidat seriál")
            add_item.setArt({'poster': 'special://home/addons/plugin.video.milionar/resources/media/plus_card.png'})
            add_item.setProperty('is_add_button', 'true')
            add_item.setProperty('channel_id', channel_id)
            grid_control.addItem(add_item)

    def refresh_program_view(self):
        """Debounced trigger - schedules async load after 300ms delay."""
        import threading
        
        # Initialize state if needed
        if not hasattr(self, '_program_load_token'):
            self._program_load_token = 0
        if not hasattr(self, '_program_load_timer'):
            self._program_load_timer = None
        
        # Increment token (cancels any pending/running loads)
        self._program_load_token += 1
        current_token = self._program_load_token
        
        # Get channel ID now (sync, fast)
        list_control = self.getControl(2000)
        idx = list_control.getSelectedPosition()
        if idx < 0:
            return
        
        item = list_control.getListItem(idx)
        channel_id = item.getProperty('channel_id')
        if not channel_id:
            return
        
        # Store for stale play prevention
        self._current_loaded_channel_id = channel_id
        
        # INSTANT LOAD: Try to load from program list cache immediately
        self._instant_load_from_cache(channel_id)
        
        # Cancel existing timer
        if self._program_load_timer:
            self._program_load_timer.cancel()
        
        # Schedule debounced async refresh (300ms delay)
        self._program_load_timer = threading.Timer(
            0.3,
            self._async_program_load,
            args=(current_token, channel_id)
        )
        self._program_load_timer.daemon = True
        self._program_load_timer.start()
    
    def _instant_load_from_cache(self, channel_id):
        """Instantly populate program list from cache (no API calls)."""
        cache_key = f"program_list_{channel_id}"
        
        cached_items = self._cache.get(cache_key)
        if not cached_items:
            return  # No cache, will load async
        
        try:
            program_list = self.getControl(9000)
            program_list.reset()
            
            for item_data in cached_items:
                item = xbmcgui.ListItem(label=item_data.get('label', ''))
                item.setLabel2(item_data.get('label2', ''))
                
                # Set all properties
                for key, value in item_data.get('properties', {}).items():
                    item.setProperty(key, str(value))
                
                # Set all art
                item.setArt(item_data.get('art', {}))
                
                program_list.addItem(item)
            
            xbmc.log(f"[MyTV] Instantly loaded {len(cached_items)} items from cache for channel {channel_id}", xbmc.LOGINFO)
            
            # For Fanart/Still: Only set Ready if no processing needed
            # If effects enabled, wait for PIL to generate processed version
            if self._bg_mode in (self.BG_FANART, self.BG_STILL) and not self._is_background_processing_needed():
                self.setProperty('WCS.MyTV.Background.Ready', 'true')
            # Static already has Ready set in onInit (if no processing)
            # Collages will set Ready after background generation
        except Exception as e:
            xbmc.log(f"[MyTV] Error loading from cache: {e}", xbmc.LOGWARNING)
    
    def _async_program_load(self, token, channel_id):
        """Background thread - loads program data without blocking UI."""
        import random
        from wcs.metadata import TMDbClient
        
        # Check if still valid
        if token != self._program_load_token:
            return  # Cancelled - user navigated away
        
        channel = self.channel_manager.get_channel_by_id(channel_id)
        if not channel or not channel.series_list:
            # Update UI with empty message
            self._apply_empty_program()
            return
        
        # Settings
        try:
            upcoming_count_val = self.addon.getSetting('mytv_upcoming_count')
            upcoming_count = int(upcoming_count_val) if upcoming_count_val else 12
        except:
            upcoming_count = 12
        
        block_size_setting = self.addon.getSetting('mytv_block_size') or "2"
        
        # Parse block size
        min_block, max_block = 1, 1
        if '-' in block_size_setting:
            try:
                parts = block_size_setting.split('-')
                min_block, max_block = int(parts[0]), int(parts[1])
            except:
                pass
        else:
            try:
                val = int(block_size_setting)
                min_block = max_block = val
            except:
                pass
        
        # Load recently played cache once (module-level)
        global _recently_played_cache
        if _recently_played_cache is None:
            _recently_played_cache = user_data.load_recently_played(self.addon)
        
        series_candidates = list(channel.series_list)
        random.shuffle(series_candidates)
        
        # Local progress simulation
        simulated_progress = {}
        items_count = 0
        candidate_idx = 0
        max_loops = upcoming_count * 2
        loops = 0
        
        # Reset list at start
        try:
            program_list = self.getControl(9000)
            program_list.reset()
        except:
            return
        
        while items_count < upcoming_count and series_candidates and loops < max_loops:
            loops += 1
            
            # Token check - exit early if cancelled
            if token != self._program_load_token:
                return
            
            series = series_candidates[candidate_idx % len(series_candidates)]
            candidate_idx += 1
            
            series_id = str(series.get('id'))
            series_name = series.get('name')
            
            # Initialize progress
            if series_id not in simulated_progress:
                last_season, last_episode = 1, 0
                for rp in _recently_played_cache or []:
                    if str(rp.get('tmdb_id', '')) == series_id and rp.get('media_type') in ['tv', 'episode', 'series']:
                        if 'season' in rp and 'episode' in rp:
                            last_season = int(rp['season'])
                            last_episode = int(rp['episode'])
                        break
                simulated_progress[series_id] = (last_season, last_episode)
            
            block_size = random.randint(min_block, max_block)
            
            for _ in range(block_size):
                if items_count >= upcoming_count:
                    break
                
                # Token check
                if token != self._program_load_token:
                    return
                
                curr_s, curr_e = simulated_progress[series_id]
                
                # Check cache first (using CacheManager)
                cache_key = f"episode_{series_id}_{curr_s}_{curr_e + 1}"
                next_meta = self._cache.get(cache_key)
                if not next_meta:
                    # Fetch from TMDb (slow)
                    next_meta = TMDbClient.get_next_episode_from_tmdb(series_id, curr_s, curr_e, self.addon)
                    if next_meta:
                        # Cache with 7-day TTL
                        self._cache.set(cache_key, next_meta, ttl_seconds=7 * 24 * 3600)
                
                if next_meta:
                    # Token check before UI update
                    if token != self._program_load_token:
                        return
                    
                    # Add item to UI immediately (progressive loading)
                    self._add_single_program_item(next_meta, series_name, series_id)
                    items_count += 1
                    simulated_progress[series_id] = (next_meta['season'], next_meta['episode'])
                    
                    # For Fanart/Still modes: Only set Ready after FIRST item if no processing needed
                    # If effects enabled, wait for PIL to generate processed version
                    if items_count == 1 and self._bg_mode in (self.BG_FANART, self.BG_STILL) and not self._is_background_processing_needed():
                        self.setProperty('WCS.MyTV.Background.Ready', 'true')
                else:
                    break
        
        # After loading program, update background for collage modes
        if items_count > 0:
            # For Still/Mix collage: generate background now (needs full program data)
            if self._bg_mode in (self.BG_COLLAGE_STILLS, self.BG_COLLAGE_MIX):
                self._update_background(channel_id)
                # Ready will be set by _generate_background_async after completion
            else:
                # For Fanart/Still with effects: trigger background update
                self._update_background(channel_id)
        
        # Save program list cache for instant reload
        self._save_program_list_cache(channel_id)
        
        # Flush cache to disk after load completes
        self._cache.flush()
    
    def _save_program_list_cache(self, channel_id):
        """Save current program list to cache for instant reload."""
        try:
            program_list = self.getControl(9000)
            items_data = []
            
            for i in range(program_list.size()):
                item = program_list.getListItem(i)
                item_data = {
                    'label': item.getLabel(),
                    'label2': item.getLabel2(),
                    'properties': {
                        'tmdb_id': item.getProperty('tmdb_id'),
                        'series_name': item.getProperty('series_name'),
                        'season': item.getProperty('season'),
                        'episode': item.getProperty('episode'),
                        'ep_code': item.getProperty('ep_code'),
                        'ep_title': item.getProperty('ep_title'),
                        'plot': item.getProperty('plot'),
                        'plot_short': item.getProperty('plot_short'),
                        'runtime': item.getProperty('runtime'),
                        'air_date': item.getProperty('air_date')
                    },
                    'art': {
                        'thumb': item.getArt('thumb'),
                        'fanart': item.getArt('fanart'),
                        'clearlogo': item.getArt('clearlogo'),
                        'poster': item.getArt('poster')
                    }
                }
                items_data.append(item_data)
            
            cache_key = f"program_list_{channel_id}"
            # Save to CacheManager with 1-day TTL (program lists change frequently)
            self._cache.set(cache_key, items_data, ttl_seconds=24 * 3600)
            xbmc.log(f"[MyTV] Saved {len(items_data)} items to program list cache for channel {channel_id}", xbmc.LOGINFO)
        except Exception as e:
            xbmc.log(f"[MyTV] Error saving program list cache: {e}", xbmc.LOGWARNING)
    
    def _add_single_program_item(self, next_meta, series_name, series_id):
        """Add a single program item to the list (progressive loading)."""
        try:
            program_list = self.getControl(9000)
            
            ep_code = f"S{next_meta['season']:02d}E{next_meta['episode']:02d}"
            full_plot = next_meta.get('plot', '')
            plot_short = full_plot[:77] + '...' if len(full_plot) > 80 else full_plot
            
            item = xbmcgui.ListItem(label=series_name)
            item.setLabel2(str(next_meta.get('runtime', '')))
            
            # Set properties
            item.setProperty('tmdb_id', series_id)
            item.setProperty('series_name', series_name)
            item.setProperty('season', str(next_meta['season']))
            item.setProperty('episode', str(next_meta['episode']))
            item.setProperty('episode_title', next_meta.get('episode_title', ''))
            item.setProperty('plot', full_plot)
            item.setProperty('plot_short', plot_short)
            item.setProperty('rating', str(next_meta.get('rating', '')))
            item.setProperty('ep_code', ep_code)
            item.setProperty('series_logo', next_meta.get('series_logo', ''))
            item.setProperty('poster', next_meta.get('poster', ''))
            item.setProperty('fanart', next_meta.get('fanart', ''))
            item.setProperty('year', str(next_meta.get('year', '')))
            item.setProperty('genre', next_meta.get('genre', ''))
            
            # Art
            thumb = next_meta.get('episode_thumb') or next_meta.get('poster') or ''
            item.setArt({
                'thumb': thumb,
                'icon': next_meta.get('poster', ''),
                'poster': next_meta.get('poster', ''),
                'fanart': next_meta.get('fanart', '')
            })
            
            # InfoTag
            info_tag = {
                'title': next_meta.get('episode_title', ''),
                'tvshowtitle': series_name,
                'plot': full_plot,
                'season': next_meta['season'],
                'episode': next_meta['episode'],
                'mediatype': 'episode'
            }
            try:
                dur_min = int(str(next_meta.get('runtime', '0')).replace(' min', '').strip() or '0')
                info_tag['duration'] = dur_min * 60
            except:
                pass
            
            utils.set_video_info_tag(item, info_tag)
            program_list.addItem(item)
        except Exception as e:
            xbmc.log(f"[MyTV] Error adding program item: {e}", xbmc.LOGERROR)
    
    def _apply_empty_program(self):
        """Apply empty state to program list."""
        try:
            program_list = self.getControl(9000)
            program_list.reset()
            item = xbmcgui.ListItem(label="Žádný program. Přidejte seriály.")
            program_list.addItem(item)
        except:
            pass
    
    def _apply_program_items(self, items_data):
        """Apply loaded program items to UI. Called from background thread."""
        try:
            program_list = self.getControl(9000)
            program_list.reset()
            
            for data in items_data:
                item = xbmcgui.ListItem(label=data['label'])
                item.setLabel2(data['label2'])
                
                # Set all properties
                for key in ['tmdb_id', 'series_name', 'season', 'episode', 'episode_title',
                            'plot', 'plot_short', 'rating', 'ep_code', 'series_logo',
                            'poster', 'fanart', 'year', 'genre']:
                    item.setProperty(key, data.get(key, ''))
                
                # Art
                item.setArt({
                    'thumb': data.get('thumb', ''),
                    'icon': data.get('poster', ''),
                    'poster': data.get('poster', ''),
                    'fanart': data.get('fanart', '')
                })
                
                # InfoTag
                info_tag = {
                    'title': data.get('episode_title', ''),
                    'tvshowtitle': data.get('series_name', ''),
                    'plot': data.get('plot', ''),
                    'season': int(data.get('season', 1)),
                    'episode': int(data.get('episode', 1)),
                    'mediatype': 'episode'
                }
                try:
                    runtime = data.get('runtime', 0)
                    if isinstance(runtime, str):
                        runtime = int(runtime.replace(' min', '').strip() or '0')
                    info_tag['duration'] = runtime * 60
                except:
                    pass
                
                utils.set_video_info_tag(item, info_tag)
                program_list.addItem(item)
        except Exception as e:
            xbmc.log(f"[MyTV] Error applying program items: {e}", xbmc.LOGERROR)

    def onAction(self, action):
        # Delegate to Series Detail dialog first if active
        if self._handle_series_detail_action(action):
            return
        
        # Delegate to Add Series controller if active
        if self._handle_add_series_action(action):
            return
        
        action_id = action.getId()
        focused_id = self.getFocusId()
        
        # Handle delete confirmation cancel with Back action
        if action_id in (xbmcgui.ACTION_PREVIOUS_MENU, xbmcgui.ACTION_NAV_BACK):
            if self.getProperty('WCS.MyTV.DeleteSeriesConfirm') == 'true':
                self._cancel_delete_series_confirmation()
                return
            if self.getProperty('WCS.MyTV.DeleteConfirm') == 'true':
                self._cancel_delete_confirmation()
                return
        
        # Get last known focus (before this action)
        last_focus = getattr(self, '_last_known_focus', None)
        
        # Handle Channel List (2000) actions locally - DO NOT pass to base class!
        # Base class has its own sidebar logic for 2000 that conflicts with MyTV
        if focused_id == 2000:
            # Get current position for tracking
            try:
                list_control = self.getControl(2000)
                current_pos = list_control.getSelectedPosition()
            except:
                current_pos = 0
            
            if action_id in [1, 2]:  # LEFT/RIGHT
                # Check if LEFT on first position - should open nav sidebar
                # Use _last_list_position (like base class) to check PREVIOUS position
                last_pos = getattr(self, '_last_list_position', 0)
                if action_id == 1 and last_pos == 0:  # LEFT at position 0
                    # Open nav sidebar
                    self._show_nav_sidebar()
                    return
                
                # Update position tracking BEFORE processing
                self._last_list_position = current_pos
                
                # Allow UI to update selection then refresh
                xbmc.sleep(50)
                self.update_editor_view()
                self.refresh_program_view()
                # Update focus tracking
                self._last_known_focus = 2000
                # Let XML handle focus movement
                xbmcgui.WindowXMLDialog.onAction(self, action)
                return
            elif action_id == 3:  # UP - Let XML handle it
                self._last_known_focus = 2000
                xbmcgui.WindowXMLDialog.onAction(self, action)
                return
            elif action_id == 4:  # DOWN - Open AI Chat Sidebar
                # Only open sidebar if we were ALREADY on 2000 before this action
                # (This prevents opening when navigating from grid TO channel list)
                if last_focus != 2000:
                    # We just arrived at 2000 via this DOWN action
                    # Update tracking and don't open sidebar
                    self._last_known_focus = 2000
                    return
                # Don't open if blocked (during internal operations like add series)
                if getattr(self, '_block_sidebar', False):
                    return
                # Only open if sidebar is not already visible
                if not self.getProperty('WCS.AIChat.Visible'):
                    self.setProperty('WCS.AIChat.Visible', 'true')
                    self._populate_mytv_chat_buttons()
                    self.setFocusId(4000)
                return
            else:
                # Other actions on 2000 - handle with base Window, not our parent
                self._last_known_focus = 2000
                xbmcgui.WindowXMLDialog.onAction(self, action)
                return
        
        # Update last known focus for non-2000 controls
        self._last_known_focus = focused_id
        
        super(MyTVDialog, self).onAction(action)
    
    def onFocus(self, controlId):
        # Delegate to Add Series controller first if active
        self._handle_add_series_focus(controlId)
        
        # Close chat sidebar when focusing channel list
        if controlId == 2000:
            self.clearProperty('WCS.AIChat.Visible')
        else:
            # For all other controls (including 4001 for sidebar close), call base class
            super(MyTVDialog, self).onFocus(controlId)

    def onClick(self, controlId):
        # Delegate to Series Detail dialog first if active
        if self._handle_series_detail_click(controlId):
            return
        
        # Delegate to Add Series controller if active
        if self._handle_add_series_click(controlId):
            return
        
        if controlId == 2000:
            # Check if "Create New"
            list_control = self.getControl(2000)
            item = list_control.getSelectedItem()
            if item.getProperty('is_create_button') == 'true':
                self._create_new_channel()
                return  # Don't call super().onClick - it may reset focus
            else:
                # Move focus to "Spustit vysilani" button with delay
                self._delayed_focus(9004, delay=0.1)  # Spustit vysílání
        elif controlId == 9100:
            # Context Menu for Grid Item (Remove)
            self._handle_grid_click()
        elif controlId == 9001:
            # Add from My Series (legacy - button removed)
            self._add_from_my_series()
        elif controlId == 9002:
            # Add New (Search) (legacy - button removed)
            self._add_new_search()
        elif controlId == 9003:
            # Delete Channel - show inline confirmation
            self._show_delete_confirmation()
        elif controlId == 9006:
            # Confirm Delete Channel
            self._confirm_delete_channel()
        elif controlId == 9004:
            # Play Broadcast (First item in upcoming)
            self._play_broadcast()
        elif controlId == 9005:
            # Refresh/Shuffle
            self.refresh_program_view()
            self._show_toast("Program aktualizován", "success")
        elif controlId == 9000:
            # Play Program Item
            self._play_program_item()
        
        super(MyTVDialog, self).onClick(controlId)

    def _create_new_channel(self):
        """Create a new channel with automatic naming based on added series."""
        # Block sidebar during this operation
        self._block_sidebar = True
        
        # Create channel with temporary name
        temp_name = f"Nový kanál {len(self.channel_manager.channels) + 1}"
        new_channel = self.channel_manager.create_channel(temp_name)
        
        # Store the new channel ID for auto-naming after dialog closes
        self._pending_new_channel_id = new_channel.id
        
        # Select the new channel (first one - new channels are prepended)
        self.current_channel_index = 0
        
        # Only refresh channel list - NO program refresh needed for empty channel
        list_control = self.getControl(2000)
        list_control.reset()
        for ch in self.channel_manager.get_all_channels():
            item = xbmcgui.ListItem(label=ch.name)
            icon = ch.icon if ch.icon else 'special://home/addons/plugin.video.milionar/resources/media/placeholder_tv_card.png'
            item.setArt({'poster': icon})
            item.setProperty('channel_id', ch.id)
            list_control.addItem(item)
        # Add "Create New Channel" item
        create_item = xbmcgui.ListItem(label="Vytvořit nový kanál")
        create_item.setArt({'poster': 'special://home/addons/plugin.video.milionar/resources/media/plus_card.png'})
        create_item.setProperty('is_create_button', 'true')
        list_control.addItem(create_item)
        # NOTE: Don't call selectItem() here - it can cause Kodi to "remember" 
        # focus on this control and restore it later, breaking AddSeries focus
        
        # Clear editor grid (empty channel)
        self.getControl(9100).reset()
        
        # Try to move focus away from 2000 (helps with focus transfer)
        # Note: 9100 is empty so this may fail, but that's OK
        self.setFocusId(9100)
        xbmc.sleep(50)
        
        # Unblock sidebar before showing dialog
        self._block_sidebar = False
        
        # Open AddSeriesDialog
        self._show_add_series_dialog()


    def _show_delete_confirmation(self):
        """Show inline delete confirmation dialog."""
        list_control = self.getControl(2000)
        item = list_control.getSelectedItem()
        if not item or item.getProperty('is_create_button') == 'true': 
            return
        
        # Store channel info for deletion
        self._pending_delete_channel_id = item.getProperty('channel_id')
        self._pending_delete_channel_name = item.getLabel()
        
        # Show confirmation dialog
        self.setProperty('WCS.MyTV.DeleteConfirm', 'true')
        self.setFocusId(9006)
    
    def _cancel_delete_confirmation(self):
        """Cancel delete confirmation and return to normal state."""
        self.clearProperty('WCS.MyTV.DeleteConfirm')
        self._pending_delete_channel_id = None
        self._pending_delete_channel_name = None
        self.setFocusId(9003)
    
    def _confirm_delete_channel(self):
        """Actually delete the channel after confirmation."""
        channel_id = getattr(self, '_pending_delete_channel_id', None)
        if not channel_id:
            return
        
        # Hide confirmation dialog
        self.clearProperty('WCS.MyTV.DeleteConfirm')
        
        # Delete channel
        self.channel_manager.delete_channel(channel_id)
        
        # Clear pending data
        self._pending_delete_channel_id = None
        self._pending_delete_channel_name = None
        
        # Refresh
        self.current_channel_index = 0
        self.refresh_channels_view()
        
        # Focus on first channel
        self._delayed_focus(2000, delay=0.1)
        
        self._show_toast("Kanál smazán", "success")

    def _show_delete_series_confirmation(self):
        """Show inline delete series confirmation dialog."""
        self.setProperty('WCS.MyTV.DeleteSeriesConfirm', 'true')
        self.setFocusId(9305)
    
    def _cancel_delete_series_confirmation(self):
        """Cancel delete series confirmation and return to normal state."""
        self.clearProperty('WCS.MyTV.DeleteSeriesConfirm')
        self.setFocusId(9300)
    
    def _confirm_delete_series(self):
        """Actually delete the series after confirmation."""
        series_id = self._series_detail_series_id
        channel_id = self._series_detail_channel_id
        
        # Hide confirmation dialog
        self.clearProperty('WCS.MyTV.DeleteSeriesConfirm')
        
        # Get series name for notification
        channel = self.channel_manager.get_channel_by_id(channel_id)
        series_name = ''
        if channel:
            for s in channel.series_list:
                if str(s.get('id')) == str(series_id):
                    series_name = s.get('name', '')
                    break
        
        # Remove series from channel
        self.channel_manager.toggle_series_in_channel(channel_id, {'id': series_id})
        
        # Close dialog and refresh
        self._close_series_detail_dialog(removed=True)
        
        self._show_toast(f"'{series_name}' odebráno", "success")

    def _generate_ai_channel_name_async(self, channel_id, series_list, generator):
        """Generate AI channel name in background thread."""
        import threading
        
        def worker():
            try:
                # Call AI to generate name
                ai_name = generator.generate_from_ai(series_list)
                
                # Apply the name change in channel manager
                self.channel_manager.rename_channel(channel_id, ai_name)
                
                # Update the ListItem label directly in channel list
                try:
                    list_control = self.getControl(2000)
                    for i in range(list_control.size()):
                        item = list_control.getListItem(i)
                        if item.getProperty('channel_id') == channel_id:
                            item.setLabel(ai_name)
                            break
                except Exception as e:
                    xbmc.log(f"[MyTV] Failed to update list item label: {e}", xbmc.LOGWARNING)
                
                # Show notification
                self._show_toast(f"Název: {ai_name}", "success")
                
            except Exception as e:
                xbmc.log(f"[MyTV] AI channel naming failed: {e}", xbmc.LOGERROR)
                self._show_toast("AI generování selhalo", "error")
        
        thread = threading.Thread(target=worker, daemon=True)
        thread.start()

    def _play_broadcast(self):
        """Plays all items in the upcoming program list continuously."""
        list_control = self.getControl(9000)
        size = list_control.size()
        if size == 0:
            self._show_toast("Žádný program k vysílání", "warning")
            return
            
        self.close()
        
        # Get Playlist
        playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
        playlist.clear()
        
        # Add all items to playlist
        for i in range(size):
            item = list_control.getListItem(i)
            
            series_name = item.getProperty('series_name')
            season = item.getProperty('season')
            episode = item.getProperty('episode')
            tmdb_id = item.getProperty('tmdb_id')
            
            if not series_name or not season or not episode:
                continue

            # Metadata params
            episode_title = item.getProperty('episode_title')
            plot = item.getProperty('plot')
            rating = item.getProperty('rating')
            poster = item.getProperty('poster')
            fanart = item.getProperty('fanart')
            genre = item.getProperty('genre')
            year = item.getProperty('year')
            
            # Construct plugin URL for PLAYLIST action
            url = (f"plugin://{self.addon.getAddonInfo('id')}?action=play_episode_from_addon_playlist"
                   f"&series_name={quote_plus(series_name)}"
                   f"&season_number={season}"
                   f"&episode_number={episode}"
                   f"&tmdb_id={tmdb_id}"
                   f"&episode_title={quote_plus(episode_title)}"
                   f"&plot={quote_plus(plot)}"
                   f"&rating={quote_plus(rating)}"
                   f"&poster={quote_plus(poster)}"
                   f"&fanart={quote_plus(fanart)}"
                   f"&genre={quote_plus(genre)}"
                   f"&year={quote_plus(year)}")
            
            # Create list item for playlist
            li = xbmcgui.ListItem(label=item.getLabel())
            li.setInfo('video', {
                'title': item.getLabel(),
                'plot': plot, 
                'year': int(year) if year.isdigit() else 0,
                'premiered': year,
                'mediatype': 'episode',
                'season': int(season),
                'episode': int(episode),
                'tvshowtitle': series_name
            })
            li.setArt({
                'poster': poster,
                'fanart': fanart,
                'thumb': poster
            })
            
            playlist.add(url, li)
            
        # Start playback
        xbmc.Player().play(playlist)

    def _play_program_item(self):
        list_control = self.getControl(9000)
        item = list_control.getSelectedItem()
        if not item: return
        
        series_name = item.getProperty('series_name')
        season = item.getProperty('season')
        episode = item.getProperty('episode')
        tmdb_id = item.getProperty('tmdb_id')
        
        # Metadata pro utils.play_episode_from_addon
        episode_title = item.getProperty('episode_title')
        plot = item.getProperty('plot')
        rating = item.getProperty('rating')
        poster = item.getProperty('poster')
        fanart = item.getProperty('fanart')
        genre = item.getProperty('genre')
        year = item.getProperty('year')
        
        if not series_name or not season or not episode:
             return

        self.close()
        
        # Trigger play action via plugin URL - using standard utils function
        url = (f"plugin://{self.addon.getAddonInfo('id')}?action=play_episode_from_addon"
               f"&series_name={quote_plus(series_name)}"
               f"&season_number={season}"
               f"&episode_number={episode}"
               f"&tmdb_id={tmdb_id}"
               f"&episode_title={quote_plus(episode_title)}"
               f"&plot={quote_plus(plot)}"
               f"&rating={quote_plus(rating)}"
               f"&poster={quote_plus(poster)}"
               f"&fanart={quote_plus(fanart)}"
               f"&genre={quote_plus(genre)}"
               f"&year={quote_plus(year)}")
        
        xbmc.executebuiltin(f"PlayMedia({url})")

    def _play_current_channel(self):
        """Spustí přehrávání aktuálního kanálu (náhodná epizoda Next Up)."""
        list_control = self.getControl(2000)
        item = list_control.getSelectedItem()
        if not item: return
        
        channel_id = item.getProperty('channel_id')
        
        # Najít další epizodu pro přehrání
        next_episode = self.channel_manager.get_random_episode_for_channel(channel_id)
        
        if not next_episode:
            self._show_toast("Kanál nemá žádné dostupné epizody", "warning")
            return
            
        self.close()
        
        # Přehrát epizodu pomocí standardní akce
        series_name = next_episode.get('series_name') or next_episode.get('series_title')
        season = next_episode.get('season_number')
        episode = next_episode.get('episode_number')
        
        url = (f"plugin://{self.addon.getAddonInfo('id')}?action=play_episode_from_addon"
               f"&series_name={quote_plus(series_name)}"
               f"&season_number={season}"
               f"&episode_number={episode}"
               f"&tmdb_id={next_episode.get('tmdb_id', '')}"
               f"&episode_title={quote_plus(next_episode.get('episode_title', ''))}"
               f"&plot={quote_plus(next_episode.get('episode_plot', ''))}"
               f"&rating={quote_plus(str(next_episode.get('episode_rating', '')))}"
               f"&poster={quote_plus(next_episode.get('series_poster', ''))}"
               f"&fanart={quote_plus(next_episode.get('series_fanart', ''))}"
               f"&year={quote_plus(str(next_episode.get('episode_year', '')))}")
        
        xbmc.executebuiltin(f"PlayMedia({url})")

    def _populate_mytv_chat_buttons(self):
        """Populate chat sidebar buttons with MyTV-specific options."""
        buttons = [
            {"label": "Vytvořit Comedy Channel", "value": "create_channel:comedy"},
            {"label": "Vytvořit Drama Channel", "value": "create_channel:drama"},
            {"label": "Vytvořit Scifi Channel", "value": "create_channel:scifi"},
        ]
        self.update_buttons(buttons)

    def _handle_grid_click(self):
        # Block sidebar during this operation
        self._block_sidebar = True
        try:
            grid = self.getControl(9100)
            item = grid.getSelectedItem()
            if not item: return
            
            # Check if it's the "Add Series" placeholder
            if item.getProperty('is_add_button') == 'true':
                # Show custom Add Series dialog overlay
                self._show_add_series_dialog()
                return

            # Show Series Detail dialog instead of context menu
            series_id = item.getProperty('series_id')
            channel_id = item.getProperty('channel_id')
            self._show_series_detail_dialog(series_id, channel_id)
        finally:
            self._block_sidebar = False

    def _add_from_my_series(self):
        # Block sidebar from opening during this operation
        self._block_sidebar = True
        try:
            # Get My Series
            my_series = user_data.load_tmdb_series(self.addon)
            if not my_series:
                 self._show_toast("Nenalezeny žádné uložené seriály", "warning")
                 return
                 
            # Filter out already added ones?
            list_control = self.getControl(2000)
            channel_id = list_control.getSelectedItem().getProperty('channel_id')
            channel = self.channel_manager.get_channel_by_id(channel_id)
            current_ids = [str(s.get('id')) for s in channel.series_list]
            
            candidates = [s for s in my_series if str(s.get('id')) not in current_ids]
            
            if not candidates:
                 self._show_toast("Všechny vaše seriály už jsou v kanálu", "info")
                 return
                 
            # Multi select
            labels = [f"{s.get('name')} ({s.get('year', '')})" for s in candidates]
            selected = xbmcgui.Dialog().multiselect("Přidat do kanálu", labels)
            
            if selected:
                for idx in selected:
                    self.channel_manager.toggle_series_in_channel(channel_id, candidates[idx])
                # Regenerate channel composite icon
                self.channel_manager.generate_channel_composite(channel_id)
                # Refresh editor view
                self.update_editor_view()
                self.refresh_program_view()
                self.refresh_channels_view(set_focus=False)  # Update channel icons
                self.setFocusId(9100)  # Changed from 3000 to correct ID
                self._show_toast("Seriály přidány", "success")
        finally:
            # Unblock sidebar
            self._block_sidebar = False

    def _add_new_search(self):
        # Block sidebar during this operation
        self._block_sidebar = True
        try:
            import requests
            from wcs.metadata.TMDbClient import get_tmdb_api_key
            
            search_query = xbmcgui.Dialog().input("Hledat seriál", type=xbmcgui.INPUT_ALPHANUM)
            if not search_query: return
            
            try:
                url = f'https://api.themoviedb.org/3/search/tv?api_key={get_tmdb_api_key()}&language=cs-CZ&query={quote_plus(search_query)}'
                response = requests.get(url, timeout=10)
                results = response.json().get('results', [])
            except Exception:
                return
                
            if not results:
                 self._show_toast("Nic nenalezeno", "warning")
                 return
                 
            choices = [f"{item['name']} ({item.get('first_air_date', '')[:4]})" for item in results]
            selected_idx = xbmcgui.Dialog().select("Vyberte seriál", choices)
            
            if selected_idx >= 0:
                selected_item = results[selected_idx]
                list_control = self.getControl(2000)
                channel_id = list_control.getSelectedItem().getProperty('channel_id')
                
                self.channel_manager.toggle_series_in_channel(channel_id, selected_item)
                # Full refresh
                self.update_editor_view()
                self.refresh_program_view()
                self.setFocusId(9100)
                self._show_toast(f"'{selected_item['name']}' přidáno", "success")
        finally:
            self._block_sidebar = False

    # ==================================================================================
    # ADD SERIES DIALOG METHODS
    # ==================================================================================
    
    def _show_add_series_dialog(self):
        """Show the inline Add Series dialog."""
        # Get current channel ID
        list_control = self.getControl(2000)
        channel_id = list_control.getSelectedItem().getProperty('channel_id')
        
        # Create or reuse controller
        self._add_series_controller = create_add_series_controller(
            self, self.addon, self.channel_manager, channel_id
        )
        
        # Show the inline dialog
        self._add_series_controller.show()
    
    def _close_add_series_dialog(self):
        """Close the inline Add Series dialog and refresh if needed."""
        if hasattr(self, '_add_series_controller') and self._add_series_controller:
            series_added = self._add_series_controller.was_series_added()
            channel_id = self._add_series_controller.channel_id
            self._add_series_controller.hide()
            self._add_series_controller = None
            
            # Check if this was a newly created channel that needs auto-naming
            pending_channel_id = getattr(self, '_pending_new_channel_id', None)
            xbmc.log(f"[MyTV] Checking pending_channel_id: {pending_channel_id}, channel_id: {channel_id}", xbmc.LOGWARNING)
            
            if pending_channel_id and pending_channel_id == channel_id:
                # Clear the pending flag
                self._pending_new_channel_id = None
                
                channel = self.channel_manager.get_channel_by_id(channel_id)
                xbmc.log(f"[MyTV] Channel found: {channel is not None}, series_list: {len(channel.series_list) if channel and channel.series_list else 0}", xbmc.LOGWARNING)
                
                if not channel or not channel.series_list:
                    # No series added - delete the empty channel
                    self.channel_manager.delete_channel(channel_id)
                    self._show_toast("Kanál nebyl vytvořen - nepřidali jste žádné seriály", "warning")
                else:
                    # Generate channel name based on genres
                    from wcs.ai.ChannelNameGenerator import ChannelNameGenerator
                    generator = ChannelNameGenerator(self.addon)
                    
                    # Check naming mode from settings (text value from select)
                    naming_mode = self.addon.getSetting('mytv_channel_naming') or ''
                    
                    # Check if AI mode is selected (contains "AI" in the value)
                    if 'AI' in naming_mode or naming_mode == '1':
                        # AI mode - use temporary name and generate asynchronously
                        temp_name = generator.generate_from_genres(channel.series_list)
                        self.channel_manager.rename_channel(channel_id, temp_name)
                        self.channel_manager.generate_channel_composite(channel_id)
                        
                        # Start async AI name generation (notification will come when done)
                        self._generate_ai_channel_name_async(channel_id, channel.series_list, generator)
                    else:
                        # Sync genre-based naming
                        new_name = generator.generate_from_genres(channel.series_list)
                        self.channel_manager.rename_channel(channel_id, new_name)
                        self.channel_manager.generate_channel_composite(channel_id)
                        self._show_toast(f"Kanál '{new_name}' vytvořen", "success")
                
                # Full refresh
                self.current_channel_index = 0
                self.refresh_channels_view(set_focus=False)
                self.update_editor_view()
                self.refresh_program_view()
                self._delayed_focus(2000, delay=0.2)
                return
            
            # Normal flow - just refresh if series were added
            if series_added:
                # Regenerate channel composite icon
                self.channel_manager.generate_channel_composite(channel_id)
                self.update_editor_view()
                self.refresh_program_view()
                self.refresh_channels_view(set_focus=False)  # Update channel icons
            
            # Return focus to the grid
            self.setFocusId(9100)
    
    def _is_add_series_dialog_active(self):
        """Check if Add Series dialog is currently active."""
        return hasattr(self, '_add_series_controller') and self._add_series_controller and self._add_series_controller.is_active()
    
    def _handle_add_series_action(self, action):
        """Delegate action to Add Series controller. Returns True if handled."""
        if self._is_add_series_dialog_active():
            handled = self._add_series_controller.onAction(action)
            # Check if dialog was closed (e.g. via ESC/Back)
            # Guard against None - callback may have already cleaned up controller
            if handled and self._add_series_controller and not self._add_series_controller.is_active():
                self._close_add_series_dialog()
            return handled
        return False
    
    def _handle_add_series_click(self, controlId):
        """Delegate click to Add Series controller. Returns True if handled."""
        if self._is_add_series_dialog_active():
            handled = self._add_series_controller.onClick(controlId)
            # Check if dialog was closed - guard against None
            if handled and self._add_series_controller and not self._add_series_controller.is_active():
                self._close_add_series_dialog()
            return handled
        return False
    
    def _handle_add_series_focus(self, controlId):
        """Delegate focus to Add Series controller."""
        if self._is_add_series_dialog_active():
            self._add_series_controller.onFocus(controlId)
    

    # ==================================================================================
    # SERIES DETAIL DIALOG METHODS
    # ==================================================================================
    
    def _show_series_detail_dialog(self, series_id, channel_id):
        """Show the inline Series Detail dialog with series info."""
        # Store current series info for removal action
        self._series_detail_series_id = series_id
        self._series_detail_channel_id = channel_id
        
        # Get series data from channel
        channel = self.channel_manager.get_channel_by_id(channel_id)
        series_data = None
        if channel:
            for s in channel.series_list:
                if str(s.get('id')) == str(series_id):
                    series_data = s
                    break
        
        if not series_data:
            return
        
        # Fetch additional metadata from TMDb if needed
        metadata = self._get_series_metadata(series_id, series_data)
        
        # Set window properties for XML
        self.setProperty('WCS.MyTV.SeriesDetail.Name', metadata.get('name', ''))
        self.setProperty('WCS.MyTV.SeriesDetail.Year', str(metadata.get('year', '')))
        self.setProperty('WCS.MyTV.SeriesDetail.Genre', metadata.get('genre', ''))
        self.setProperty('WCS.MyTV.SeriesDetail.Rating', str(metadata.get('rating', '')))
        self.setProperty('WCS.MyTV.SeriesDetail.Plot', metadata.get('plot', ''))
        self.setProperty('WCS.MyTV.SeriesDetail.Fanart', metadata.get('fanart', ''))
        self.setProperty('WCS.MyTV.SeriesDetail.Logo', metadata.get('logo', ''))
        self.setProperty('WCS.MyTV.SeriesDetail.Poster', metadata.get('poster', ''))
        
        # Show the dialog
        self.setProperty('WCS.MyTV.SeriesDetail.Active', 'true')
        
        # Focus on remove button
        self._delayed_focus(9300, delay=0.1)
    
    def _get_series_metadata(self, series_id, series_data):
        """Get series metadata from cache or TMDb."""
        import requests
        from wcs.metadata.TMDbClient import get_tmdb_api_key
        
        # Try cache first
        cache_key = f"series_detail_{series_id}"
        cached = self._cache.get(cache_key)
        if cached:
            return cached
        
        # Build basic metadata from what we have
        metadata = {
            'name': series_data.get('name', ''),
            'year': series_data.get('year', '') or (series_data.get('first_air_date', '')[:4] if series_data.get('first_air_date') else ''),
            'genre': '',
            'rating': '',
            'plot': series_data.get('overview', '') or series_data.get('plot', ''),
            'fanart': '',
            'logo': '',
            'poster': ''
        }
        
        # Get poster
        poster = series_data.get('poster_path') or series_data.get('poster')
        if poster:
            if not poster.startswith('http'):
                poster = f"https://image.tmdb.org/t/p/w500{poster}"
            metadata['poster'] = poster
        
        # Fetch full details from TMDb
        try:
            api_key = get_tmdb_api_key(self.addon)
            
            # Fetch series details
            details_url = f'https://api.themoviedb.org/3/tv/{series_id}?api_key={api_key}&language=cs-CZ'
            response = requests.get(details_url, timeout=10)
            if response.status_code == 200:
                details = response.json()
                
                metadata['name'] = details.get('name', metadata['name'])
                metadata['plot'] = details.get('overview', metadata['plot'])
                metadata['rating'] = f"{details.get('vote_average', 0):.1f}"
                
                # Year
                if details.get('first_air_date'):
                    metadata['year'] = details['first_air_date'][:4]
                
                # Genres
                genres = details.get('genres', [])
                if genres:
                    metadata['genre'] = ', '.join([g.get('name', '') for g in genres[:3]])
                
                # Fanart/backdrop
                backdrop = details.get('backdrop_path')
                if backdrop:
                    metadata['fanart'] = f"https://image.tmdb.org/t/p/w1280{backdrop}"
            
            # Fetch images for clearlogo
            images_url = f'https://api.themoviedb.org/3/tv/{series_id}/images?api_key={api_key}'
            response = requests.get(images_url, timeout=10)
            if response.status_code == 200:
                images = response.json()
                logos = images.get('logos', [])
                for logo in logos:
                    if logo.get('iso_639_1') in ('en', 'cs', None):
                        metadata['logo'] = f"https://image.tmdb.org/t/p/w500{logo['file_path']}"
                        break
            
            # Cache for 7 days
            self._cache.set(cache_key, metadata, ttl_seconds=7 * 24 * 3600)
        except Exception as e:
            xbmc.log(f"[MyTV] Error fetching series details: {e}", xbmc.LOGWARNING)
        
        return metadata
    
    def _close_series_detail_dialog(self, removed=False):
        """Close the inline Series Detail dialog."""
        # Clear any pending confirmation dialog
        self.clearProperty('WCS.MyTV.DeleteSeriesConfirm')
        
        self.clearProperty('WCS.MyTV.SeriesDetail.Active')
        self.clearProperty('WCS.MyTV.SeriesDetail.Name')
        self.clearProperty('WCS.MyTV.SeriesDetail.Year')
        self.clearProperty('WCS.MyTV.SeriesDetail.Genre')
        self.clearProperty('WCS.MyTV.SeriesDetail.Rating')
        self.clearProperty('WCS.MyTV.SeriesDetail.Plot')
        self.clearProperty('WCS.MyTV.SeriesDetail.Fanart')
        self.clearProperty('WCS.MyTV.SeriesDetail.Logo')
        self.clearProperty('WCS.MyTV.SeriesDetail.Poster')
        
        if removed:
            # Refresh views
            self.channel_manager.generate_channel_composite(self._series_detail_channel_id)
            self.update_editor_view()
            self.refresh_program_view()
            self.refresh_channels_view(set_focus=False)
        
        # Return focus to grid
        self.setFocusId(9100)
    
    def _is_series_detail_dialog_active(self):
        """Check if Series Detail dialog is currently active."""
        return bool(self.getProperty('WCS.MyTV.SeriesDetail.Active'))
    
    def _handle_series_detail_action(self, action):
        """Handle actions for Series Detail dialog. Returns True if handled."""
        if not self._is_series_detail_dialog_active():
            return False
        
        action_id = action.getId()
        
        # ESC / Back - check confirmation dialog first
        if action_id in [10, 92]:  # ACTION_PREVIOUS_MENU, ACTION_NAV_BACK
            # If confirmation dialog is active, just close it (don't close the whole dialog)
            if self.getProperty('WCS.MyTV.DeleteSeriesConfirm') == 'true':
                self._cancel_delete_series_confirmation()
                return True
            # Otherwise close the series detail dialog
            self._close_series_detail_dialog(removed=False)
            return True
        
        return False
    
    def _handle_series_detail_click(self, controlId):
        """Handle click events for Series Detail dialog. Returns True if handled."""
        if not self._is_series_detail_dialog_active():
            return False
        
        # Remove from channel button - show confirmation
        if controlId == 9300:
            self._show_delete_series_confirmation()
            return True
        
        # Confirm delete series button
        elif controlId == 9305:
            self._confirm_delete_series()
            return True
        
        # Close button
        elif controlId == 9301:
            self._close_series_detail_dialog(removed=False)
            return True
        
        # Play game button
        elif controlId == 9302:
            from urllib.parse import quote_plus
            series_id = self._series_detail_series_id
            series_name = self.getProperty('WCS.MyTV.SeriesDetail.Name') or ''
            
            # Close dialog first
            self._close_series_detail_dialog(removed=False)
            
            # Launch the game
            game_url = f"plugin://plugin.video.milionar/?action=play_text_game&media_type=series&tmdb_id={series_id}&title={quote_plus(series_name)}"
            xbmc.executebuiltin(f"RunPlugin({game_url})")
            return True
        
        # Play series button (NEW - 9304)
        elif controlId == 9304:
            from urllib.parse import quote_plus
            from wcs.metadata import TMDbClient
            
            series_id = self._series_detail_series_id
            series_name = self.getProperty('WCS.MyTV.SeriesDetail.Name') or ''
            
            # Close dialog and main window
            self._close_series_detail_dialog(removed=False)
            self.close()
            
            # Get episode count from settings
            try:
                upcoming_count_val = self.addon.getSetting('mytv_upcoming_count')
                upcoming_count = int(upcoming_count_val) if upcoming_count_val else 12
            except:
                upcoming_count = 12
            
            # Find user progress from recently played
            last_season, last_episode = 1, 0
            recently_played = user_data.load_recently_played(self.addon)
            for rp in recently_played or []:
                if str(rp.get('tmdb_id', '')) == str(series_id) and rp.get('media_type') in ['tv', 'episode', 'series']:
                    if 'season' in rp and 'episode' in rp:
                        last_season = int(rp['season'])
                        last_episode = int(rp['episode'])
                    break
            
            # Build playlist
            playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
            playlist.clear()
            
            curr_s, curr_e = last_season, last_episode
            episodes_added = 0
            
            while episodes_added < upcoming_count:
                next_meta = TMDbClient.get_next_episode_from_tmdb(series_id, curr_s, curr_e, self.addon)
                if not next_meta:
                    break
                
                # Build URL for playlist item
                url = (f"plugin://{self.addon.getAddonInfo('id')}?action=play_episode_from_addon_playlist"
                       f"&series_name={quote_plus(series_name)}"
                       f"&season_number={next_meta['season']}"
                       f"&episode_number={next_meta['episode']}"
                       f"&tmdb_id={series_id}"
                       f"&episode_title={quote_plus(next_meta.get('episode_title', ''))}"
                       f"&plot={quote_plus(next_meta.get('plot', ''))}"
                       f"&rating={quote_plus(str(next_meta.get('rating', '')))}"
                       f"&poster={quote_plus(next_meta.get('poster', ''))}"
                       f"&fanart={quote_plus(next_meta.get('fanart', ''))}"
                       f"&genre={quote_plus(next_meta.get('genre', ''))}"
                       f"&year={quote_plus(str(next_meta.get('year', '')))}")
                
                # Create list item
                li = xbmcgui.ListItem(label=series_name)
                li.setInfo('video', {
                    'title': next_meta.get('episode_title', ''),
                    'plot': next_meta.get('plot', ''),
                    'season': next_meta['season'],
                    'episode': next_meta['episode'],
                    'tvshowtitle': series_name,
                    'mediatype': 'episode'
                })
                li.setArt({
                    'poster': next_meta.get('poster', ''),
                    'fanart': next_meta.get('fanart', ''),
                    'thumb': next_meta.get('episode_thumb', next_meta.get('poster', ''))
                })
                
                playlist.add(url, li)
                episodes_added += 1
                curr_s, curr_e = next_meta['season'], next_meta['episode']
            
            if episodes_added > 0:
                xbmc.Player().play(playlist)
            else:
                # No episodes found - reopen and show toast
                # Can't easily reopen, just log
                xbmc.log(f"[MyTV] No episodes found for series {series_name}", xbmc.LOGWARNING)
            
            return True
        
        # Similar button
        elif controlId == 9303:
            import requests
            from wcs.metadata.TMDbClient import get_tmdb_api_key
            
            series_id = self._series_detail_series_id
            series_name = self.getProperty('WCS.MyTV.SeriesDetail.Name') or 'Seriál'
            channel_id = self._series_detail_channel_id
            
            # Close dialog first
            self._close_series_detail_dialog(removed=False)
            
            try:
                # Fetch recommendations from TMDb
                url = f'https://api.themoviedb.org/3/tv/{series_id}/recommendations?api_key={get_tmdb_api_key()}&language=cs-CZ'
                response = requests.get(url, timeout=10)
                results = response.json().get('results', [])
                
                if not results:
                    self._show_toast("Žádné podobné seriály", "info")
                    return True
                
                # Show Add Series dialog with similar series
                self._show_add_series_dialog()
                
                # Populate with similar series
                if self._add_series_controller:
                    # Filter out already in channel
                    channel = self.channel_manager.get_channel_by_id(channel_id)
                    current_ids = {str(s.get('id')) for s in channel.series_list} if channel else set()
                    filtered = [r for r in results if str(r.get('id')) not in current_ids][:20]
                    
                    if filtered:
                        self._add_series_controller._items = filtered
                        self._add_series_controller.parent.setProperty('WCS.MyTV.AddDialog.ListingMode', 'similar')
                        self._add_series_controller.parent.setProperty('WCS.MyTV.AddDialog.ListingTitle', f"Podobné: {series_name}")
                        self._add_series_controller._populate_list(filtered)
                        self._add_series_controller.parent.setFocusId(9211)
                    else:
                        self._show_toast("Všechny podobné už jsou v kanálu", "info")
                        
            except Exception as e:
                xbmc.log(f"[MyTV] Error finding similar: {e}", xbmc.LOGERROR)
                self._show_toast("Chyba při hledání", "error")
            return True
        
        return False

    # ==================================================================================
    # BACKGROUND GENERATION METHODS
    # ==================================================================================
    
    def _is_background_processing_needed(self):
        """Check if background needs PIL processing (effects or collages)."""
        # Collage modes always need processing
        if self._bg_mode in (self.BG_COLLAGE_POSTERS, self.BG_COLLAGE_STILLS, self.BG_COLLAGE_MIX):
            return True
        
        # Check if effects are enabled
        blur_enabled = self.addon.getSetting('mytv_background_blur') == 'true'
        glow_enabled = self.addon.getSetting('mytv_background_glow') == 'true'
        return blur_enabled or glow_enabled
    
    def _setup_background_properties(self):
        """Read background settings and set window properties for XML. Returns mode_idx."""
        # Background mode - Kodi returns text value, need to map to index
        mode_value = self.addon.getSetting('mytv_background_mode') or 'Fanart seriálu'
        
        # Map text values to indices
        mode_map = {
            'Statické': 0,
            'Fanart seriálu': 1,
            'Still epizody': 2,
            'Koláž posterů': 3,
            'Koláž stillů': 4,
            'Mix koláž': 5
        }
        mode_idx = mode_map.get(mode_value, 1)  # Default to fanart
        self._bg_mode = mode_idx  # Store for later use
        
        xbmc.log(f"[MyTV] Background mode: '{mode_value}' -> {mode_idx}", xbmc.LOGINFO)
        self.setProperty('WCS.MyTV.BackgroundMode', str(mode_idx))
        
        # Dimming
        dim_pct = self.addon.getSetting('mytv_background_dim') or '50'
        try:
            dim_val = int(dim_pct)
        except ValueError:
            dim_val = 50
        # Convert percentage to hex alpha (0-90% -> hex value)
        dim_alpha = int(255 * (dim_val / 100))
        dim_hex = f"{dim_alpha:02X}000000"
        self.setProperty('WCS.MyTV.BackgroundDim', dim_hex)
        
        # Animation - Ken Burns effect with selectable style
        anim_enabled = self.addon.getSetting('mytv_background_animation') == 'true'
        self.setProperty('WCS.MyTV.Animation', 'true' if anim_enabled else '')
        
        if anim_enabled:
            # Read animation style and set property for XML conditions
            style = self.addon.getSetting('mytv_anim_style') or 'Jemny dech'
            style_map = {
                'Jemny dech': '0',        # Subtle zoom + slight drift, 45s
                'Pulzujici srdce': '1',   # Only zoom, no slide, 30s
                'Plynouci tok': '2',      # Only horizontal slide, 50s
                'Dynamicka vlna': '3',    # Big zoom + diagonal, fast 25s
                'Zrcadlovy odraz': '4'    # Medium zoom, opposite diagonal, 40s
            }
            style_idx = style_map.get(style, '0')
            self.setProperty('WCS.MyTV.AnimStyle', style_idx)
            xbmc.log(f"[MyTV] Animation: style={style} (idx={style_idx})", xbmc.LOGINFO)
    
    def _update_background(self, channel_id=None):
        """Update background image based on current mode and selected channel."""
        mode_str = self.getProperty('WCS.MyTV.BackgroundMode') or '1'
        try:
            mode = int(mode_str)
        except ValueError:
            mode = self.BG_FANART
        
        # Check if effects are enabled
        blur_enabled = self.addon.getSetting('mytv_background_blur') == 'true'
        glow_enabled = self.addon.getSetting('mytv_background_glow') == 'true'
        effects_enabled = blur_enabled or glow_enabled
        
        # If no effects and not collage mode, XML handles backgrounds directly - no action needed
        # Don't clear property - keep any cached background visible
        if not effects_enabled and mode in (self.BG_STATIC, self.BG_FANART, self.BG_STILL):
            return
        
        # All other cases need PIL processing
        if not PIL_AVAILABLE:
            xbmc.log("[MyTV] PIL not available, effects disabled", xbmc.LOGWARNING)
            return
        
        # Static mode doesn't need channel info - uses fixed image
        # Other modes need channel data for fanart/stills/collages
        channel = None
        if mode != self.BG_STATIC:
            # Get channel info
            if not channel_id:
                try:
                    list_control = self.getControl(2000)
                    idx = list_control.getSelectedPosition()
                    if idx >= 0:
                        item = list_control.getListItem(idx)
                        channel_id = item.getProperty('channel_id')
                except:
                    return
            
            if not channel_id:
                return
            
            channel = self.channel_manager.get_channel_by_id(channel_id)
            if not channel:
                return
        
        # Generate background in background thread
        import threading
        threading.Thread(
            target=self._generate_background_async,
            args=(mode, channel_id, channel),
            daemon=True
        ).start()
    
    def _generate_background_async(self, mode, channel_id, channel):
        """Generate background with effects in background thread."""
        try:
            # Get effect settings for cache key
            blur_on = self.addon.getSetting('mytv_background_blur') == 'true'
            blur_r = self.addon.getSetting('mytv_background_blur_radius') or '5'
            glow_on = self.addon.getSetting('mytv_background_glow') == 'true'
            glow_i = self.addon.getSetting('mytv_background_glow_intensity') or '3'
            effects_key = f"b{blur_on}{blur_r}g{glow_on}{glow_i}"
            
            result_image = None
            source_url = None
            
            # Mode 0: Static background
            if mode == self.BG_STATIC:
                source_url = 'special://home/addons/plugin.video.milionar/resources/media/hub_background_v2.png'
                cache_key = f"bg_static_{effects_key}"
            
            # Mode 1: Fanart from first program item
            elif mode == self.BG_FANART:
                try:
                    program_list = self.getControl(9000)
                    if program_list.size() > 0:
                        source_url = program_list.getListItem(0).getArt('fanart')
                except:
                    pass
                if not source_url:
                    return
                cache_key = f"bg_fanart_{hash(source_url)}_{effects_key}"
            
            # Mode 2: Still from first program item
            elif mode == self.BG_STILL:
                try:
                    program_list = self.getControl(9000)
                    if program_list.size() > 0:
                        source_url = program_list.getListItem(0).getArt('thumb')
                except:
                    pass
                if not source_url:
                    return
                cache_key = f"bg_still_{hash(source_url)}_{effects_key}"
            
            # Mode 3-5: Collages
            else:
                images = []
                
                if mode == self.BG_COLLAGE_POSTERS:
                    if channel and channel.series_list:
                        for series in channel.series_list[:12]:
                            poster = series.get('poster_path') or series.get('poster')
                            if poster:
                                if not poster.startswith('http'):
                                    poster = f"https://image.tmdb.org/t/p/w342{poster}"
                                images.append(('poster', poster))
                
                elif mode == self.BG_COLLAGE_STILLS:
                    try:
                        program_list = self.getControl(9000)
                        for i in range(min(12, program_list.size())):
                            item = program_list.getListItem(i)
                            thumb = item.getArt('thumb')
                            if thumb:
                                images.append(('still', thumb))
                    except:
                        pass
                
                elif mode == self.BG_COLLAGE_MIX:
                    if channel and channel.series_list:
                        for series in channel.series_list[:6]:
                            poster = series.get('poster_path') or series.get('poster')
                            if poster:
                                if not poster.startswith('http'):
                                    poster = f"https://image.tmdb.org/t/p/w342{poster}"
                                images.append(('poster', poster))
                    try:
                        program_list = self.getControl(9000)
                        for i in range(min(6, program_list.size())):
                            item = program_list.getListItem(i)
                            thumb = item.getArt('thumb')
                            if thumb:
                                images.append(('still', thumb))
                    except:
                        pass
                
                if not images:
                    return
                
                cache_key = f"bg_collage_{mode}_{channel_id}_{len(images)}_{effects_key}"
            
            cache_path = self._cache.get_asset_path(cache_key)
            if cache_path:
                self.setProperty('WCS.MyTV.Background.Custom', cache_path)
                # Set Ready for ALL modes (background is ready from cache)
                self.setProperty('WCS.MyTV.Background.Ready', 'true')
                return
            
            # Generate/download base image
            if mode in (self.BG_STATIC, self.BG_FANART, self.BG_STILL):
                # Single image modes
                result_image = self._download_image(source_url)
                if result_image:
                    result_image = result_image.convert('RGB')
                    # Resize to 1920x1080
                    result_image = self._resize_to_cover(result_image, 1920, 1080)
            else:
                # Collage modes
                if mode == self.BG_COLLAGE_MIX:
                    result_image = self._generate_mix_collage(images)
                else:
                    result_image = self._generate_collage(images)
            
            if result_image:
                # Apply effects
                result_image = self._apply_effects(result_image)
                
                cache_path = self._cache.save_pil_image(cache_key, result_image)
                if cache_path:
                    self.setProperty('WCS.MyTV.Background.Custom', cache_path)
                    # Set Ready for ALL modes with effects (background just generated)
                    self.setProperty('WCS.MyTV.Background.Ready', 'true')
        
        except Exception as e:
            xbmc.log(f"[MyTV] Error generating background: {e}", xbmc.LOGERROR)

    def _resize_to_cover(self, image, target_w, target_h):
        """Resize image to cover target dimensions (crop excess)."""
        img_ratio = image.width / image.height
        target_ratio = target_w / target_h
        
        if img_ratio > target_ratio:
            new_h = target_h
            new_w = int(new_h * img_ratio)
        else:
            new_w = target_w
            new_h = int(new_w / img_ratio)
        
        image = image.resize((new_w, new_h), Image.LANCZOS)
        
        left = (new_w - target_w) // 2
        top = (new_h - target_h) // 2
        return image.crop((left, top, left + target_w, top + target_h))
    
    def _generate_collage(self, images):
        """Generate a grid collage from images."""
        import requests
        from io import BytesIO
        
        # Determine grid size
        count = len(images)
        if count <= 4:
            cols, rows = 2, 2
        elif count <= 6:
            cols, rows = 3, 2
        elif count <= 9:
            cols, rows = 3, 3
        else:
            cols, rows = 4, 3
        
        # Canvas size (1920x1080)
        canvas_w, canvas_h = 1920, 1080
        cell_w = canvas_w // cols
        cell_h = canvas_h // rows
        
        canvas = Image.new('RGB', (canvas_w, canvas_h), (20, 20, 20))
        
        for i, (img_type, url) in enumerate(images[:cols*rows]):
            if i >= cols * rows:
                break
            
            try:
                img = self._download_image(url)
                if img:
                    # Resize to fill cell
                    img = img.convert('RGB')
                    img_ratio = img.width / img.height
                    cell_ratio = cell_w / cell_h
                    
                    if img_ratio > cell_ratio:
                        new_h = cell_h
                        new_w = int(new_h * img_ratio)
                    else:
                        new_w = cell_w
                        new_h = int(new_w / img_ratio)
                    
                    img = img.resize((new_w, new_h), Image.LANCZOS)
                    
                    # Crop to center
                    left = (new_w - cell_w) // 2
                    top = (new_h - cell_h) // 2
                    img = img.crop((left, top, left + cell_w, top + cell_h))
                    
                    # Paste to canvas
                    col = i % cols
                    row = i // cols
                    canvas.paste(img, (col * cell_w, row * cell_h))
            except Exception as e:
                xbmc.log(f"[MyTV] Collage image error: {e}", xbmc.LOGWARNING)
        
        return canvas
    
    def _generate_mix_collage(self, images):
        """Generate creative mixed collage with varying sizes."""
        import random
        from io import BytesIO
        
        canvas_w, canvas_h = 1920, 1080
        canvas = Image.new('RGB', (canvas_w, canvas_h), (15, 15, 20))
        
        random.shuffle(images)
        
        # Define varying cell sizes for creative layout
        positions = [
            (0, 0, 640, 540),      # Large left top
            (0, 540, 640, 540),    # Large left bottom
            (640, 0, 480, 360),    # Medium center top
            (640, 360, 480, 360),  # Medium center middle
            (640, 720, 480, 360),  # Medium center bottom
            (1120, 0, 400, 270),   # Small right top 1
            (1520, 0, 400, 270),   # Small right top 2
            (1120, 270, 400, 270), # Small right mid 1
            (1520, 270, 400, 270), # Small right mid 2
            (1120, 540, 800, 540), # Large right bottom
        ]
        
        for i, (img_type, url) in enumerate(images[:len(positions)]):
            if i >= len(positions):
                break
            
            x, y, w, h = positions[i]
            
            try:
                img = self._download_image(url)
                if img:
                    img = img.convert('RGB')
                    
                    # Resize maintaining aspect ratio then crop
                    img_ratio = img.width / img.height
                    cell_ratio = w / h
                    
                    if img_ratio > cell_ratio:
                        new_h = h
                        new_w = int(new_h * img_ratio)
                    else:
                        new_w = w
                        new_h = int(new_w / img_ratio)
                    
                    img = img.resize((new_w, new_h), Image.LANCZOS)
                    
                    left = (new_w - w) // 2
                    top = (new_h - h) // 2
                    img = img.crop((left, top, left + w, top + h))
                    
                    canvas.paste(img, (x, y))
            except Exception as e:
                xbmc.log(f"[MyTV] Mix collage image error: {e}", xbmc.LOGWARNING)
        
        return canvas
    
    def _apply_effects(self, image):
        """Apply blur/glow effects based on settings."""
        # Blur
        if self.addon.getSetting('mytv_background_blur') == 'true':
            try:
                radius = int(self.addon.getSetting('mytv_background_blur_radius') or '5')
                image = image.filter(ImageFilter.GaussianBlur(radius=radius))
            except:
                pass
        
        # Glow (brightness + blur for glow effect)
        if self.addon.getSetting('mytv_background_glow') == 'true':
            try:
                from PIL import ImageEnhance
                intensity = int(self.addon.getSetting('mytv_background_glow_intensity') or '3')
                # Increase brightness
                enhancer = ImageEnhance.Brightness(image)
                image = enhancer.enhance(1 + (intensity * 0.05))
                # Subtle blur for glow
                image = image.filter(ImageFilter.GaussianBlur(radius=intensity))
            except:
                pass
        
        return image
    
    def _download_image(self, url):
        """Download image from URL and return PIL Image."""
        import requests
        from io import BytesIO
        
        try:
            # Handle special:// paths
            if url.startswith('special://'):
                local_path = xbmcvfs.translatePath(url)
                if xbmcvfs.exists(local_path):
                    return Image.open(local_path)
                return None
            
            # Download from URL
            response = requests.get(url, timeout=10)
            if response.status_code == 200:
                return Image.open(BytesIO(response.content))
        except Exception as e:
            xbmc.log(f"[MyTV] Download image error: {url} - {e}", xbmc.LOGWARNING)
        
        return None


def show_my_tv_dialog(addon, show_nav_sidebar=False, nav_position=0):
    dialog = MyTVDialog(
        "ai/DialogMyTV.xml",
        addon.getAddonInfo('path'),
        "Default",
        "1080i",
        show_nav_sidebar=show_nav_sidebar,
        nav_position=nav_position
    )
    dialog.doModal()
    del dialog
