"""Dynamic loader for compiled paid checkers from ~/.stablestack/checkers/."""

import importlib.util
import json
import logging
import sys
from pathlib import Path
from typing import List, Optional, Type

from stablestack.checkers.base import BaseChecker

logger = logging.getLogger(__name__)

_cached_paid_checkers: Optional[List[Type[BaseChecker]]] = None


def get_paid_checkers_dir() -> Path:
    """Return the directory where paid compiled checkers are installed."""
    return Path.home() / ".stablestack" / "checkers"


def invalidate_cache() -> None:
    """Invalidate the cached paid checkers so they are reloaded next call."""
    global _cached_paid_checkers
    _cached_paid_checkers = None


def load_paid_checkers() -> List[Type[BaseChecker]]:
    """
    Scan ~/.stablestack/checkers/ for manifest.json, load compiled .so/.pyd
    modules, and return a list of paid checker classes.

    Results are cached per-process; call invalidate_cache() after activation
    to force a reload.
    """
    global _cached_paid_checkers
    if _cached_paid_checkers is not None:
        return list(_cached_paid_checkers)

    checkers_dir = get_paid_checkers_dir()
    manifest_path = checkers_dir / "manifest.json"

    if not manifest_path.exists():
        _cached_paid_checkers = []
        return []

    try:
        manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError) as e:
        logger.warning("Failed to read paid checkers manifest: %s", e)
        _cached_paid_checkers = []
        return []

    # Version compatibility check: manifest's stablestack_version must match
    # installed major.minor
    manifest_version = manifest.get("stablestack_version", "")
    try:
        from stablestack import __version__
        installed_parts = __version__.split(".")[:2]
        manifest_parts = manifest_version.split(".")[:2]
        if installed_parts != manifest_parts:
            logger.warning(
                "Paid checkers version mismatch: manifest requires %s, "
                "installed is %s. Run 'stablestack activate <key>' to update.",
                manifest_version,
                __version__,
            )
            _cached_paid_checkers = []
            return []
    except (ImportError, ValueError):
        pass

    checkers: List[Type[BaseChecker]] = []
    ext = _get_compiled_extension()

    for entry in manifest.get("checkers", []):
        module_name = entry.get("module")
        class_name = entry.get("class_name")
        if not module_name or not class_name:
            continue

        module_file = checkers_dir / f"{module_name}{ext}"
        if not module_file.exists():
            logger.debug("Compiled module not found: %s", module_file)
            continue

        try:
            checker_cls = _load_checker_from_file(
                module_file, module_name, class_name
            )
            if checker_cls is not None:
                checkers.append(checker_cls)
        except Exception as e:
            logger.warning("Failed to load paid checker %s: %s", module_name, e)

    _cached_paid_checkers = checkers
    return list(checkers)


def _get_compiled_extension() -> str:
    """Return the platform-specific compiled extension (.so or .pyd)."""
    if sys.platform == "win32":
        return ".pyd"
    # On Linux/macOS, cpython extensions include version+platform info
    # e.g. .cpython-312-darwin.so — but we use the simplified .so
    return ".so"


def _load_checker_from_file(
    file_path: Path,
    module_name: str,
    class_name: str,
) -> Optional[Type[BaseChecker]]:
    """Load a single checker class from a compiled .so/.pyd file."""
    full_module_name = f"stablestack_paid.{module_name}"

    spec = importlib.util.spec_from_file_location(
        full_module_name, str(file_path)
    )
    if spec is None or spec.loader is None:
        return None

    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)

    cls = getattr(module, class_name, None)
    if cls is None:
        logger.warning("Class %s not found in %s", class_name, file_path)
        return None

    if not (isinstance(cls, type) and issubclass(cls, BaseChecker)):
        logger.warning("%s is not a BaseChecker subclass", class_name)
        return None

    # Skip abstract classes (missing check_file implementation)
    import inspect
    if inspect.isabstract(cls):
        logger.warning("%s is abstract (missing check_file), skipping", class_name)
        return None

    return cls
