from abc import ABC, abstractmethod
from typing import Type, Any, Optional, Set, Union, Callable

from edpdependencyinjection import DependencyBuilder
from ._types import AnyType

BuilderType = Union[Type[DependencyBuilder], Callable]

class DependencyManagerBase(ABC):
    """Abstract base class defining the interface for a dependency manager.

    This class specifies the contract for classes that manage the registration
    and retrieval of dependencies, instances, builders, and parameters.
    """

    @abstractmethod
    def add_dependency_instance(self, dependency_instance: Any):
        """Adds a pre-existing instance to the manager.

        Args:
            dependency_instance (Any): The instance to add.
        """
        raise NotImplementedError()

    @abstractmethod
    def has_instance(self, instance_cls: Type) -> bool:
        """Checks if an instance of a specific class is managed.

        Args:
            instance_cls (Type): The class type to check for.

        Returns:
            bool: True if an instance exists, False otherwise.
        """
        raise NotImplementedError()

    @abstractmethod
    def get_instance(self, instance_cls: Type[AnyType]) -> AnyType:
        """Retrieves a managed instance of a specific class.

        Args:
            instance_cls (Type[AnyType]): The class type of the instance to retrieve.

        Returns:
            AnyType: The stored instance.
        """
        raise NotImplementedError()

    @abstractmethod
    def get_dependencies_by_parent(self, parent_type: Type[AnyType]) -> Set[Type[AnyType]]:
        """Gets all registered dependency classes that are subclasses of a given parent type.

        Args:
            parent_type (Type[AnyType]): The parent type to search for.

        Returns:
            Set[Type[AnyType]]: A set of dependency types that inherit from the parent type.
        """
        raise NotImplementedError()

    @abstractmethod
    def add_dependency_extra_parameter(
        self,
        parameter_name: str,
        parameter_value: Any,
        for_type: Optional[Type] = None
    ):
        """Adds an extra parameter for dependency instantiation.

        Args:
            parameter_name (str): The name of the parameter.
            parameter_value (Any): The value of the parameter.
            for_type (Optional[Type]): The specific type this parameter is for. If None, it's a global parameter.
        """
        raise NotImplementedError()

    @abstractmethod
    def has_extra_parameter(self, parameter_name: str, for_type: Optional[Type] = None) -> bool:
        """Checks if an extra parameter is defined.

        Args:
            parameter_name (str): The name of the parameter.
            for_type (Optional[Type]): The specific type the parameter might be for.

        Returns:
            bool: True if the parameter is defined, False otherwise.
        """
        raise NotImplementedError()

    @abstractmethod
    def get_extra_parameter(self, parameter_name: str, for_type: Optional[Type] = None) -> Any:
        """Gets the value of an extra parameter.

        Args:
            parameter_name (str): The name of the parameter.
            for_type (Optional[Type]): The specific type the parameter is for.

        Returns:
            Any: The value of the parameter.
        """
        raise NotImplementedError()

    @abstractmethod
    def has_dependency_class(self, dependency_cls: Type) -> bool:
        """Checks if a dependency class is registered.

        Args:
            dependency_cls (Type): The class to check.

        Returns:
            bool: True if the class is registered, False otherwise.
        """
        raise NotImplementedError()

    @abstractmethod
    def get_dependency_class(self, dependency_cls: Type[AnyType]) -> Type[AnyType]:
        """Gets a registered dependency class.

        Args:
            dependency_cls (Type[AnyType]): The class to retrieve.

        Returns:
            Type[AnyType]: The registered dependency class.
        """
        raise NotImplementedError()

    @abstractmethod
    def add_dependency_extra_parameters(
        self,
        parameters: Any,
        for_type: Optional[Type] = None,
    ):
        """Adds multiple extra parameters from a dictionary or object.

        Args:
            parameters (Any): A dictionary or object with parameter names and values.
            for_type (Optional[Type]): The specific type these parameters are for. If None, they are global.
        """
        raise NotImplementedError()

    @abstractmethod
    def add_builder(
        self,
        builder: BuilderType,
        for_type: Optional[Type[AnyType]] = None
    ):
        """Adds a custom builder for a specific type.

        Args:
            builder (BuilderType): The builder class or callable.
            for_type (Optional[Type[AnyType]]): The type this builder is responsible for creating.
        """
        raise NotImplementedError()
