# resources/lib/utils/play_handler.py
# -*- coding: utf-8 -*-

import os
import re
import json
import time
from datetime import datetime
from urllib.parse import urlencode

import xbmc
import xbmcgui
import xbmcplugin
import xbmcvfs
import xbmcaddon

from resources.lib.utils.log_utils import get_logger
from resources.lib.series import series_manager
from resources.lib.history import local_history
from resources.lib.integrations import titulky_integration

log = get_logger()

class PlayHandler(object):
    """
    Zapouzdřuje veškerou logiku přehrávání z doplňku.
    Migrováno z kořenového router bridge -> play(params).
    """

    def __init__(self, handle, addon, profile, ws_client):
        self.handle = handle
        self.addon = addon
        self.profile = profile
        self.ws = ws_client  # WebshareClient instance
        self.addon_name = addon.getAddonInfo('name')
        self.ua = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                   "AppleWebKit/537.36 (KHTML, like Gecko) "
                   "Chrome/81.0.4044.138 Safari/537.36")
        self.base = 'https://webshare.cz'

    def _popinfo(self, message, icon=xbmcgui.NOTIFICATION_INFO):
        xbmcgui.Dialog().notification(self.addon_name, message, icon, 3000)

    def _clean_filename(self, name):
        if not name:
            return "download"
        safe = re.sub(r'[\:\\/*?"<>|]', '_', str(name))
        return safe.strip() or "download"

    def _path_exists(self, p):
        try:
            if xbmcvfs.exists(p):
                return True
        except Exception:
            pass
        try:
            return os.path.isdir(p) or os.path.exists(p)
        except Exception:
            return False

    def _listdir_safe(self, p):
        try:
            dirs, files = xbmcvfs.listdir(p)
            return files
        except Exception:
            try:
                return os.listdir(p)
            except Exception:
                return []

    def play(self, params):
        log.debug(f"PlayHandler.play() IN: {params}")
        tmdb_id_debug = params.get('tmdb_id')
        mediatype_debug = params.get('mediatype')
        log.info(f"Parametry přehrávání: tmdb_id={tmdb_id_debug}, mediatype={mediatype_debug}, ident={params.get('ident')}, name='{params.get('name')}'")

        # 1. Přímé URL (Trailer atd.)
        direct_url = params.get('url') or params.get('stream_url')
        if direct_url:
            log.info(f"Přímé URL nalezeno → setResolvedUrl: {direct_url}")
            li = xbmcgui.ListItem(path=direct_url)
            li.setProperty('IsPlayable', 'true')
            titulky_integration.apply_titulky_listitem_properties(
                li, params, self.addon, self.profile
            )
            titulky_integration.publish_auto_job_if_enabled(
                params, self.addon, self.profile
            )
            xbmcplugin.setResolvedUrl(self.handle, True, li)
            return

        # 2. Lokální nebo interaktivní výběr pro seriály
        if params.get('select') == '1' and params.get('series_name') and params.get('season') and params.get('episode'):
            if self._handle_series_selection(params):
                return

        # 3. Webshare Stream Link
        token = self.ws.revalidate()
        log.info(f"Webshare token check: {'<NONE>' if not token else '<OK>'}")
        if not token:
            self._popinfo("Pro přehrávání je nutné přihlášení k Webshare.", icon=xbmcgui.NOTIFICATION_WARNING)
            xbmcplugin.setResolvedUrl(self.handle, False, xbmcgui.ListItem())
            return

        ident = params.get('ident')
        name = params.get('name', '')
        if not ident:
            self._popinfo("Chybí identifikátor streamu.", icon=xbmcgui.NOTIFICATION_WARNING)
            xbmcplugin.setResolvedUrl(self.handle, False, xbmcgui.ListItem())
            return

        link = self.ws.getlink(ident)
        if not link:
            # Toast zobrazuje už WebshareClient přes _popinfo pokud getlink selže
            xbmcplugin.setResolvedUrl(self.handle, False, xbmcgui.ListItem())
            return

        # 4. Sestavení finálního URL s headery
        headers = {'Cookie': 'wst=' + (token or ''), 'User-Agent': self.ua, 'Referer': self.base}
        link_with_headers = f"{link}|{urlencode(headers)}"
        
        if params.get('tmdb_id') and params.get('mediatype'):
            extra_params = urlencode({'tmdb_id': params['tmdb_id'], 'mediatype': params['mediatype']})
            link_with_headers = f"{link_with_headers}&{extra_params}"
            
        log.info(f"Finální stream URL připraveno.")

        # 5. ListItem příprava
        li = xbmcgui.ListItem(label=name, path=link_with_headers)
        li.setProperty('IsPlayable', 'true')
        li.setProperty('mimetype', 'application/octet-stream')
        
        tmdb_id = params.get('tmdb_id')
        mediatype = params.get('mediatype')
        
        if tmdb_id and mediatype:
            li.setProperty('tmdb_id', str(tmdb_id))
            li.setProperty('mediatype', mediatype)
            log.info(f"Scrobbling metadata nastavena: tmdb_id={tmdb_id}, mediatype={mediatype}")

        titulky_integration.apply_titulky_listitem_properties(
            li, params, self.addon, self.profile
        )
        titulky_integration.publish_auto_job_if_enabled(
            params, self.addon, self.profile
        )

        # 6. Scrobbling start (Epizoda)
        if tmdb_id and mediatype == 'episode':
            self._start_episode_scrobble(params, link_with_headers)

        xbmcplugin.setResolvedUrl(self.handle, True, li)

        # 7. Scrobbling start (Film) + Lokální Historie
        if tmdb_id and mediatype == 'movie':
            self._start_movie_scrobble(tmdb_id, link_with_headers)

        # 8. Záložní zapálení přehrávače
        time.sleep(0.2)
        if not xbmc.Player().isPlaying():
            log.warning("setResolvedUrl nezapálilo, zkouším záložní Player().play(...)")
            xbmc.Player().play(item=link_with_headers, listitem=li)

    def _handle_series_selection(self, params):
        """Zpracuje vyhledání lokálního souboru nebo výběr streamu pro epizodu."""
        try:
            series_name = params.get('series_name')
            season = str(params.get('season'))
            episode = str(params.get('episode'))
            sm = series_manager.SeriesManager(self.addon, self.profile)
            series_data = sm.load_series_data(series_name) or {}

            # A) Zkusit najít lokální soubor
            candidate_path = None
            dfolder = self.addon.getSetting('dfolder') or ''
            if dfolder:
                base_real = xbmcvfs.translatePath(dfolder)
                folder_candidates = [self._clean_filename(series_name)]
                display_name = (series_data.get('name') or '').strip()
                if display_name:
                    display_folder = self._clean_filename(display_name)
                    if display_folder not in folder_candidates:
                        folder_candidates.append(display_folder)

                for folder_name in folder_candidates:
                    season_folder = os.path.normpath(os.path.join(base_real, folder_name, f"Sezona {season}"))
                    if not self._path_exists(season_folder):
                        continue
                    candidate_path = self._find_best_local_file(series_name, season, episode, season_folder)
                    if candidate_path:
                        log.info(f"Nalezen lokální soubor: {candidate_path}")
                        break

            # B) Načíst uložené streamy epizody
            streams = series_manager.get_episode_streams(sm, series_name, season, episode)

            # C) Pokud máme offline i streamy, nabídnout volbu (jen když je vyžádána parametrem)
            want_offline_choice = str(params.get('offline_choice', '0')) == '1'
            if candidate_path and streams and want_offline_choice:
                options = [
                    "Přehrát offline",
                    "Vybrat uložený stream",
                ]
                try:
                    choice = xbmcgui.Dialog().select(
                        f"S{int(season):02d}E{int(episode):02d} • Zdroj přehrání",
                        options,
                        preselect=0,
                    )
                except TypeError:
                    choice = xbmcgui.Dialog().select(
                        f"S{int(season):02d}E{int(episode):02d} • Zdroj přehrání",
                        options,
                    )

                if choice < 0:
                    xbmcplugin.setResolvedUrl(self.handle, False, xbmcgui.ListItem())
                    return True
                if choice == 0:
                    return self._play_local_file(candidate_path, series_name, season, episode)
                # choice == 1 -> pokračujeme do výběru streamu níže

            # D) Fallback: jen offline
            if candidate_path and not streams:
                return self._play_local_file(candidate_path, series_name, season, episode)

            # E) Legacy fallback: offline i streamy, ale dialog nebyl vyžádán
            if candidate_path and streams and not want_offline_choice:
                return self._play_local_file(candidate_path, series_name, season, episode)

            # F) Není co přehrát
            if not streams:
                self._popinfo('Pro tuto epizodu nejsou k dispozici streamy.', icon=xbmcgui.NOTIFICATION_WARNING)
                xbmcplugin.setResolvedUrl(self.handle, False, xbmcgui.ListItem())
                return True

            # G) Výběr streamu
            # Řazení: streamy s CZdab nebo CZtit nahoru
            from resources.lib.series.series_helpers import get_stream_languages
            def sort_key(st):
                langs = get_stream_languages(st)
                has_cz = any(l in ('CZdab', 'CZtit') for l in langs)
                # False (0) přijde před True (1) při vzestupném řazení,
                # my chceme CZ (True) na začátku -> použijeme (not has_cz, ...)
                # Sekundární řazení podle velikosti (větší dříve)
                return (not has_cz, -int(st.get('size', 0)))

            streams = sorted(streams, key=sort_key)

            ep_runtime = self._get_episode_runtime(sm, series_name, season, episode)
            items, labels = [], []
            for st in streams:
                ln1, ln2 = series_manager.format_stream_two_lines(st, runtime=ep_runtime)
                items.append(xbmcgui.ListItem(label=ln1, label2=ln2))
                labels.append(f"{ln1} — {ln2}")
            
            title = f"Vyberte stream · S{int(season):02d}E{int(episode):02d}"
            try:
                idx = xbmcgui.Dialog().select(title, items, useDetails=True)
            except TypeError:
                idx = xbmcgui.Dialog().select(title, labels)
            
            if idx < 0:
                xbmcplugin.setResolvedUrl(self.handle, False, xbmcgui.ListItem())
                return True
            
            # Aktualizace params pro Webshare link flow v hlavním play()
            params['ident'] = streams[idx].get('ident')
            if not params.get('name'):
                params['name'] = streams[idx].get('name') or title
                
            # Přidání TMDB ID epizody pro scrobble
            ep_meta = series_data.get('seasons', {}).get(str(season), {}).get(str(episode), {}).get('tmdb', {})
            tmdb_ep_id = ep_meta.get('episode_id')
            if tmdb_ep_id:
                params['tmdb_id'] = str(tmdb_ep_id)
            elif not params.get('tmdb_id'):
                # Fallback na TMDb ID seriálu, pokud epizoda nemá `episode_id`.
                tmdb_show_id = (series_data.get('tmdb') or {}).get('id')
                if tmdb_show_id:
                    params['tmdb_id'] = str(tmdb_show_id)
            params['mediatype'] = 'episode'
            
            return False # Pokračovat k Webshare link flow

        except Exception as e:
            log.error(f"Chyba v _handle_series_selection: {e}")
            xbmcplugin.setResolvedUrl(self.handle, False, xbmcgui.ListItem())
            return True

    def _find_best_local_file(self, series_name, season, episode, folder):
        try:
            sm = series_manager.SeriesManager(self.addon, self.profile)
            data = sm.load_series_data(series_name) or {}
            ep = data.get('seasons', {}).get(season, {}).get(episode, {})
            ep_title = ep.get('name') or ep.get('title') or ep.get('source_name') or ''
            
            expected_bases = []
            if ep_title:
                expected_bases.append(self._clean_filename(ep_title))
            try:
                s_i = int(season); e_i = int(episode)
                expected_bases.append(f"S{s_i:02d}E{e_i:02d}")
            except Exception:
                expected_bases.append(f"S{season}E{episode}")
            
            files = self._listdir_safe(folder)
            candidate_path = None
            candidate_size = -1
            
            for fname in files:
                base_noext = os.path.splitext(fname)[0]
                for b in expected_bases:
                    if base_noext == b or base_noext.lower().startswith(b.lower()):
                        full_path = os.path.join(folder, fname)
                        sz = self._get_file_size(full_path)
                        if sz > candidate_size:
                            candidate_path = full_path
                            candidate_size = sz
                        break
            return candidate_path
        except Exception:
            return None

    def _get_file_size(self, path):
        try:
            st = xbmcvfs.Stat(path)
            return st.st_size() if hasattr(st, 'st_size') and callable(st.st_size) else 0
        except Exception:
            try:
                return os.path.getsize(path)
            except Exception:
                return 0

    def _play_local_file(self, path, series_name, season, episode):
        play_item = xbmcgui.ListItem(path=path)
        play_item.setProperty('IsPlayable', 'true')
        
        # Metadata pro scrobble
        try:
            sm = series_manager.SeriesManager(self.addon, self.profile)
            data = sm.load_series_data(series_name) or {}
            ep = data.get('seasons', {}).get(season, {}).get(episode, {})
            tmdb_id = ep.get('tmdb', {}).get('episode_id')
            if tmdb_id:
                play_item.setProperty('tmdb_id', str(tmdb_id))
                play_item.setProperty('mediatype', 'episode')
            
            ext = os.path.splitext(path)[1].lower()
            mime_map = {'.mkv': 'video/x-matroska', '.mp4': 'video/mp4', '.avi': 'video/x-msvideo', '.m4v': 'video/mp4', '.ts': 'video/MP2T'}
            if ext in mime_map:
                play_item.setProperty('mimetype', mime_map[ext])
            
            # Start scrobble
            from resources.lib.history.scrobble import build_episode_payload_from_db, lock_payload, scrobble_start
            payload = build_episode_payload_from_db(series_name, int(season), int(episode))
            if payload:
                lock_payload(payload, last_stream_url=path, init_progress=0.0)
                scrobble_start(item=payload)
        except Exception as e:
            log.error(f"Chyba při přípravě offline scrobblingu: {e}")

        local_params = {
            'series_name': series_name,
            'season': season,
            'episode': episode,
            'mediatype': 'episode',
            'name': os.path.basename(path),
        }
        titulky_integration.apply_titulky_listitem_properties(
            play_item, local_params, self.addon, self.profile
        )
        titulky_integration.publish_auto_job_if_enabled(
            local_params, self.addon, self.profile
        )

        xbmcplugin.setResolvedUrl(self.handle, True, play_item)
        time.sleep(0.2)
        if not xbmc.Player().isPlaying():
            xbmc.Player().play(item=path, listitem=play_item)
        return True

    def _get_episode_runtime(self, sm, series_name, season, episode):
        try:
            data = sm.load_series_data(series_name) or {}
            ep_meta = data.get('seasons', {}).get(str(season), {}).get(str(episode), {}).get('tmdb', {})
            rt = ep_meta.get('runtime')
            return rt if isinstance(rt, int) and rt > 0 else None
        except Exception:
            return None

    def _start_episode_scrobble(self, params, url):
        try:
            series_name = params.get('series_name')
            season = params.get('season')
            episode = params.get('episode')
            if series_name and season and episode:
                from resources.lib.history.scrobble import build_episode_payload_from_db, lock_payload, scrobble_start
                payload = build_episode_payload_from_db(series_name, int(season), int(episode))
                if payload:
                    lock_payload(payload, last_stream_url=url, init_progress=0.0)
                    scrobble_start(item=payload)
                    log.info("Epizoda scrobbling zahájen.")
        except Exception as e:
            log.error(f"Chyba při zahájení scrobblingu epizody: {e}")

    def _start_movie_scrobble(self, tmdb_id, url):
        try:
            from resources.lib.history.scrobble import build_payload_from_id, lock_payload, scrobble_start
            payload = build_payload_from_id(tmdb_id, 'movie')
            if payload:
                # Uzamknout payload přes čerstvou instanci (lock_payload to řeší)
                lock_payload(payload, last_stream_url=url, init_progress=0.0)
                
                # Uložit do lokální historie
                clean_title = (payload.get('movie', {}).get('title') or 'Neznámý film')
                item_for_local_db = {
                    "name": clean_title,
                    "title": clean_title,
                    "media_type": "movie",
                    "tmdb_id": str(tmdb_id),
                    "progress": 0.0,
                    "watched_at": datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S'),
                    "stream_url": url,
                    "trakt_payload": payload,
                    "movie": payload.get('movie'),
                    "trakt_ok": None,
                }
                local_history.store_item(item_for_local_db)
                log.info(f'Film "{clean_title}" uložen do historie a scrobbling zahájen.')
                scrobble_start(item=payload)
        except Exception as e:
            log.error(f"Chyba při zahájení scrobblingu filmu: {e}")
