#!/usr/bin/env python3
"""Generate a JSON manifest for addon plugins."""
from __future__ import annotations

import importlib.util
import inspect
import json
import sys
from pathlib import Path

ROOT_DIR = Path(__file__).resolve().parents[1]
PLUGIN_DIR = ROOT_DIR / "addon" / "plugins"
OUTPUT_PATH = ROOT_DIR / "docs" / "PLUGIN_MANIFEST.json"

sys.path.insert(0, str(ROOT_DIR / "addon"))

try:
    from plugin_interface import BasisPlugin  # type: ignore
except Exception as exc:  # pragma: no cover
    raise SystemExit(f"Failed to import BasisPlugin: {exc}")


def _import_module(path: Path):
    spec = importlib.util.spec_from_file_location(path.stem, path)
    if spec is None or spec.loader is None:
        raise ImportError(f"Missing spec for {path}")
    module = importlib.util.module_from_spec(spec)
    sys.modules[spec.name] = module
    spec.loader.exec_module(module)
    return module


def _collect_plugins():
    plugins = []
    for file_path in sorted(PLUGIN_DIR.glob("*.py")):
        if file_path.name.startswith("_"):
            continue
        entry = {
            "file": str(file_path.relative_to(ROOT_DIR)),
            "module": file_path.stem,
            "name": None,
            "class": None,
            "version": None,
            "capabilities": [],
            "prefer_source_metadata": False,
            "base_url_setting": None,
            "available": None,
            "unavailable_reason": None,
            "error": None,
        }
        try:
            module = _import_module(file_path)
            preferred = getattr(module, "Plugin", None)
            if inspect.isclass(preferred) and issubclass(preferred, BasisPlugin) and preferred is not BasisPlugin:
                classes = [preferred]
            else:
                classes = [
                    obj
                    for obj in module.__dict__.values()
                    if inspect.isclass(obj) and issubclass(obj, BasisPlugin) and obj is not BasisPlugin
                ]
                classes.sort(key=lambda cls: cls.__name__.casefold())

            if not classes:
                entry["error"] = "No plugin classes found"
                plugins.append(entry)
                continue

            cls = classes[0]
            instance = cls()
            entry["class"] = cls.__name__
            entry["name"] = str(getattr(instance, "name", "") or "") or None
            entry["version"] = str(getattr(instance, "version", "0.0.0") or "0.0.0")
            entry["prefer_source_metadata"] = bool(getattr(instance, "prefer_source_metadata", False))
            entry["available"] = bool(getattr(instance, "is_available", True))
            entry["unavailable_reason"] = getattr(instance, "unavailable_reason", None)
            try:
                caps = instance.capabilities()  # type: ignore[call-arg]
                entry["capabilities"] = sorted([str(c) for c in caps]) if caps else []
            except Exception:
                entry["capabilities"] = []

            entry["base_url_setting"] = getattr(module, "SETTING_BASE_URL", None)
        except Exception as exc:  # pragma: no cover
            entry["error"] = str(exc)
        plugins.append(entry)

    plugins.sort(key=lambda item: (item.get("name") or item["module"]).casefold())
    return plugins


def main() -> int:
    if not PLUGIN_DIR.exists():
        raise SystemExit("Plugin directory missing")
    manifest = {
        "schema_version": 1,
        "plugins": _collect_plugins(),
    }
    OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_PATH.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
    print(str(OUTPUT_PATH))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
