# default.py
# coding: utf-8

import sys
import os
import json
import urllib.parse
import traceback

import xbmc
import xbmcgui
import xbmcplugin
import xbmcaddon
import xbmcvfs

import common  # Gemeinsame Einstellungen und Funktionen

# ---- Addon / Pfade ----------------------------------------------------------
addon       = common.addon
addon_path  = common.addon_path
error_icon  = addon.getAddonInfo("icon")

def _p(*parts):
    return os.path.join(addon_path, *parts)

def _fallback(path, fallback):
    return path if path and xbmcvfs.exists(path) else fallback

filme_icon     = _fallback(_p("movies.png"), error_icon)
serien_icon    = _fallback(_p("series.png"), error_icon)
favoriten_icon = _fallback(_p("favorites.png"), error_icon)
fanart_image   = _fallback(_p("fanart.png"), addon.getAddonInfo("fanart") or error_icon)

# ---- externe Module laden (resources/lib) -----------------------------------
try:
    lib_path = os.path.join(addon_path, 'resources', 'lib')
    if lib_path not in sys.path:
        sys.path.append(lib_path)
    import favorites
    import movies
    import series
    import settings
except ImportError as e:
    xbmc.log(f"[{common.addon_id}] FEHLER: Bibliotheken konnten nicht importiert werden: {e}", xbmc.LOGERROR)
    xbmcgui.Dialog().notification("Addon Fehler", "Wichtige Bibliotheken fehlen!", error_icon)
    sys.exit(1)
except Exception as e:
    xbmc.log(f"[{common.addon_id}] Unerwarteter Fehler beim Importieren: {e}", xbmc.LOGERROR)
    xbmc.log(traceback.format_exc(), xbmc.LOGERROR)
    xbmcgui.Dialog().notification("Addon Fehler", "Unerwarteter Init-Fehler!", error_icon)
    sys.exit(1)

# ---- Helpers ----------------------------------------------------------------
LOGP = f"[{common.addon_id} default.py]"

def log(msg, lvl=xbmc.LOGINFO):
    # INFO, damit du es im normalen kodi.log siehst
    try:
        xbmc.log(f"{LOGP} {msg}", lvl)
    except Exception:
        pass

def notify(title, message, icon=error_icon, time=3000, sound=False):
    xbmcgui.Dialog().notification(title, message, icon, time, sound)

def parse_params():
    """Liest Plugin-Query (?a=1&b=2) in ein Dict."""
    if len(sys.argv) > 2 and sys.argv[2].startswith('?'):
        return dict(urllib.parse.parse_qsl(sys.argv[2][1:], keep_blank_values=True))
    return {}

def end_dir_fail():
    try:
        if common.addon_handle >= 0:
            xbmcplugin.endOfDirectory(common.addon_handle, succeeded=False)
    except Exception:
        pass

def end_dir_ok(cache=False):
    """Sauberer Abschluss, auch wenn handle=-1 (dann wird gefallbackt/geskippt)."""
    try:
        if common.addon_handle >= 0:
            xbmcplugin.endOfDirectory(common.addon_handle, succeeded=True, cacheToDisc=cache)
    except Exception:
        pass

def require(params, keys, msg):
    """AND-Logik für Pflichtparameter."""
    if isinstance(keys, (list, tuple)):
        ok = all(params.get(k) not in (None, "") for k in keys)
    else:
        ok = params.get(keys) not in (None, "")
    if not ok:
        notify("Fehler", msg)
        end_dir_fail()
    return ok

def require_any(params, keys, msg):
    """OR-Logik: mindestens ein Key muss existieren."""
    ok = any(params.get(k) not in (None, "") for k in keys)
    if not ok:
        notify("Fehler", msg)
        end_dir_fail()
    return ok

def pick_first(params, keys, default=None):
    for k in keys:
        v = params.get(k)
        if v not in (None, ""):
            return v
    return default

def _short(s, n=160):
    if s is None:
        return "None"
    s = str(s)
    return s if len(s) <= n else (s[:n] + "...(cut)")

def _is_pid_payload(v):
    return isinstance(v, str) and v.strip().startswith("pid:")

# ---- WICHTIG: Fallback für RunPlugin/Handle=-1 ------------------------------
# Viele Random-Buttons/Skins rufen Addons via RunPlugin auf => sys.argv[1] == -1
# Wenn series/movies dann xbmcplugin.setResolvedUrl(-1, ...) nutzen, startet nix.
_ORIG_SETRESOLVED = xbmcplugin.setResolvedUrl
_ORIG_ENDDIR      = xbmcplugin.endOfDirectory

def _setResolvedUrl_fallback(handle, succeeded, listitem):
    try:
        h = int(handle) if handle is not None else -1
    except Exception:
        h = -1

    # Script/RunPlugin Mode => direkt abspielen
    if h < 0:
        try:
            path = ""
            try:
                path = listitem.getPath()
            except Exception:
                path = ""

            if not path:
                # ein paar mögliche Properties (falls gesetzt)
                for prop in ("path", "url", "stream_url"):
                    try:
                        v = listitem.getProperty(prop)
                        if v:
                            path = v
                            break
                    except Exception:
                        pass

            if path:
                log(f"Fallback setResolvedUrl(handle={h}) -> Player().play(path={_short(path)})")
                xbmc.Player().play(path, listitem)
                return True

            log("Fallback setResolvedUrl: handle<0 aber ListItem hat keinen Path!", xbmc.LOGERROR)
        except Exception as e:
            log(f"Fallback setResolvedUrl Exception: {e}", xbmc.LOGERROR)

    # normaler Kodi-Plugin-Flow
    return _ORIG_SETRESOLVED(handle, succeeded, listitem)

def _endOfDirectory_fallback(handle, succeeded=True, updateListing=False, cacheToDisc=True):
    try:
        h = int(handle)
    except Exception:
        h = -1
    if h < 0:
        # RunPlugin => kein Directory-Ende nötig
        log(f"Skip endOfDirectory(handle={h}) im RunPlugin-Modus")
        return True
    return _ORIG_ENDDIR(handle, succeeded, updateListing, cacheToDisc)

# Patch aktivieren (harmlos auch im Normalbetrieb)
xbmcplugin.setResolvedUrl = _setResolvedUrl_fallback
xbmcplugin.endOfDirectory = _endOfDirectory_fallback

# ---- Menü -------------------------------------------------------------------
def main_menu():
    try:
        xbmcplugin.setPluginCategory(common.addon_handle, common.addon_name)
        xbmcplugin.setContent(common.addon_handle, "addons")
    except Exception:
        pass

    li = xbmcgui.ListItem(label="Filme")
    li.setArt({'thumb': filme_icon, 'icon': filme_icon, 'poster': filme_icon, 'fanart': fanart_image})
    xbmcplugin.addDirectoryItem(handle=common.addon_handle, url=common.build_url({'action': A.SHOW_FILME}), listitem=li, isFolder=True)

    li = xbmcgui.ListItem(label="Serien")
    li.setArt({'thumb': serien_icon, 'icon': serien_icon, 'poster': serien_icon, 'fanart': fanart_image})
    xbmcplugin.addDirectoryItem(handle=common.addon_handle, url=common.build_url({'action': A.SHOW_SERIEN}), listitem=li, isFolder=True)

    li = xbmcgui.ListItem(label="Favoriten")
    li.setArt({'thumb': favoriten_icon, 'icon': favoriten_icon, 'poster': favoriten_icon, 'fanart': fanart_image})
    xbmcplugin.addDirectoryItem(handle=common.addon_handle, url=common.build_url({'action': A.SHOW_FAV}), listitem=li, isFolder=True)

    xbmcplugin.endOfDirectory(common.addon_handle, cacheToDisc=True)

# ---- Action Konstanten ------------------------------------------------------
class A:
    SHOW_FILME  = 'show_filme'
    SHOW_SERIEN = 'show_serien'
    SHOW_FAV    = 'show_favorites'

    SHOW_SEASONS   = 'show_seasons'
    SHOW_EPISODES  = 'show_episodes'
    SHOW_FAV_SEAS  = 'show_fav_seasons'
    SHOW_FAV_EPIS  = 'show_fav_episodes'

    PLAY_MOVIE     = 'play_movie'
    PLAY_SERIES    = 'play_series'
    PLAY_TRAILER   = 'play_trailer'

    TOGGLE_WATCHED_MOVIE  = 'toggle_watched_status'
    TOGGLE_WATCHED_SERIES = 'toggle_watched_status_series'

    DOWNLOAD_MOVIE  = 'download_movie'
    DOWNLOAD_SERIES = 'download_series'
    DELETE_MOVIE    = 'delete_local_movie'
    DELETE_SERIES   = 'delete_local_series'

    SETTINGS_UI     = 'addon_settings'
    TEST_CONN       = 'test_connection'
    RESET_W_MOVIE   = 'reset_watched_status_movie'
    RESET_W_SERIES  = 'reset_watched_status_series'
    CLEAR_CACHE     = 'clear_cache'

    # Random-Play
    PLAY_RANDOM_MOVIE            = 'play_random_movie'
    PLAY_RANDOM_SERIES_GLOBAL    = 'play_random_series_globally'
    PLAY_RANDOM_SERIES_THIS      = 'play_random_episode_from_this_series'
    PLAY_RANDOM_SERIES_SEASON    = 'play_random_episode_from_this_season'
    PLAY_RANDOM_FAV_SEASON       = 'play_random_favorite_episode_from_season'
    PLAY_RANDOM_FAV_SERIES       = 'play_random_favorite_series'
    PLAY_RANDOM_FAV_SERIES_THIS  = 'play_random_fav_episode_from_this_series'

    # ✅ NEU: AlarmClock/RunPlugin Hooks
    MAYBE_REFRESH_MOVIES = 'maybe_refresh_movies'
    INIT_BG_MOVIES       = 'init_bg_movies'

# ---- Dispatch-Helfer --------------------------------------------------------
def _call(func):
    def _inner(params):
        return func()
    return _inner

def _call_params(func):
    def _inner(params):
        return func(params)
    return _inner

def _call_with(keys, msg, func):
    def _inner(params):
        if not require(params, keys, msg):
            return
        return func(params)
    return _inner

def _call_with_any(keys_any, msg, func_with_value):
    def _inner(params):
        if not require_any(params, keys_any, msg):
            return
        val = pick_first(params, keys_any, default=None)
        log(f"Dispatch pick_first -> keyset={keys_any}, chosen_len={len(val) if isinstance(val,str) else -1}, preview={_short(val)}")
        return func_with_value(params, val)
    return _inner

# ---- Random aus Staffel: NICHT JSON-parsen, weil data oft pid:... ist -------
def handle_random_episode_from_season(params):
    # je nach Stelle kommt data/season_data/staffel_data
    raw = pick_first(params, ('season_data', 'staffel_data', 'data'), default=None)
    log(f"RandomSeason: raw_len={len(raw) if isinstance(raw,str) else -1}, raw_preview={_short(raw)}")

    if not raw:
        notify("Fehler", "Staffeldaten fehlen (Random Season).")
        end_dir_fail()
        return

    # Wichtig: raw so durchreichen wie es ist (pid:... oder JSON)
    return series.play_random_episode_from_this_season(raw)

# ✅ NEU: maybe_refresh_movies Hook (AlarmClock -> RunPlugin)
def handle_maybe_refresh_movies(params):
    try:
        req_ts = params.get("req_ts")
        reason = params.get("reason", "")
        log(f"maybe_refresh_movies: req_ts={req_ts} reason='{reason}'", xbmc.LOGINFO)

        # movies.py kann auch sys.argv parsen, aber wir geben es explizit rein:
        movies.maybe_refresh_movies(req_ts_param=req_ts, reason=reason)
    except Exception as e:
        log(f"maybe_refresh_movies failed: {e}\n{traceback.format_exc()}", xbmc.LOGWARNING)
    finally:
        end_dir_ok(cache=False)

# optional: einmaliger BG-Refresh (falls du das irgendwo triggerst)
def handle_init_bg_movies(params):
    try:
        log("init_bg_movies: background_refresh_movies_once()", xbmc.LOGINFO)
        movies.background_refresh_movies_once()
    except Exception as e:
        log(f"init_bg_movies failed: {e}\n{traceback.format_exc()}", xbmc.LOGWARNING)
    finally:
        end_dir_ok(cache=False)

# ---- Router (Dispatch-Tabelle) ----------------------------------------------
DISPATCH = {
    # Hauptmenüs
    A.SHOW_FILME:  _call(lambda: movies.check_movies_json()),
    A.SHOW_SERIEN: _call(lambda: series.check_series_json()),
    A.SHOW_FAV:    _call(lambda: favorites.show_favorite_series()),

    # Auflistungen (Serien/Favoriten)
    A.SHOW_SEASONS:  _call_with_any(('data', 'serie_data', 'series_data'),
                                    "Seriendaten für Staffeln fehlen.",
                                    lambda p, v: series.show_seasons(v)),
    A.SHOW_EPISODES: _call_with_any(('data', 'season_data', 'staffel_data'),
                                    "Staffeldaten für Episoden fehlen.",
                                    lambda p, v: series.show_episodes(v)),
    A.SHOW_FAV_SEAS: _call_with_any(('data', 'serie_data', 'series_data'),
                                    "Seriendaten für Favoriten-Staffeln fehlen.",
                                    lambda p, v: favorites.show_fav_seasons(v)),
    A.SHOW_FAV_EPIS: _call_with_any(('data', 'season_data', 'staffel_data'),
                                    "Staffeldaten für Favoriten-Episoden fehlen.",
                                    lambda p, v: favorites.show_fav_episodes(v)),

    # Random Play
    A.PLAY_RANDOM_MOVIE:         _call(lambda: movies.play_random_movie()),
    A.PLAY_RANDOM_SERIES_GLOBAL: _call(lambda: series.play_random_series_globally()),

    # Wichtig: wenn pid:... => Favoriten-Handler, sonst Serien-Handler
    A.PLAY_RANDOM_SERIES_THIS:   _call_with_any(('data', 'serie_data', 'series_data'),
                                    "Seriendaten für Zufallswiedergabe fehlen (Serien).",
                                    lambda p, v: favorites.play_random_episode_from_specific_series(v) if _is_pid_payload(v)
                                                else series.play_random_episode_from_this_series(v)),

    # Neuer Favoriten-Aktionsname (aus Favoriten-Staffelansicht)
    A.PLAY_RANDOM_FAV_SERIES_THIS: _call_with_any(('data', 'serie_data', 'series_data'),
                                    "Seriendaten für Zufallswiedergabe fehlen (Favoriten-Serie).",
                                    lambda p, v: favorites.play_random_episode_from_specific_series(v)),

    # Spezialhandler:
    A.PLAY_RANDOM_SERIES_SEASON: _call(lambda: None),

    A.PLAY_RANDOM_FAV_SEASON:    _call_with_any(('data', 'season_data', 'staffel_data'),
                                    "Staffeldaten für Zufallswiedergabe fehlen (Favoriten).",
                                    lambda p, v: favorites.play_random_episode_from_season(v)),
    A.PLAY_RANDOM_FAV_SERIES:    _call(lambda: favorites.play_random_favorite_series()),

    # Wiedergabe / Trailer
    A.PLAY_SERIES: _call_with('serie_key', "Episoden-Schlüssel für Wiedergabe fehlt.",
                    lambda p: series.play_series(p['serie_key'], p.get('title', "Episode wird geladen..."), p.get('tmdbid'))),

    # ✅ FIX: original_online_key an movies.play_movie weiterreichen
    A.PLAY_MOVIE:  _call_with('movie_key', "Film-Schlüssel für Wiedergabe fehlt.",
                    lambda p: movies.play_movie(
                        p['movie_key'],
                        p.get('title', "Film wird geladen..."),
                        original_online_key_param=p.get('original_online_key')
                    )),

    # Trailer: OR statt AND
    A.PLAY_TRAILER:_call_with_any(('trailer_url','trailer'),
                    "Trailer-URL fehlt.",
                    lambda p, v: movies.play_trailer(v)),

    # Watched-Status
    A.TOGGLE_WATCHED_MOVIE:  _call_with('movie_key', "Film-Schlüssel für Statusänderung fehlt.",
                            lambda p: movies.toggle_watched_status(p['movie_key'])),
    A.TOGGLE_WATCHED_SERIES: _call_with('serie_key', "Serien/Episoden-Schlüssel für Statusänderung fehlt.",
                            lambda p: series.toggle_watched_status(p['serie_key'])),

    # Download / Delete
    A.DOWNLOAD_MOVIE:  _call_with(['movie_key', 'data'], "Film-Infos für Download fehlen.",
                        lambda p: movies.download_movie(p['movie_key'], p.get('title', "Film herunterladen"), p['data'])),
    A.DOWNLOAD_SERIES: _call_with(['serie_key', 'data'], "Serien-Infos für Download fehlen.",
                        lambda p: series.download_series(p['serie_key'], p.get('title', "Serie herunterladen"),
                                                         p['data'], p.get('mode', 'serie'), p.get('season'), p.get('episode'))),
    A.DELETE_MOVIE:    _call_with('movie_key', "Film-Schlüssel zum Löschen fehlt.",
                        lambda p: movies.delete_local_movie(p['movie_key'], p.get('title', "Film löschen"))),
    A.DELETE_SERIES:   _call_with('serie_key', "Serien-Schlüssel zum Löschen fehlt.",
                        lambda p: series.delete_local_series(p['serie_key'], p.get('title', "Serie löschen"))),

    # Settings
    A.SETTINGS_UI:   _call(lambda: xbmc.executebuiltin(f"Addon.OpenSettings({common.addon_id})")),
    A.TEST_CONN:     _call(lambda: settings.verbindungscheck()),
    A.RESET_W_MOVIE: _call(lambda: settings.reset_watched_status_movie()),
    A.RESET_W_SERIES:_call(lambda: settings.reset_watched_status_series()),
    A.CLEAR_CACHE:   _call(lambda: settings.clear_cache()),

    # ✅ NEU: AlarmClock/RunPlugin Hooks
    A.MAYBE_REFRESH_MOVIES: _call_params(handle_maybe_refresh_movies),
    A.INIT_BG_MOVIES:       _call_params(handle_init_bg_movies),
}

# ---- Entry ------------------------------------------------------------------
if __name__ == '__main__':
    try:
        # Handle bestimmen:
        current_handle = common.addon_handle

        raw_handle = sys.argv[1] if len(sys.argv) > 1 else None
        parsed_handle = None
        if isinstance(raw_handle, str) and raw_handle.lstrip("-").isdigit():
            try:
                parsed_handle = int(raw_handle)
            except Exception:
                parsed_handle = None

        if parsed_handle is not None:
            if parsed_handle >= 0:
                current_handle = parsed_handle
                common.addon_handle = parsed_handle
            else:
                log(f"Hinweis: Handle '{raw_handle}' → Script/RunPlugin-Modus (verwende {common.addon_handle}).")
        else:
            log(f"Hinweis: sys.argv[1]='{raw_handle}' ist kein numerischer Handle. Verwende {common.addon_handle}.")

        params = parse_params()
        action = (params.get('action') or '').strip()

        keys = sorted(list(params.keys()))
        lens = {k: (len(params.get(k)) if isinstance(params.get(k), str) else -1) for k in keys}
        log(f"Router: Action='{action}', keys={keys}, lens={lens}")

        if not action:
            main_menu()
        else:
            if action == A.PLAY_RANDOM_SERIES_SEASON:
                handle_random_episode_from_season(params)
            else:
                handler = DISPATCH.get(action)
                if handler:
                    handler(params)
                else:
                    log(f"Unbekannte Aktion '{action}' erhalten. Zeige Hauptmenü.", xbmc.LOGWARNING)
                    main_menu()

    except Exception as e:
        xbmc.log(f"[{common.addon_id}] Schwerwiegender Fehler im Haupt-Dispatcher: {e}", xbmc.LOGERROR)
        xbmc.log(traceback.format_exc(), xbmc.LOGERROR)
        notify(common.addon_name + " Fehler", f"Ein Fehler ist aufgetreten: {e}", error_icon, 5000)
        try:
            if common.addon_handle >= 0:
                xbmcplugin.endOfDirectory(common.addon_handle, succeeded=False)
        except Exception as e_end:
            xbmc.log(f"[{common.addon_id}] Fehler beim Beenden des Verzeichnisses nach Exception: {e_end}", xbmc.LOGERROR)
