"""Dependency injection container for managing services."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any, TypeVar, cast

if TYPE_CHECKING:
    from collections.abc import Callable

logger = logging.getLogger(__name__)

T = TypeVar("T")


class ServiceContainer:
    """Dependency injection container for managing services."""

    def __init__(self) -> None:
        """Initialize the service container."""
        self._services: dict[type[Any], Any] = {}
        self._factories: dict[type[Any], Callable[[ServiceContainer], Any]] = {}
        self._singletons: dict[type[Any], Any] = {}
        self._scoped_instances: dict[type[Any], Any] = {}

    def register_instance(self, service_type: type[T], instance: T) -> None:
        """Register a service instance (singleton).

        Args:
            service_type: Type of the service
            instance: Instance to register
        """
        self._services[service_type] = instance
        logger.debug(f"Registered instance for {service_type.__name__}")

    def register_factory(
        self,
        service_type: type[T],
        factory: Callable[[ServiceContainer], T],
        singleton: bool = False,
    ) -> None:
        """Register a factory function for creating service instances.

        Args:
            service_type: Type of the service
            factory: Factory function that takes container and returns instance
            singleton: If True, create only one instance (default: False)
        """
        self._factories[service_type] = factory
        if singleton:
            self._singletons[service_type] = None
        logger.debug(f"Registered factory for {service_type.__name__} (singleton: {singleton})")

    def register_singleton(
        self, service_type: type[T], factory: Callable[[ServiceContainer], T]
    ) -> None:
        """Register a singleton factory (convenience method).

        Args:
            service_type: Type of the service
            factory: Factory function that takes container and returns instance
        """
        self.register_factory(service_type, factory, singleton=True)

    def resolve(self, service_type: type[T]) -> T:
        """Resolve a service instance.

        Args:
            service_type: Type of service to resolve

        Returns:
            Service instance

        Raises:
            ValueError: If service is not registered
        """
        if service_type in self._services:
            return cast("T", self._services[service_type])

        if service_type in self._scoped_instances:
            return cast("T", self._scoped_instances[service_type])

        if service_type in self._singletons:
            if self._singletons[service_type] is not None:
                return cast("T", self._singletons[service_type])

            if service_type in self._factories:
                instance = self._factories[service_type](self)
                self._singletons[service_type] = instance
                logger.debug(f"Created singleton instance of {service_type.__name__}")
                return cast("T", instance)

        if service_type in self._factories:
            instance = self._factories[service_type](self)
            self._scoped_instances[service_type] = instance
            logger.debug(f"Created scoped instance of {service_type.__name__}")
            return cast("T", instance)

        raise ValueError(f"Service {service_type.__name__} not registered")

    def try_resolve(self, service_type: type[T]) -> T | None:
        """Try to resolve a service, returning None if not registered.

        Args:
            service_type: Type of service to resolve

        Returns:
            Service instance or None if not registered
        """
        try:
            return self.resolve(service_type)
        except ValueError:
            return None

    def is_registered(self, service_type: type[Any]) -> bool:
        """Check if a service type is registered.

        Args:
            service_type: Type to check

        Returns:
            True if the service is registered
        """
        return (
            service_type in self._services
            or service_type in self._factories
            or service_type in self._singletons
        )

    def create_scope(self) -> ServiceContainer:
        """Create a scoped container that inherits registrations.

        Returns:
            New container with copied registrations but shared singletons
        """
        scope = ServiceContainer()

        scope._factories = self._factories.copy()
        scope._singletons = self._singletons
        scope._services = self._services.copy()
        logger.debug("Created new service container scope")

        return scope

    def dispose_scope(self) -> None:
        """Dispose of scoped instances."""
        disposed_count = len(self._scoped_instances)
        self._scoped_instances.clear()

        if disposed_count > 0:
            logger.debug(f"Disposed {disposed_count} scoped instances")

    def get_registered_services(self) -> list[type[Any]]:
        """Get list of all registered service types.

        Returns:
            List of registered service types
        """
        registered: set[type[Any]] = set()
        registered.update(self._services.keys())
        registered.update(self._factories.keys())
        registered.update(self._singletons.keys())
        return list(registered)

    def clear_all(self) -> None:
        """Clear all registrations and instances."""
        services_count = len(self._services)
        factories_count = len(self._factories)
        singletons_count = len(self._singletons)
        scoped_count = len(self._scoped_instances)

        self._services.clear()
        self._factories.clear()
        self._singletons.clear()
        self._scoped_instances.clear()

        logger.debug(
            f"Cleared all container registrations: "
            f"{services_count} instances, {factories_count} factories, "
            f"{singletons_count} singletons, {scoped_count} scoped"
        )
