#!/usr/bin/env python3
"""Plugin-Erkennung und -Verwaltung fuer ViewIT.

Dieses Modul laedt dynamisch alle Plugins aus dem `plugins/` Verzeichnis,
instanziiert sie und cached die Instanzen im RAM.
"""

from __future__ import annotations

import importlib.util
import inspect
import sys
from pathlib import Path
from types import ModuleType

try:  # pragma: no cover - Kodi runtime
    import xbmc  # type: ignore[import-not-found]
except ImportError:  # pragma: no cover
    xbmc = None

from plugin_interface import BasisPlugin

PLUGIN_DIR = Path(__file__).resolve().parent.parent / "plugins"
_PLUGIN_CACHE: dict[str, BasisPlugin] | None = None


def _log(message: str, level: int = 1) -> None:
    if xbmc is not None:
        xbmc.log(f"[ViewIt] {message}", level)


def import_plugin_module(path: Path) -> ModuleType:
    """Importiert eine einzelne Plugin-Datei als Python-Modul."""
    spec = importlib.util.spec_from_file_location(path.stem, path)
    if spec is None or spec.loader is None:
        raise ImportError(f"Modul-Spezifikation fuer {path.name} fehlt.")
    module = importlib.util.module_from_spec(spec)
    sys.modules[spec.name] = module
    try:
        spec.loader.exec_module(module)
    except Exception:
        sys.modules.pop(spec.name, None)
        raise
    return module


def discover_plugins() -> dict[str, BasisPlugin]:
    """Laedt alle Plugins aus `plugins/*.py` und cached Instanzen im RAM."""
    global _PLUGIN_CACHE
    if _PLUGIN_CACHE is not None:
        return _PLUGIN_CACHE

    plugins: dict[str, BasisPlugin] = {}
    if not PLUGIN_DIR.exists():
        _PLUGIN_CACHE = plugins
        return plugins

    for file_path in sorted(PLUGIN_DIR.glob("*.py")):
        if file_path.name.startswith("_"):
            continue
        try:
            module = import_plugin_module(file_path)
        except Exception as exc:
            _log(f"Plugin-Datei {file_path.name} konnte nicht geladen werden: {exc}", 2)
            continue

        preferred = getattr(module, "Plugin", None)
        if inspect.isclass(preferred) and issubclass(preferred, BasisPlugin) and preferred is not BasisPlugin:
            plugin_classes = [preferred]
        else:
            plugin_classes = [
                obj
                for obj in module.__dict__.values()
                if inspect.isclass(obj) and issubclass(obj, BasisPlugin) and obj is not BasisPlugin
            ]
            plugin_classes.sort(key=lambda cls: cls.__name__.casefold())

        for cls in plugin_classes:
            try:
                instance = cls()
            except Exception as exc:
                _log(f"Plugin {cls.__name__} konnte nicht geladen werden: {exc}", 2)
                continue
            if getattr(instance, "is_available", True) is False:
                reason = getattr(instance, "unavailable_reason", "Nicht verfuegbar.")
                _log(f"Plugin {cls.__name__} deaktiviert: {reason}", 2)
                continue
            plugin_name = str(getattr(instance, "name", "") or "").strip()
            if not plugin_name:
                _log(
                    f"Plugin {cls.__name__} wurde ohne Name registriert und wird uebersprungen.",
                    2,
                )
                continue
            if plugin_name in plugins:
                _log(
                    f"Plugin-Name doppelt ({plugin_name}), {cls.__name__} wird uebersprungen.",
                    2,
                )
                continue
            plugins[plugin_name] = instance

    plugins = dict(sorted(plugins.items(), key=lambda item: item[0].casefold()))
    _PLUGIN_CACHE = plugins
    return plugins


def plugin_has_capability(plugin: BasisPlugin, capability: str) -> bool:
    """Prueft ob ein Plugin eine bestimmte Faehigkeit hat."""
    getter = getattr(plugin, "capabilities", None)
    if callable(getter):
        try:
            capabilities = getter()
        except Exception:
            capabilities = set()
        try:
            return capability in set(capabilities or [])
        except Exception:
            return False
    # Backwards compatibility: Popular via POPULAR_GENRE_LABEL constant.
    if capability == "popular_series":
        return _popular_genre_label(plugin) is not None
    return False


def _popular_genre_label(plugin: BasisPlugin) -> str | None:
    label = getattr(plugin, "POPULAR_GENRE_LABEL", None)
    if isinstance(label, str) and label.strip():
        return label.strip()
    return None


def popular_genre_label(plugin: BasisPlugin) -> str | None:
    """Gibt das POPULAR_GENRE_LABEL des Plugins zurueck, falls vorhanden."""
    return _popular_genre_label(plugin)


def plugins_with_popular() -> list[tuple[str, BasisPlugin, str]]:
    """Liefert alle Plugins die 'popular_series' unterstuetzen."""
    results: list[tuple[str, BasisPlugin, str]] = []
    for plugin_name, plugin in discover_plugins().items():
        if not plugin_has_capability(plugin, "popular_series"):
            continue
        label = _popular_genre_label(plugin) or ""
        results.append((plugin_name, plugin, label))
    return results


def series_url_params(plugin: BasisPlugin, title: str) -> dict[str, str]:
    """Liefert series_url Parameter fuer Kodi-Navigation, falls vom Plugin bereitgestellt."""
    getter = getattr(plugin, "series_url_for_title", None)
    if not callable(getter):
        return {}
    try:
        series_url = str(getter(title) or "").strip()
    except Exception:
        return {}
    return {"series_url": series_url} if series_url else {}
