"""
CMP Library Registry — discovers and registers all CMP modules.

Each CMP module exports two module-level symbols:
  - MATCHER: a compiled regex matching the JSON endpoint URL
  - reconstruct(data: dict) -> str: builds the cookie-policy text from JSON

The registry auto-discovers:
  1. Hand-written modules: epaas, onetrust, cookiebot, usercentrics,
     didomi, trustarc
  2. Auto-promoted modules: any file matching `auto_*.py` in this folder
     (created by Phase E when an LLM successfully discovers a new pattern)

A consent-tester restart picks up new auto_*.py files automatically.
"""

from __future__ import annotations

import importlib
import importlib.util
import logging
import os
import pkgutil
from pathlib import Path
from typing import Callable

logger = logging.getLogger(__name__)

# (cmp_name, url_pattern, reconstruct_fn)
Registry = list[tuple[str, "object", Callable[[dict], str]]]

# Phase E: persistent auto-promoted modules live in a writable volume
# (separate from the source tree so deploys do not wipe them).
AUTO_DIR = Path(os.getenv("CMP_AUTO_DIR", "/data/auto_cmp"))


def load_all() -> Registry:
    """Import every module in this package and from AUTO_DIR."""
    registry: Registry = []
    _load_from_package(registry)
    _load_from_auto_dir(registry)
    return registry


def _load_from_package(registry: Registry) -> None:
    """Import the hand-written modules in services/cmp_library/."""
    import services.cmp_library as pkg  # type: ignore[import-not-found]
    pkg_path = Path(pkg.__file__).parent
    for module_info in pkgutil.iter_modules([str(pkg_path)]):
        name = module_info.name
        if name.startswith("_"):
            continue
        try:
            module = importlib.import_module(f"services.cmp_library.{name}")
            _register(registry, name, module)
        except Exception as e:
            logger.warning("CMP module %s failed to load: %s", name, e)


def _load_from_auto_dir(registry: Registry) -> None:
    """Import auto-promoted modules from the runtime volume."""
    if not AUTO_DIR.exists():
        return
    for path in sorted(AUTO_DIR.glob("auto_*.py")):
        name = path.stem
        try:
            spec = importlib.util.spec_from_file_location(name, path)
            if not spec or not spec.loader:
                continue
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)
            _register(registry, name, module)
        except Exception as e:
            logger.warning("Auto CMP module %s failed to load: %s", name, e)


def _register(registry: Registry, name: str, module) -> None:
    matcher = getattr(module, "MATCHER", None)
    reconstruct = getattr(module, "reconstruct", None)
    if matcher is None or not callable(reconstruct):
        logger.warning(
            "CMP module %s missing MATCHER or reconstruct() — skipped", name,
        )
        return
    registry.append((name, matcher, reconstruct))
    logger.info("CMP loaded: %s", name)
