# wcs/ai/MyTVChannelManager.py
import json
import random
import uuid
import xbmc
import xbmcaddon
import os
import xbmcvfs
from wcs import user_data
from wcs.library import LibraryManager

class Channel:
    def __init__(self, id=None, name="Můj Kanál", icon=None, series_list=None):
        self.id = id or str(uuid.uuid4())
        self.name = name
        self.icon = icon
        # series_list: list of dicts { 'id': tmdb_id, 'name': ..., 'poster_path': ... }
        self.series_list = series_list or []

    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'icon': self.icon,
            'series_list': self.series_list
        }

    @classmethod
    def from_dict(cls, data):
        return cls(
            id=data.get('id'),
            name=data.get('name'),
            icon=data.get('icon'),
            series_list=data.get('series_list')
        )

class MyTVChannelManager:
    def __init__(self, addon):
        self.addon = addon
        self.channels = []
        self._load_channels()

    def _load_channels(self):
        data = self.addon.getSetting('my_tv_channels')
        if data:
            try:
                raw_list = json.loads(data)
                self.channels = [Channel.from_dict(item) for item in raw_list]
            except Exception as e:
                xbmc.log(f"[plugin.video.milionar] Error loading MyTV channels: {e}", level=xbmc.LOGERROR)
                self.channels = []
        
                self.channels = []
        
        # Default channel creation removed.
        # if not self.channels:
        #     self.create_channel("Hlavní kanál")

    def _save_channels(self):
        try:
            raw_list = [ch.to_dict() for ch in self.channels]
            self.addon.setSetting('my_tv_channels', json.dumps(raw_list))
        except Exception as e:
             xbmc.log(f"[plugin.video.milionar] Error saving MyTV channels: {e}", level=xbmc.LOGERROR)

    def get_all_channels(self):
        return self.channels

    def get_channel_by_id(self, channel_id):
        return next((ch for ch in self.channels if ch.id == channel_id), None)

    def create_channel(self, name):
        new_channel = Channel(name=name)
        self.channels.append(new_channel)
        self._save_channels()
        return new_channel

    def toggle_series_in_channel(self, channel_id, series_data):
        """
        Adds or removes a series from the channel.
        series_data should explicitly contain 'id', 'name', 'poster_path' (or 'poster').
        """
        channel = self.get_channel_by_id(channel_id)
        if not channel:
            return False

        # Normalize ID to str for comparison
        target_id = str(series_data.get('id'))
        
        # Check if exists
        exists_idx = -1
        for idx, s in enumerate(channel.series_list):
            if str(s.get('id')) == target_id:
                exists_idx = idx
                break
        
        if exists_idx >= 0:
            # Remove
            channel.series_list.pop(exists_idx)
            action = "removed"
        else:
            # Add
            # Ensure we store minimal required data
            clean_data = {
                'id': series_data.get('id'),
                'name': series_data.get('name') or series_data.get('title'),
                'poster_path': series_data.get('poster_path') or series_data.get('poster'),
                'year': series_data.get('year') or series_data.get('first_air_date', '')[:4]
            }
            channel.series_list.append(clean_data)
            action = "added"
        
        self._save_channels()
        
        # Regenerate channel icon
        self.generate_channel_composite(channel.id)
        
        return action == "added"

    def generate_channel_composite(self, channel_id):
        """
        Generates a composite icon for the channel based on its series.
        Uses PIL to create a 2x2 grid of posters.
        """
        try:
            from PIL import Image
            import io
        except ImportError as e:
            xbmc.log(f"[plugin.video.milionar] PIL import failed: {e}", level=xbmc.LOGERROR)
            import traceback
            traceback.print_exc()
            return

        # Robust selection of resampling filter for Android/Old PIL compatibility
        resample_filter = None
        filter_name = "Unknown"
        try:
            if hasattr(Image, 'Resampling'):
                resample_filter = Image.Resampling.LANCZOS
                filter_name = "Image.Resampling.LANCZOS"
            elif hasattr(Image, 'LANCZOS'):
                resample_filter = Image.LANCZOS
                filter_name = "Image.LANCZOS"
            elif hasattr(Image, 'ANTIALIAS'):
                resample_filter = Image.ANTIALIAS
                filter_name = "Image.ANTIALIAS"
            else:
                resample_filter = Image.BICUBIC
                filter_name = "Image.BICUBIC (Fallback)"
        except Exception:
            resample_filter = Image.BICUBIC
            filter_name = "Image.BICUBIC (Exception Fallback)"
            
        xbmc.log(f"[plugin.video.milionar] PIL loaded. Using resampling filter: {filter_name}", level=xbmc.LOGINFO)

        channel = self.get_channel_by_id(channel_id)
        if not channel: return

        # Constants
        CANVAS_WIDTH = 500
        CANVAS_HEIGHT = 750
        
        # Prepare text directory
        profile_path = xbmcvfs.translatePath(self.addon.getAddonInfo('profile'))
        icons_dir = os.path.join(profile_path, 'channel_icons')
        if not os.path.exists(icons_dir):
            xbmcvfs.mkdir(icons_dir) # Use xbmcvfs.mkdir just in case
            
        target_file = os.path.join(icons_dir, f"{channel_id}.jpg")
        
        # Get series posters (max 4)
        posters = []
        for s in channel.series_list[:4]:
            p_path = s.get('poster_path') or s.get('poster')
            if p_path:
                local_path = self._get_cached_poster(p_path)
                if local_path:
                    posters.append(local_path)
        
        if not posters:
            # Revert to none (let UI use fallback) or use specific default?
            # If we set None, UI uses placeholder.
            channel.icon = None
            self._save_channels()
            return

        # Create canvas
        canvas = Image.new('RGB', (CANVAS_WIDTH, CANVAS_HEIGHT), color=(20, 20, 30))
        
        try:
            if len(posters) == 1:
                # Single poster - stretch to fit? Or just use it?
                # Just resize to canvas
                # We need to open properly. safe method: xbmcvfs.File -> BytesIO -> Image.open
                # But Image.open usually handles file paths fine if they exist.
                # Let's try direct open first, if it fails, fallback to xbmcvfs read.
                # Actually, standard open might be issue on Android too.
                # Let's use a helper to open image safely
                img = self._open_image_safely(posters[0])
                if img:
                    img = img.resize((CANVAS_WIDTH, CANVAS_HEIGHT), resample_filter)
                    canvas.paste(img, (0, 0))
                
            elif len(posters) >= 2:
                # 2-4 posters: 2x2 grid
                # Determine cell size
                cell_w = CANVAS_WIDTH // 2
                cell_h = CANVAS_HEIGHT // 2
                
                # Positions for 4 quadrants: TL, TR, BL, BR
                positions = [
                    (0, 0), (cell_w, 0),
                    (0, cell_h), (cell_w, cell_h)
                ]
                
                for i, p_path in enumerate(posters):
                    if i >= 4: break
                    img = self._open_image_safely(p_path)
                    if img:
                        img = img.resize((cell_w, cell_h), resample_filter)
                        canvas.paste(img, positions[i])
            
            # Save using xbmcvfs to ensure Android write permission compatibility
            buffer = io.BytesIO()
            canvas.save(buffer, 'JPEG', quality=90)
            buffer.seek(0)
            
            f = xbmcvfs.File(target_file, 'w')
            f.write(buffer.read())
            f.close()
            
            # Update channel
            channel.icon = target_file
            self._save_channels()
            xbmc.log(f"[plugin.video.milionar] Generated channel icon: {target_file}", level=xbmc.LOGINFO)
            
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Error generating composite: {e}", level=xbmc.LOGERROR)
            import traceback
            traceback.print_exc()

    def _open_image_safely(self, path):
        """Helper to open image compatible with Android xbmcvfs"""
        from PIL import Image
        import io
        try:
            # Try direct open first (works on most systems)
            return Image.open(path)
        except Exception:
            try:
                # Fallback: read bytes via xbmcvfs
                f = xbmcvfs.File(path, 'rb')
                data = f.read()
                f.close()
                return Image.open(io.BytesIO(data))
            except Exception as e:
                xbmc.log(f"[plugin.video.milionar] Failed to open image safely {path}: {e}", level=xbmc.LOGERROR)
                return None

    def _get_cached_poster(self, poster_path):
        """
        Downloads poster if needed and returns local path.
        poster_path can be full URL or TMDB partial path.
        """
        if not poster_path: return None
        
        # Construct URL
        if poster_path.startswith('http'):
            url = poster_path
        else:
            url = f"https://image.tmdb.org/t/p/w500{poster_path}"
            
        # Define cache path
        # Hash URL to get filename
        import hashlib
        name_hash = hashlib.md5(url.encode('utf-8')).hexdigest()
        profile_path = xbmcvfs.translatePath(self.addon.getAddonInfo('profile'))
        cache_dir = os.path.join(profile_path, 'img_cache')
        if not os.path.exists(cache_dir):
            xbmcvfs.mkdir(cache_dir)
            
        local_file = os.path.join(cache_dir, f"{name_hash}.jpg")
        
        if xbmcvfs.exists(local_file):
            return local_file
            
        # Download
        try:
            import urllib.request
            # Minimal headers to avoid some 403s
            req = urllib.request.Request(url, headers={'User-Agent': 'Kodi-Milionar/1.0'})
            with urllib.request.urlopen(req) as response:
                data = response.read()
                # Write using xbmcvfs
                f = xbmcvfs.File(local_file, 'w')
                f.write(data)
                f.close()
            return local_file
        except Exception as e:
            xbmc.log(f"[plugin.video.milionar] Download error for {url}: {e}", level=xbmc.LOGERROR)
            return None

    def get_random_episode_for_channel(self, channel_id):
        """
        Returns info about a random 'Next Up' episode from the channel.
        Returns dict or None.
        """
        channel = self.get_channel_by_id(channel_id)
        if not channel or not channel.series_list:
            return None

        # Shuffle series to try random ones
        series_candidates = list(channel.series_list)
        random.shuffle(series_candidates)

        recently_played = user_data.load_recently_played(self.addon)

        for series in series_candidates:
            series_name = series.get('name')
            series_id = str(series.get('id'))
            
            # Find progress
            last_season = 1
            last_episode = 0 # So next is 1
            
            # Look in recently played
            for item in recently_played:
                # Match by ID if possible, else Title
                rp_id = str(item.get('tmdb_id', ''))
                if rp_id == series_id or item.get('title') == series_name:
                    # Parse season/episode from somewhere? 
                    # Recently played items usually store metadata
                    # user_data.add_recently_played_item stores whatever we pass it.
                    # Usually it has 'season' and 'episode' if it was an episode
                    if 'season' in item and 'episode' in item:
                        last_season = int(item['season'])
                        last_episode = int(item['episode'])
                        break
            
            # Get next episode
            next_metadata = LibraryManager.get_next_episode_metadata_from_library(
                series_name, last_season, last_episode
            )

            if next_metadata:
                # Add TMDB ID to metadata for player/tracking
                next_metadata['tmdb_id'] = series_id
                return next_metadata
            
            # If nothing found for next episode (maybe Last Season, Last Episode was watched),
            # try to see if S01E01 exists (maybe re-watch? Or maybe user never watched it but it's not in recently played)
            if last_season == 1 and last_episode == 0:
                # It means we didn't find it in recently played.
                # get_next_episode_metadata_from_library(..., 1, 0) should have returned S01E01 if it exists.
                # If it returned None, maybe the library name doesn't match perfectly or it's not in library.
                pass

        return None
