# wcs/ai/DialogMyTV.py
import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
import os
import hashlib
from urllib.parse import quote_plus
from wcs.ai.DialogAIChatRecommendation import AIChatRecommendationDialog
from wcs.ai.MyTVChannelManager import MyTVChannelManager
from wcs import user_data, utils

# 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)

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
        
        # Background cache
        self._bg_cache = {}
        self._bg_cache_dir = xbmcvfs.translatePath(
            'special://profile/addon_data/plugin.video.milionar/bg_cache/'
        )
        if not xbmcvfs.exists(self._bg_cache_dir):
            xbmcvfs.mkdirs(self._bg_cache_dir)

    def onInit(self):
        self._reset_state()
        self.setProperty('WCS.AIChat.Title', 'Moje televizní vysílaní')
        self.setProperty('WCS.AIChat.Status', 'Pripraven')
        
        # Always start with the first channel
        self.current_channel_index = 0
        
        # Set background mode and animation properties
        self._setup_background_properties()
        
        # Load View
        self.refresh_channels_view(set_focus=True)
        
        # Ensure sidebar is hidden initially, handled by base class logic or show_nav_sidebar arg
        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() # Nechame program list byt, at to neblika
            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)

            # Check if empty - add placeholder "Add First Series" to prevent focus trap
            if grid_control.size() == 0:
                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
        if not hasattr(self, '_episode_cache'):
            self._episode_cache = {}  # series_id -> list of episode metadata
        
        # 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
        
        # Cancel existing timer
        if self._program_load_timer:
            self._program_load_timer.cancel()
        
        # Schedule debounced load (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 _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
        if not hasattr(self, '_recently_played_cache'):
            self._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 self._recently_played_cache:
                    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
                cache_key = f"{series_id}_{curr_s}_{curr_e + 1}"
                if cache_key in self._episode_cache:
                    next_meta = self._episode_cache[cache_key]
                else:
                    # Fetch from TMDb (slow)
                    next_meta = TMDbClient.get_next_episode_from_tmdb(series_id, curr_s, curr_e, self.addon)
                    if next_meta:
                        self._episode_cache[cache_key] = next_meta
                
                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'])
                else:
                    break
        
        # After loading program, update background if collage mode
        if items_count > 0:
            self._update_background(channel_id)
    
    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="Zadny program. Pridejte serialy.")
            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):
        action_id = action.getId()
        focused_id = self.getFocusId()
        
        # 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):
        # 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):
        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()
            else:
                # Move focus to "Spustit vysilani" button with delay
                self._delayed_focus(9004, delay=0.1)
        elif controlId == 9100:
            # Context Menu for Grid Item (Remove)
            self._handle_grid_click()
        elif controlId == 9001:
            # Add from My Series
            self._add_from_my_series()
        elif controlId == 9002:
            # Add New (Search)
            self._add_new_search()
        elif controlId == 9003:
            # Delete Channel
            self._delete_current_channel()
        elif controlId == 9004:
            # Play Broadcast (First item in upcoming)
            self._play_broadcast()
        elif controlId == 9005:
            # Refresh/Shuffle
            self.refresh_program_view()
            xbmcgui.Dialog().notification("Moje TV", "Program aktualizovan", xbmcgui.NOTIFICATION_INFO)
        elif controlId == 9000:
            # Play Program Item
            self._play_program_item()
        
        super(MyTVDialog, self).onClick(controlId)

    def _create_new_channel(self):
        # Block sidebar during this operation
        self._block_sidebar = True
        try:
            name = xbmcgui.Dialog().input("Nazev kanalu", type=xbmcgui.INPUT_ALPHANUM)
            if not name: return
            
            self.channel_manager.create_channel(name)
            # Select the new one (first one - new channels are prepended)
            self.current_channel_index = 0
            self.refresh_channels_view(set_focus=False)
            
            # Auto-trigger "Add from My Series"
            self._add_from_my_series()
            
            # After adding series, refresh and focus on the new channel
            self.refresh_channels_view(set_focus=False)
            self.update_editor_view()
            self.refresh_program_view()
            
            # Focus on channel list (new channel card)
            self._delayed_focus(2000, delay=0.2)
        finally:
            self._block_sidebar = False

    def _delete_current_channel(self):
        list_control = self.getControl(2000)
        item = list_control.getSelectedItem()
        if not item or item.getProperty('is_create_button') == 'true': return
        
        channel_id = item.getProperty('channel_id')
        channel_name = item.getLabel()
        
        if xbmcgui.Dialog().yesno("Smazat kanál", f"Opravdu chccete smazat kanál '{channel_name}'?"):
            # We need method in manager to delete
            # self.channel_manager.delete_channel(channel_id) # Missing method
            # Let's implement ad-hoc or add to manager. Best to add to manager.
            # For quick fix, I will do it here via accessing list directly, but cleaner is to update manager file.
            # I will update manager file first? No, I can access internal list if I have to, but let's assume I can update manager.
            # Actually I already implemented the manager file in previous turns. I should check if I have delete method.
            # I don't think I added delete method.
            
            # Accessing internal list for now to save tool calls, or re-write manager.
            # Let's rewrite manager slightly or just filter list.
            
            self.channel_manager.channels = [ch for ch in self.channel_manager.channels if ch.id != channel_id]
            self.channel_manager._save_channels()
            
            self.current_channel_index = 0
            self.refresh_channels_view()
            self.current_channel_index = 0
            self.refresh_channels_view()
            self.refresh_channels_view()
            xbmcgui.Dialog().notification("Moje TV", "Kanál smazán.", xbmcgui.NOTIFICATION_INFO)

    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:
            xbmcgui.Dialog().notification("Moje TV", "Žádný program k vysílání.", xbmcgui.NOTIFICATION_INFO)
            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:
            xbmcgui.Dialog().notification(
                self.addon.getAddonInfo('name'),
                "Kanál nemá žádné dostupné epizody",
                xbmcgui.NOTIFICATION_INFO
            )
            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": "Vytvorit Comedy Channel", "value": "create_channel:comedy"},
            {"label": "Vytvorit Drama Channel", "value": "create_channel:drama"},
            {"label": "Vytvorit 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':
                self._add_from_my_series()
                return

            series_label = item.getLabel()
            channel_id = item.getProperty('channel_id')
            series_id = item.getProperty('series_id')
            
            # Context menu
            menu = xbmcgui.Dialog().contextmenu([f"Smazat '{series_label}' z kanalu"])
            if menu == 0:
                # Remove
                self.channel_manager.toggle_series_in_channel(channel_id, {'id': series_id})
                # Refresh editor view only to keep focus position best as possible
                self.update_editor_view()
                self.refresh_program_view()
                self.setFocusId(9100)  # Keep focus in grid (fixed ID)
                xbmcgui.Dialog().notification("Moje TV", f"'{series_label}' odebrano.", xbmcgui.NOTIFICATION_INFO)
        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:
                 xbmcgui.Dialog().notification("Moje TV", "Nenalezeny zadne ulozene serialy.", xbmcgui.NOTIFICATION_INFO)
                 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:
                 xbmcgui.Dialog().notification("Moje TV", "Vsechny vase serialy uz jsou v kanalu.", xbmcgui.NOTIFICATION_INFO)
                 return
                 
            # Multi select
            labels = [f"{s.get('name')} ({s.get('year', '')})" for s in candidates]
            selected = xbmcgui.Dialog().multiselect("Pridat do kanalu", labels)
            
            if selected:
                for idx in selected:
                    self.channel_manager.toggle_series_in_channel(channel_id, candidates[idx])
                # Refresh editor view
                self.update_editor_view()
                self.refresh_program_view()
                self.setFocusId(9100)  # Changed from 3000 to correct ID
                xbmcgui.Dialog().notification("Moje TV", "Serialy pridany.", xbmcgui.NOTIFICATION_INFO)
        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 serial", 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:
                 xbmcgui.Dialog().notification("Moje TV", "Nic nenalezeno.", xbmcgui.NOTIFICATION_INFO)
                 return
                 
            choices = [f"{item['name']} ({item.get('first_air_date', '')[:4]})" for item in results]
            selected_idx = xbmcgui.Dialog().select("Vyberte serial", 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)
                xbmcgui.Dialog().notification("Moje TV", f"'{selected_item['name']}' pridano.", xbmcgui.NOTIFICATION_INFO)
        finally:
            self._block_sidebar = False

    # ==================================================================================
    # BACKGROUND GENERATION METHODS
    # ==================================================================================
    
    def _setup_background_properties(self):
        """Read background settings and set window properties for XML."""
        mode = self.addon.getSetting('mytv_background_mode') or '1'
        try:
            mode_idx = int(mode)
        except ValueError:
            mode_idx = 1  # Default to fanart
        
        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
        anim_enabled = self.addon.getSetting('mytv_background_animation') == 'true'
        self.setProperty('WCS.MyTV.Animation', 'true' if anim_enabled else '')
        
        if anim_enabled:
            anim_speed = self.addon.getSetting('mytv_background_anim_speed') or '20'
            anim_zoom = self.addon.getSetting('mytv_background_anim_zoom') or '10'
            self.setProperty('WCS.MyTV.AnimSpeed', anim_speed)
            self.setProperty('WCS.MyTV.AnimZoom', anim_zoom)
    
    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
        
        # Static mode - no dynamic background needed
        if mode == self.BG_STATIC:
            self.clearProperty('WCS.MyTV.Background.Custom')
            return
        
        # Fanart/Still modes - handled by XML via Container(9000).ListItem properties
        if mode in (self.BG_FANART, self.BG_STILL):
            self.clearProperty('WCS.MyTV.Background.Custom')
            return
        
        # Collage modes - need to generate image
        if not PIL_AVAILABLE:
            xbmc.log("[MyTV] PIL not available, falling back to fanart mode", xbmc.LOGWARNING)
            self.setProperty('WCS.MyTV.BackgroundMode', str(self.BG_FANART))
            return
        
        # Get images for collage
        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 or not channel.series_list:
            return
        
        # Generate collage 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 collage background in background thread."""
        try:
            images = []
            
            if mode == self.BG_COLLAGE_POSTERS:
                # Get poster URLs from series in channel
                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:
                # Get episode stills from program list
                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:
                # Mix of posters and stills
                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
            
            # Generate cache key
            cache_key = hashlib.md5(f"{mode}_{channel_id}_{len(images)}".encode()).hexdigest()
            cache_path = os.path.join(self._bg_cache_dir, f"bg_{cache_key}.jpg")
            
            # Check cache
            if xbmcvfs.exists(cache_path):
                self.setProperty('WCS.MyTV.Background.Custom', cache_path)
                return
            
            # Generate collage
            if mode == self.BG_COLLAGE_MIX:
                collage = self._generate_mix_collage(images)
            else:
                collage = self._generate_collage(images)
            
            if collage:
                # Apply effects
                collage = self._apply_effects(collage)
                
                # Save to cache
                collage.save(cache_path, 'JPEG', quality=85)
                self.setProperty('WCS.MyTV.Background.Custom', cache_path)
        
        except Exception as e:
            xbmc.log(f"[MyTV] Error generating background: {e}", xbmc.LOGERROR)
    
    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
