from __future__ import annotations

from typing import (
    TypeVar,
    Generic,
    Type,
    Callable as _Callable,
    Any,
    Tuple,
    List as _List,
    Optional,
    Dict,
    Union,
    Coroutine as _Coroutine,
)

from .types import Provider as _Provider


Injection = Any
T = TypeVar('T')


class OverridingContext:
    def __init__(self, overridden: Provider, overriding: Provider): ...
    def __enter__(self) -> Provider: ...
    def __exit__(self, *_: Any) -> None: ...


class Provider(_Provider):
    def __init__(self) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> Any: ...
    def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ...
    def __str__(self) -> str: ...
    def __repr__(self) -> str: ...
    @property
    def overridden(self) -> Tuple[Provider]: ...
    @property
    def last_overriding(self) -> Optional[Provider]: ...
    def override(self, provider: Union[Provider, Any]) -> OverridingContext: ...
    def reset_last_overriding(self) -> None: ...
    def reset_override(self) -> None: ...
    def delegate(self) -> Provider: ...
    @property
    def provider(self) -> Provider: ...
    def _copy_overridings(self, copied: Provider, memo: Optional[Dict[str, Any]]) -> None: ...


class Object(Provider, Generic[T]):
    def __init__(self, provides: T) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
    @property
    def provided(self) -> ProvidedInstance: ...


class Delegate(Provider):
    def __init__(self, provides: Provider) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> Provider: ...


class Dependency(Provider, Generic[T]):
    def __init__(self, instance_of: Type[T] = object) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
    @property
    def provided(self) -> ProvidedInstance: ...
    @property
    def instance_of(self) -> Type[T]: ...
    def provided_by(self, provider: Provider) -> OverridingContext: ...


class ExternalDependency(Dependency): ...


class DependenciesContainer(Object):
    def __init__(self, **dependencies: Provider) -> None: ...
    def __getattr__(self, name: str) -> Provider: ...
    @property
    def providers(self) -> Dict[str, Provider]: ...


class Callable(Provider, Generic[T]):
    def __init__(self, provides: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
    @property
    def provides(self) -> T: ...
    @property
    def provided(self) -> ProvidedInstance: ...
    @property
    def args(self) -> Tuple[Injection]: ...
    def add_args(self, *args: Injection) -> Callable[T]: ...
    def set_args(self, *args: Injection) -> Callable[T]: ...
    def clear_args(self) -> Callable[T]: ...
    @property
    def kwargs(self) -> Dict[str, Injection]: ...
    def add_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
    def set_kwargs(self, **kwargs: Injection) -> Callable[T]: ...
    def clear_kwargs(self) -> Callable[T]: ...


class DelegatedCallable(Callable): ...


class AbstractCallable(Callable):
    def override(self, provider: Callable) -> OverridingContext: ...


class CallableDelegate(Delegate):
    def __init__(self, callable: Callable) -> None: ...


class Coroutine(Callable): ...


class DelegatedCoroutine(Coroutine): ...


class AbstractCoroutine(Coroutine):
    def override(self, provider: Coroutine) -> OverridingContext: ...


class CoroutineDelegate(Delegate):
    def __init__(self, coroutine: Coroutine) -> None: ...


class ConfigurationOption(Provider):
    UNDEFINED: object
    def __init__(self, name: str, root: Configuration) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> Any: ...
    def __getattr__(self, item: str) -> ConfigurationOption: ...
    def __getitem__(self, item: str) -> ConfigurationOption: ...
    def get_name(self) -> str: ...
    def as_int(self) -> Callable[int]: ...
    def as_float(self) -> Callable[float]: ...
    def as_(self, callback: _Callable[..., T], *args: Injection, **kwargs: Injection) -> Callable[T]: ...
    def update(self, value: Any) -> None: ...
    def from_ini(self, filepath: str) -> None: ...
    def from_yaml(self, filepath: str) -> None: ...
    def from_dict(self, options: Dict[str, Any]) -> None: ...
    def from_env(self, name: str, default: Optional[Any] = None) -> None: ...


class Configuration(Object):
    DEFAULT_NAME: str = 'config'
    def __init__(self, name: str = DEFAULT_NAME, default: Optional[Any] = None) -> None: ...
    def __getattr__(self, item: str) -> ConfigurationOption: ...
    def __getitem__(self, item: str) -> ConfigurationOption: ...
    def get_name(self) -> str: ...
    def get(self, selector: str) -> Any: ...
    def set(self, selector: str, value: Any) -> OverridingContext: ...
    def reset_cache(self) -> None: ...
    def update(self, value: Any) -> None: ...
    def from_ini(self, filepath: str) -> None: ...
    def from_yaml(self, filepath: str) -> None: ...
    def from_dict(self, options: Dict[str, Any]) -> None: ...
    def from_env(self, name: str, default: Optional[Any] = None) -> None: ...


class Factory(Provider, Generic[T]):
    provided_type: Optional[Type]
    def __init__(self, provides: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
    @property
    def cls(self) -> T: ...
    @property
    def provides(self) -> T: ...
    @property
    def provided(self) -> ProvidedInstance: ...
    @property
    def args(self) -> Tuple[Injection]: ...
    def add_args(self, *args: Injection) -> Factory[T]: ...
    def set_args(self, *args: Injection) -> Factory[T]: ...
    def clear_args(self) -> Factory[T]: ...
    @property
    def kwargs(self) -> Dict[str, Injection]: ...
    def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
    def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
    def clear_kwargs(self) -> Factory[T]: ...
    @property
    def attributes(self) -> Dict[str, Injection]: ...
    def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
    def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
    def clear_attributes(self) -> Factory[T]: ...


class DelegatedFactory(Factory): ...


class AbstractFactory(Factory):
    def override(self, provider: Factory) -> OverridingContext: ...


class FactoryDelegate(Delegate):
    def __init__(self, factory: Factory): ...


class FactoryAggregate(Provider):
    def __init__(self, **factories: Factory): ...
    def __call__(self, factory_name: str, *args: Injection, **kwargs: Injection) -> Any: ...
    def __getattr__(self, factory_name: str) -> Factory: ...
    @property
    def factories(self) -> Dict[str, Factory]: ...


class BaseSingleton(Provider, Generic[T]):
    provided_type = Optional[Type]
    def __init__(self, provides: _Callable[..., T], *args: Injection, **kwargs: Injection) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
    @property
    def cls(self) -> T: ...
    @property
    def provided(self) -> ProvidedInstance: ...
    @property
    def args(self) -> Tuple[Injection]: ...
    def add_args(self, *args: Injection) -> Factory[T]: ...
    def set_args(self, *args: Injection) -> Factory[T]: ...
    def clear_args(self) -> Factory[T]: ...
    @property
    def kwargs(self) -> Dict[str, Injection]: ...
    def add_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
    def set_kwargs(self, **kwargs: Injection) -> Factory[T]: ...
    def clear_kwargs(self) -> Factory[T]: ...
    @property
    def attributes(self) -> Dict[str, Injection]: ...
    def add_attributes(self, **kwargs: Injection) -> Factory[T]: ...
    def set_attributes(self, **kwargs: Injection) -> Factory[T]: ...
    def clear_attributes(self) -> Factory[T]: ...
    def reset(self) -> None: ...


class Singleton(BaseSingleton): ...


class DelegatedSingleton(Singleton): ...


class ThreadSafeSingleton(Singleton): ...


class DelegatedThreadSafeSingleton(ThreadSafeSingleton): ...


class ThreadLocalSingleton(BaseSingleton): ...


class DelegatedThreadLocalSingleton(ThreadLocalSingleton): ...


class AbstractSingleton(BaseSingleton):
    def override(self, provider: BaseSingleton) -> OverridingContext: ...


class SingletonDelegate(Delegate):
    def __init__(self, factory: BaseSingleton): ...


class List(Provider):
    def __init__(self, *args: Injection): ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> _List[Any]: ...
    @property
    def provided(self) -> ProvidedInstance: ...
    @property
    def args(self) -> Tuple[Injection]: ...
    def add_args(self, *args: Injection) -> List: ...
    def set_args(self, *args: Injection) -> List: ...
    def clear_args(self) -> List: ...


class Container(Provider):

    def __init__(self, container_cls: Type[T], container: Optional[T] = None, **overriding_providers: Provider) -> None: ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
    def __getattr__(self, name: str) -> Provider: ...


class Selector(Provider):
    def __init__(self, selector: _Callable[..., Any], **providers: Provider): ...
    def __call__(self, *args: Injection, **kwargs: Injection) -> Any: ...
    def __getattr__(self, name: str) -> Provider: ...
    @property
    def provided(self) -> ProvidedInstance: ...
    @property
    def providers(self) -> Dict[str, Provider]: ...


class ProvidedInstanceFluentInterface:
    def __getattr__(self, item: Any) -> AttributeGetter: ...
    def __getitem__(self, item: Any) -> ItemGetter: ...
    def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...


class ProvidedInstance(Provider, ProvidedInstanceFluentInterface):
    def __init__(self, provider: Provider) -> None: ...


class AttributeGetter(Provider, ProvidedInstanceFluentInterface):
    def __init__(self, provider: Provider, attribute: str) -> None: ...


class ItemGetter(Provider, ProvidedInstanceFluentInterface):
    def __init__(self, provider: Provider, item: str) -> None: ...


class MethodCaller(Provider, ProvidedInstanceFluentInterface):
    def __init__(self, provider: Provider, *args: Injection, **kwargs: Injection) -> None: ...


def is_provider(instance: Any) -> bool: ...


def ensure_is_provider(instance: Any) -> Provider: ...


def is_delegated(instance: Any) -> bool: ...


def represent_provider(provider: Provider, provides: Any) -> str: ...


def deepcopy(instance: Any, memo: Optional[Dict[str, Any]]): Any: ...


def merge_dicts(dict1: Dict[Any, Any], dict2: Dict[Any, Any]) -> Dict[Any, Any]: ...
