"""Dependency injection container for Meta Ads MCP Server."""

from __future__ import annotations

from typing import Any, Callable, Dict, Optional, TypeVar

T = TypeVar("T")


class Container:
    """Lightweight dependency injection container.
    
    Supports singleton registration and factory patterns for managing dependencies.
    Enables testability by allowing easy mocking of dependencies.
    """
    
    def __init__(self) -> None:
        """Initialize the container."""
        self._singletons: Dict[str, Any] = {}
        self._factories: Dict[str, Callable[[], Any]] = {}
        self._initialized: Dict[str, bool] = {}
    
    def register_singleton(self, name: str, instance: Any) -> None:
        """Register a singleton instance.
        
        Args:
            name: The name/key for the service
            instance: The singleton instance to register
            
        Raises:
            ValueError: If a service with this name is already registered
        """
        if name in self._singletons or name in self._factories:
            raise ValueError(f"Service '{name}' is already registered")
        
        self._singletons[name] = instance
        self._initialized[name] = True
    
    def register_factory(self, name: str, factory: Callable[[], Any]) -> None:
        """Register a factory function for lazy initialization.
        
        The factory is called only once when first accessed (lazy singleton).
        
        Args:
            name: The name/key for the service
            factory: A callable that returns the service instance
            
        Raises:
            ValueError: If a service with this name is already registered
        """
        if name in self._singletons or name in self._factories:
            raise ValueError(f"Service '{name}' is already registered")
        
        self._factories[name] = factory
        self._initialized[name] = False
    
    def get(self, name: str) -> Any:
        """Get a service by name.
        
        If the service is registered as a factory and hasn't been initialized,
        it will be initialized on first access.
        
        Args:
            name: The name/key of the service
            
        Returns:
            The service instance
            
        Raises:
            KeyError: If the service is not registered
        """
        # Check if it's a singleton
        if name in self._singletons:
            return self._singletons[name]
        
        # Check if it's a factory
        if name in self._factories:
            # Initialize if not already done
            if not self._initialized[name]:
                instance = self._factories[name]()
                self._singletons[name] = instance
                self._initialized[name] = True
            
            return self._singletons[name]
        
        raise KeyError(f"Service '{name}' is not registered")
    
    def has(self, name: str) -> bool:
        """Check if a service is registered.
        
        Args:
            name: The name/key of the service
            
        Returns:
            True if the service is registered, False otherwise
        """
        return name in self._singletons or name in self._factories
    
    def get_or_none(self, name: str) -> Optional[Any]:
        """Get a service by name, returning None if not found.
        
        Args:
            name: The name/key of the service
            
        Returns:
            The service instance or None if not found
        """
        try:
            return self.get(name)
        except KeyError:
            return None
    
    def remove(self, name: str) -> None:
        """Remove a service from the container.
        
        Args:
            name: The name/key of the service
            
        Raises:
            KeyError: If the service is not registered
        """
        if name not in self._singletons and name not in self._factories:
            raise KeyError(f"Service '{name}' is not registered")
        
        self._singletons.pop(name, None)
        self._factories.pop(name, None)
        self._initialized.pop(name, None)
    
    def clear(self) -> None:
        """Clear all registered services."""
        self._singletons.clear()
        self._factories.clear()
        self._initialized.clear()
    
    def list_services(self) -> list[str]:
        """List all registered service names.
        
        Returns:
            List of service names
        """
        return list(set(self._singletons.keys()) | set(self._factories.keys()))


# Global container instance
_container: Optional[Container] = None


def get_container() -> Container:
    """Get the global container instance.
    
    Returns:
        Container: The global container instance
        
    Raises:
        RuntimeError: If container hasn't been initialized
    """
    global _container
    if _container is None:
        raise RuntimeError(
            "Container not initialized. Call initialize_container() first."
        )
    return _container


def initialize_container() -> Container:
    """Initialize the global container instance.
    
    Returns:
        Container: The initialized container instance
    """
    global _container
    _container = Container()
    return _container


def reset_container() -> None:
    """Reset the global container instance (mainly for testing)."""
    global _container
    if _container is not None:
        _container.clear()
    _container = None

