"""
Patcher Registry

Central registry for monkey-patch functions. Each patcher is a callable
that returns an unpatch callable (or None if it chose not to patch).

    def my_patcher() -> Optional[Callable[[], None]]:
        # patch something
        return lambda: # unpatch it

This pattern ensures every patch is reversible for testing and shutdown.
"""

import logging
from typing import Callable, Dict, List, Optional

logger = logging.getLogger("pendo_sdk")

# patcher name → patcher function
_registry: Dict[str, Callable[[], Optional[Callable[[], None]]]] = {}

# patcher name → unpatch function (populated after patching)
_active_unpatchers: Dict[str, Callable[[], None]] = {}


def register_patcher(
    name: str,
    patcher: Callable[[], Optional[Callable[[], None]]],
) -> None:
    """Register a patcher function.

    Args:
        name: Unique name for this patcher (e.g. "anthropic", "openai").
        patcher: Callable that applies the patch. Must return an unpatch
            callable, or None if it chose not to patch (e.g. lib not installed).
    """
    _registry[name] = patcher


def patch_all() -> List[str]:
    """Run all registered patchers. Returns names of successfully patched libs."""
    patched = []
    for name, patcher in _registry.items():
        if name in _active_unpatchers:
            logger.debug("Patcher '%s' already active, skipping", name)
            continue
        try:
            unpatcher = patcher()
            if unpatcher is not None:
                _active_unpatchers[name] = unpatcher
                patched.append(name)
                logger.debug("Patched '%s'", name)
        except Exception:
            logger.debug("Patcher '%s' failed, skipping", name, exc_info=True)
    return patched


def unpatch_all() -> None:
    """Restore all patched libraries to their originals."""
    for name, unpatcher in list(_active_unpatchers.items()):
        try:
            unpatcher()
            logger.debug("Unpatched '%s'", name)
        except Exception:
            logger.debug("Unpatcher '%s' failed", name, exc_info=True)
    _active_unpatchers.clear()


def get_registry() -> Dict[str, Callable]:
    """Return a copy of the current registry (for introspection/testing)."""
    return dict(_registry)


def _clear_registry() -> None:
    """Clear registry and active patches. For testing only."""
    unpatch_all()
    _registry.clear()
