from abc import ABC, abstractmethod
from typing import ClassVar
import importlib


class BaseInstrumentor(ABC):
    """
    Abstract base class for all agenx framework integrations

    The lifecycle is:
        1. instrument()   — called once at startup, installs all patches
        2. (app runs)     — all patched methods emit agenx spans automatically
        3. uninstrument() — optional, restores originals (useful in tests)
    """

    """instrument() checks these before patching so users get a clear error"""
    required_packages: ClassVar[list[str]] = []

    _instrumented: bool = False


    def instrument(self) -> None:
        """Installs all patches for this library"""
        if self._instrumented:
            return
        
        self._check_requirements()
        self._instrument()
        self._instrumented = True


    def uninstrument(self) -> None:
        """Restores all the patched methods to their originals"""
        if not self._instrumented:
            return
        self._uninstrument()
        self._instrumented = False


    @property
    def is_instrumented(self) -> bool:
        return self._instrumented
    

    @abstractmethod
    def _instrument(self) -> None:
        """Install patches"""

    
    @abstractmethod
    def _uninstrument(self) -> None:
        """Remove patches"""


    def _check_requirements(self) -> None:
        for req in self.required_packages:
            pkg = req.split(">=")[0].split("==")[0].strip()
            if importlib.util.find_spec(pkg) is None:
                raise ImportError(
                    f"{type(self).__name__} requires '{req}' but it is not installed"
                    f"Install it with: pip install {pkg}"
                )

