import os
import socket
import sys
import traceback
import urllib.error
import urllib.request

import xbmc
import xbmcaddon
import xbmcgui
import xbmcplugin
import xbmcvfs

import gb_common as common  # Gemeinsame Variablen/Funktionen


LOGP = f"[{getattr(common, 'addon_id', 'plugin.video.glotzbox')} settings.py]"
WATCHED_STATUS_MOVIES_FILE = "watched_status_movies.json"
WATCHED_STATUS_SERIES_FILE = "watched_status_series.json"


def _log(msg, level=xbmc.LOGINFO):
    try:
        xbmc.log(f"{LOGP} {msg}", level)
    except Exception:
        pass


def _log_debug_exc(context, exc):
    try:
        _log(f"{context}: {exc}", xbmc.LOGDEBUG)
    except Exception:
        pass


def _safe_end_of_directory():
    try:
        h = int(getattr(common, "addon_handle", -1))
        if h >= 0:
            xbmcplugin.endOfDirectory(h)
    except Exception as e:
        _log_debug_exc("endOfDirectory skipped after exception", e)


def _resolve_addon_context():
    addon_name = getattr(common, "addon_name", "GlotzBox")
    addon_data_dir = getattr(common, "addon_data_dir", "")
    addon_handle = -1

    try:
        h = int(getattr(common, "addon_handle", -1))
        if h >= 0:
            addon_handle = h
    except Exception as e:
        _log_debug_exc("could not parse common.addon_handle", e)

    try:
        addon_obj = xbmcaddon.Addon()
        addon_name = addon_obj.getAddonInfo("name") or addon_name
        profile_dir = xbmcvfs.translatePath(addon_obj.getAddonInfo("profile"))
        if profile_dir:
            addon_data_dir = profile_dir
    except Exception as e:
        _log_debug_exc("could not resolve addon profile from xbmcaddon.Addon()", e)

    try:
        if addon_data_dir and not xbmcvfs.exists(addon_data_dir):
            xbmcvfs.mkdirs(addon_data_dir)
            _log(f"Addon-Datenverzeichnis erstellt: {addon_data_dir}", xbmc.LOGINFO)
    except Exception as e:
        _log_debug_exc(f"could not ensure addon data dir '{addon_data_dir}'", e)

    try:
        if len(sys.argv) >= 2:
            h = int(sys.argv[1])
            if h >= 0:
                addon_handle = h
    except Exception as e:
        _log_debug_exc("could not parse addon handle from sys.argv", e)

    try:
        common.addon_handle = addon_handle
    except Exception as e:
        _log_debug_exc("failed to sync common.addon_handle", e)

    return addon_name, addon_data_dir, addon_handle


def verbindungscheck():
    """
    Testet die Remote-Verbindung mit derselben Host-/Pfad-Logik wie die übrigen Module.
    Nutzt bevorzugt HEAD (schnell), bei 405/501 Fallback auf GET.
    """
    try:
        host, base_path, scheme = common.normalize_ftp_host_basepath()
        if not host:
            xbmcgui.Dialog().notification("Fehler", "Host fehlt!", xbmcgui.NOTIFICATION_ERROR)
            _safe_end_of_directory()
            return

        base_path = (base_path or "/user/downloads/kodiAddon/").rstrip("/") + "/"
        probe_files = ("kodiMovie.json", "kodiSeries.json", "seriesFav.json")
        probe_urls = [f"{scheme}://{host}{base_path}{fn}" for fn in probe_files]

        ssl_ctx = None
        try:
            if hasattr(common, "get_ssl_context"):
                ssl_ctx = common.get_ssl_context()
        except Exception as e:
            _log_debug_exc("verbindungscheck: could not get shared ssl context", e)
            ssl_ctx = None

        last_error = None

        for url in probe_urls:
            try:
                req = common.get_authenticated_request(url)
                if not req:
                    continue
                try:
                    req.get_method = lambda: "HEAD"
                except Exception as e:
                    _log_debug_exc("verbindungscheck: could not force HEAD method", e)

                with urllib.request.urlopen(req, context=ssl_ctx, timeout=15) as response:
                    status = int(response.getcode() or 0)
                if 200 <= status < 400:
                    xbmcgui.Dialog().notification("Verbindung OK", "Verbindungscheck erfolgreich", xbmcgui.NOTIFICATION_INFO)
                    _safe_end_of_directory()
                    return
            except urllib.error.HTTPError as e:
                # Manche Server erlauben HEAD nicht -> GET-Fallback.
                if int(getattr(e, "code", 0)) in (405, 501):
                    try:
                        req_get = common.get_authenticated_request(url)
                        if req_get:
                            req_get.add_header("Range", "bytes=0-0")
                            with urllib.request.urlopen(req_get, context=ssl_ctx, timeout=15) as response:
                                status = int(response.getcode() or 0)
                            if 200 <= status < 400:
                                xbmcgui.Dialog().notification("Verbindung OK", "Verbindungscheck erfolgreich", xbmcgui.NOTIFICATION_INFO)
                                _safe_end_of_directory()
                                return
                    except Exception as e_get:
                        _log_debug_exc(f"verbindungscheck: GET fallback failed for {common.mask_url(url)}", e_get)
                        last_error = e_get
                        continue
                last_error = e
            except Exception as e:
                last_error = e

        if last_error:
            raise last_error
        raise Exception("Kein Test-Endpunkt erreichbar.")
    except urllib.error.HTTPError as e:
        error_msg = f"HTTP-Fehler: {e.code} {e.reason}"
        _log(error_msg, xbmc.LOGERROR)
        xbmcgui.Dialog().notification("HTTP-Fehler", error_msg, xbmcgui.NOTIFICATION_ERROR)
        _safe_end_of_directory()
    except socket.timeout:
        error_msg = "Timeout: Server antwortet nicht."
        _log(error_msg, xbmc.LOGERROR)
        xbmcgui.Dialog().notification("Timeout", error_msg, xbmcgui.NOTIFICATION_ERROR)
        _safe_end_of_directory()
    except urllib.error.URLError as e:
        reason = getattr(e, "reason", e)
        if isinstance(reason, socket.timeout):
            error_msg = "Timeout: Server antwortet nicht."
        else:
            error_msg = f"URL-Fehler: {str(reason)}"
        _log(error_msg, xbmc.LOGERROR)
        title = "Timeout" if "Timeout:" in error_msg else "URL-Fehler"
        xbmcgui.Dialog().notification(title, error_msg, xbmcgui.NOTIFICATION_ERROR)
        _safe_end_of_directory()
    except Exception as e:
        error_msg = f"Unbekannter Fehler: {str(e)}"
        _log(error_msg, xbmc.LOGERROR)
        xbmcgui.Dialog().notification("Kritischer Fehler", error_msg, xbmcgui.NOTIFICATION_ERROR)
        _safe_end_of_directory()


def clear_cache():
    """
    Löscht die *relevanten* Cache-/Meta-Dateien des Addons.

    WICHTIG: Downloads (downloaded_*.json) und watched_status_* bleiben erhalten.
    """
    try:
        base = getattr(common, "addon_data_dir", "") or xbmcvfs.translatePath(f"special://home/userdata/addon_data/{common.addon_id}/")
        base = xbmcvfs.translatePath(base) if base else ""
        if not base:
            xbmcgui.Dialog().notification("Cache leeren", "Addon-Datenverzeichnis nicht gefunden.", xbmcgui.NOTIFICATION_ERROR)
            return

        cache_dirs = [
            "cache",
            "cache_movies_images",
            "cache_series_images",
            "cache_fav_series_images",
            "cache_series_details_json",
            "cache_series_details_json_meta",
        ]

        cache_files = [
            "movies.cache.json",
            "series.cache.json",
            "seriesFav.cache.json",
            "movies_remote_meta.json",
            "series_remote_meta.json",
            "seriesFav.remote_meta.json",
            "kodiMovie.json.meta.json",
            "kodiSeries.json.meta.json",
            "seriesFav.json.meta.json",
            "details_sha1_map.json",
            "series_seen_urls.json",
            "details_cache_schema.txt",
            "series_details_meta.json",
        ]

        def _delete_file(fp: str):
            try:
                if fp and xbmcvfs.exists(fp):
                    if not xbmcvfs.delete(fp):
                        _log(f"clear_cache: xbmcvfs.delete returned False for {fp}", xbmc.LOGDEBUG)
                    return
            except Exception as e:
                _log_debug_exc(f"clear_cache: xbmcvfs.delete failed for {fp}", e)
            try:
                if fp and os.path.exists(fp):
                    os.remove(fp)
            except Exception as e:
                _log_debug_exc(f"clear_cache: os.remove failed for {fp}", e)

        def _delete_tree(path: str):
            try:
                if not path:
                    return
                if xbmcvfs.exists(path):
                    dirs, files = xbmcvfs.listdir(path)
                    for f_name in files:
                        _delete_file(os.path.join(path, f_name))
                    for d_name in dirs:
                        sub = os.path.join(path, d_name)
                        _delete_tree(sub)
                        try:
                            if not xbmcvfs.rmdir(sub):
                                _log(f"clear_cache: xbmcvfs.rmdir returned False for {sub}", xbmc.LOGDEBUG)
                        except Exception as e:
                            _log_debug_exc(f"clear_cache: xbmcvfs.rmdir failed for {sub}", e)
                    try:
                        if not xbmcvfs.rmdir(path):
                            _log(f"clear_cache: xbmcvfs.rmdir returned False for {path}", xbmc.LOGDEBUG)
                    except Exception as e:
                        _log_debug_exc(f"clear_cache: xbmcvfs.rmdir failed for {path}", e)
                    return
            except Exception as e:
                _log_debug_exc(f"clear_cache: recursive VFS delete failed for {path}", e)

            try:
                if os.path.isdir(path):
                    import shutil
                    shutil.rmtree(path, ignore_errors=True)
            except Exception as e:
                _log_debug_exc(f"clear_cache: shutil.rmtree failed for {path}", e)

        for d_name in cache_dirs:
            _delete_tree(os.path.join(base, d_name))

        for f_name in cache_files:
            _delete_file(os.path.join(base, f_name))

        _log(f"Cache/Meta bereinigt unter: {base}", xbmc.LOGINFO)
        xbmcgui.Dialog().notification("Cache gelöscht", "Cache/Meta wurden erfolgreich gelöscht!", xbmcgui.NOTIFICATION_INFO)
        try:
            if common.addon_handle >= 0:
                xbmcplugin.endOfDirectory(common.addon_handle, succeeded=True, cacheToDisc=False)
        except Exception:
            pass

    except Exception as e:
        _log(f"Fehler beim Cache löschen: {e}", xbmc.LOGERROR)
        xbmcgui.Dialog().notification(
            "Cache leeren",
            "Fehler beim Cache-Loeschen. Details im Kodi-Log.",
            xbmcgui.NOTIFICATION_ERROR,
        )
        try:
            if common.addon_handle >= 0:
                xbmcplugin.endOfDirectory(common.addon_handle, succeeded=False)
        except Exception:
            pass


def _reset_watched_status_file(watched_filename: str):
    addon_name, addon_data_dir, addon_handle = _resolve_addon_context()

    if not addon_data_dir:
        xbmcgui.Dialog().notification(
            "Fehler",
            "Addon-Pfad nicht gefunden.",
            xbmcgui.NOTIFICATION_ERROR,
            5000,
        )
        _safe_end_of_directory()
        return

    watched_status_file = os.path.join(addon_data_dir, watched_filename)
    _log(f"[{addon_name}] Prüfe/Lösche Gesehen-Status Datei: {watched_status_file}", xbmc.LOGINFO)

    try:
        exists = False
        try:
            exists = bool(xbmcvfs.exists(watched_status_file))
        except Exception as e:
            _log_debug_exc(f"[{addon_name}] xbmcvfs.exists failed for {watched_status_file}", e)
            exists = bool(os.path.exists(watched_status_file))

        if exists:
            deleted = False
            try:
                deleted = bool(xbmcvfs.delete(watched_status_file))
            except Exception as e:
                _log_debug_exc(f"[{addon_name}] xbmcvfs.delete failed for {watched_status_file}", e)

            if not deleted:
                try:
                    if os.path.exists(watched_status_file):
                        os.remove(watched_status_file)
                        deleted = True
                except Exception as e:
                    _log_debug_exc(f"[{addon_name}] os.remove failed for {watched_status_file}", e)

            if deleted:
                _log(f"[{addon_name}] Gesehen-Status-Datei erfolgreich gelöscht.", xbmc.LOGINFO)
                xbmcgui.Dialog().notification(
                    addon_name,
                    "Gesehen-Status wurde zurückgesetzt.",
                    xbmcgui.NOTIFICATION_INFO,
                    3000,
                )
            else:
                _log(f"[{addon_name}] Fehler: Konnte existierende Datei nicht löschen: {watched_status_file}", xbmc.LOGERROR)
                xbmcgui.Dialog().notification(
                    f"{addon_name} - Fehler",
                    "Datei gefunden, aber Löschen fehlgeschlagen.",
                    xbmcgui.NOTIFICATION_ERROR,
                    5000,
                )
        else:
            _log(f"[{addon_name}] Gesehen-Status-Datei nicht gefunden (Status war bereits zurückgesetzt).", xbmc.LOGINFO)
            xbmcgui.Dialog().notification(
                addon_name,
                "Nichts zu löschen!",
                xbmcgui.NOTIFICATION_INFO,
                3000,
            )

    except Exception as e:
        _log(f"[{addon_name}] Fehler beim Prüfen/Löschen der Gesehen-Status-Datei: {e}", xbmc.LOGERROR)
        _log(traceback.format_exc(), xbmc.LOGERROR)
        xbmcgui.Dialog().notification(
            f"{addon_name} - Fehler",
            "Fehler beim Zuruecksetzen. Details im Kodi-Log.",
            xbmcgui.NOTIFICATION_ERROR,
            5000,
        )

    if addon_handle != -1:
        _log(f"[{addon_name}] Aufruf von endOfDirectory mit Handle: {addon_handle}", xbmc.LOGDEBUG)
    else:
        _log(f"[{addon_name}] Kein gültiges Addon-Handle für endOfDirectory.", xbmc.LOGWARNING)
    _safe_end_of_directory()


def reset_watched_status_movie():
    _reset_watched_status_file(WATCHED_STATUS_MOVIES_FILE)


def reset_watched_status_series():
    _reset_watched_status_file(WATCHED_STATUS_SERIES_FILE)
