from __future__ import annotations

from contextlib import AbstractAsyncContextManager, AbstractContextManager
from pathlib import Path
from typing import (
    Any,
    AsyncIterator as _AsyncIterator,
    Awaitable,
    Callable as _Callable,
    Coroutine as _Coroutine,
    Dict as _Dict,
    Generator as _Generator,
    Generic,
    Iterable as _Iterable,
    Iterator as _Iterator,
    List as _List,
    Mapping,
    Optional,
    Tuple,
    Type,
    Union,
    overload,
)

from typing_extensions import Self as _Self, TypeVar

try:
    import yaml
except ImportError:
    yaml = None

try:
    import pydantic
except ImportError:
    pydantic = None

from . import resources

Injection = Any
ProviderParent = Union["Provider", Any]
T = TypeVar("T")
TT = TypeVar("TT")
T_Any = TypeVar("T_Any", default=Any)
P = TypeVar("P", bound="Provider")
BS = TypeVar("BS", bound="BaseSingleton")

class Provider(Generic[T]):
    def __init__(self) -> None: ...
    @overload
    def __call__(self, *args: Injection, **kwargs: Injection) -> T: ...
    @overload
    def __call__(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
    def async_(self, *args: Injection, **kwargs: Injection) -> Awaitable[T]: ...
    def __deepcopy__(self, memo: Optional[_Dict[Any, 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[P]: ...
    def reset_last_overriding(self) -> None: ...
    def reset_override(self) -> None: ...
    @property
    def overrides(self) -> Tuple[Provider]: ...
    def register_overrides(self, provider: Union[Provider, Any]) -> None: ...
    def unregister_overrides(self, provider: Union[Provider, Any]) -> None: ...
    def delegate(self) -> Provider: ...
    @property
    def provider(self) -> Provider[T]: ...
    @property
    def provided(self) -> ProvidedInstance: ...
    def enable_async_mode(self) -> None: ...
    def disable_async_mode(self) -> None: ...
    def reset_async_mode(self) -> None: ...
    def is_async_mode_enabled(self) -> bool: ...
    def is_async_mode_disabled(self) -> bool: ...
    def is_async_mode_undefined(self) -> bool: ...
    @property
    def related(self) -> _Iterator[Provider]: ...
    def traverse(
        self, types: Optional[_Iterable[Type[TT]]] = None
    ) -> _Iterator[TT]: ...
    def _copy_overridings(
        self, copied: Provider, memo: Optional[_Dict[Any, Any]]
    ) -> None: ...

class Object(Provider[T]):
    def __init__(self, provides: Optional[T] = None) -> None: ...
    @property
    def provides(self) -> Optional[T]: ...
    def set_provides(self, provides: Optional[T]) -> _Self: ...

class Self(Provider[T]):
    def __init__(self, container: Optional[T] = None) -> None: ...
    def set_container(self, container: T) -> None: ...
    def set_alt_names(self, alt_names: _Iterable[Any]) -> None: ...
    @property
    def alt_names(self) -> Tuple[Any]: ...

class Delegate(Provider[Provider]):
    def __init__(self, provides: Optional[Provider] = None) -> None: ...
    @property
    def provides(self) -> Optional[Provider]: ...
    def set_provides(self, provides: Optional[Provider]) -> _Self: ...

class Aggregate(Provider[T]):
    def __init__(
        self,
        provider_dict: Optional[Mapping[Any, Provider[T]]] = None,
        **provider_kwargs: Provider[T],
    ): ...
    def __getattr__(self, provider_name: Any) -> Provider[T]: ...
    @overload
    def __call__(
        self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
    ) -> T: ...
    @overload
    def __call__(
        self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
    ) -> Awaitable[T]: ...
    def async_(
        self, provider_name: Optional[Any] = None, *args: Injection, **kwargs: Injection
    ) -> Awaitable[T]: ...
    @property
    def providers(self) -> _Dict[Any, Provider[T]]: ...
    def set_providers(
        self,
        provider_dict: Optional[Mapping[Any, Provider[T]]] = None,
        **provider_kwargs: Provider[T],
    ) -> _Self: ...

class Dependency(Provider[T]):
    def __init__(
        self,
        instance_of: Type[T] = object,
        default: Optional[Union[Provider, Any]] = None,
    ) -> None: ...
    def __getattr__(self, name: str) -> Any: ...
    @property
    def instance_of(self) -> Type[T]: ...
    def set_instance_of(self, instance_of: Type[T]) -> _Self: ...
    @property
    def default(self) -> Provider[T]: ...
    def set_default(self, default: Optional[Union[Provider, Any]]) -> _Self: ...
    @property
    def is_defined(self) -> bool: ...
    def provided_by(self, provider: Provider) -> OverridingContext[P]: ...
    @property
    def parent(self) -> Optional[ProviderParent]: ...
    @property
    def parent_name(self) -> Optional[str]: ...
    def assign_parent(self, parent: ProviderParent) -> None: ...

class ExternalDependency(Dependency[T]): ...

class DependenciesContainer(Object):
    def __init__(self, **dependencies: Provider) -> None: ...
    def __getattr__(self, name: str) -> Provider: ...
    @property
    def providers(self) -> _Dict[str, Provider]: ...
    def resolve_provider_name(self, provider: Provider) -> str: ...
    @property
    def parent(self) -> Optional[ProviderParent]: ...
    @property
    def parent_name(self) -> Optional[str]: ...
    def assign_parent(self, parent: ProviderParent) -> None: ...

class Callable(Provider[T_Any]):
    def __init__(
        self,
        provides: Optional[Union[_Callable[..., T_Any], str]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @property
    def provides(self) -> Optional[_Callable[..., T_Any]]: ...
    def set_provides(
        self, provides: Optional[Union[_Callable[..., T_Any], str]]
    ) -> _Self: ...
    @property
    def args(self) -> Tuple[Injection]: ...
    def add_args(self, *args: Injection) -> _Self: ...
    def set_args(self, *args: Injection) -> _Self: ...
    def clear_args(self) -> _Self: ...
    @property
    def kwargs(self) -> _Dict[str, Injection]: ...
    def add_kwargs(self, **kwargs: Injection) -> _Self: ...
    def set_kwargs(self, **kwargs: Injection) -> _Self: ...
    def clear_kwargs(self) -> _Self: ...

class DelegatedCallable(Callable[T]): ...

class AbstractCallable(Callable[T]):
    def override(self, provider: Callable) -> OverridingContext[P]: ...

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

class Coroutine(Callable[T_Any]): ...
class DelegatedCoroutine(Coroutine[T]): ...

class AbstractCoroutine(Coroutine[T]):
    def override(self, provider: Coroutine) -> OverridingContext[P]: ...

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

class ConfigurationOption(Provider[Any]):
    UNDEFINED: object
    def __init__(self, name: Tuple[str], root: Configuration) -> None: ...
    def __enter__(self) -> _Self: ...
    def __exit__(self, *exc_info: Any) -> None: ...
    def __getattr__(self, item: str) -> ConfigurationOption: ...
    def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
    @property
    def root(self) -> Configuration: ...
    def get_name(self) -> str: ...
    def get_name_segments(self) -> Tuple[Union[str, Provider]]: ...
    def as_int(self) -> TypedConfigurationOption[int]: ...
    def as_float(self) -> TypedConfigurationOption[float]: ...
    def as_(
        self, callback: _Callable[..., T], *args: Injection, **kwargs: Injection
    ) -> TypedConfigurationOption[T]: ...
    def required(self) -> ConfigurationOption: ...
    def is_required(self) -> bool: ...
    def update(self, value: Any) -> None: ...
    def from_ini(
        self,
        filepath: Union[Path, str],
        required: bool = False,
        envs_required: Optional[bool] = False,
    ) -> None: ...
    def from_yaml(
        self,
        filepath: Union[Path, str],
        required: bool = False,
        loader: Optional[Any] = None,
        envs_required: Optional[bool] = False,
    ) -> None: ...
    def from_json(
        self,
        filepath: Union[Path, str],
        required: bool = False,
        envs_required: Optional[bool] = False,
    ) -> None: ...
    def from_pydantic(
        self, settings: PydanticSettings, required: bool = False, **kwargs: Any
    ) -> None: ...
    def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
    def from_env(
        self,
        name: str,
        default: Optional[Any] = None,
        required: bool = False,
        as_: Optional[_Callable[..., Any]] = None,
    ) -> None: ...
    def from_value(self, value: Any) -> None: ...

class TypedConfigurationOption(Callable[T]):
    @property
    def option(self) -> ConfigurationOption: ...

class Configuration(Object[Any]):
    DEFAULT_NAME: str = "config"
    def __init__(
        self,
        name: str = DEFAULT_NAME,
        default: Optional[Any] = None,
        *,
        strict: bool = False,
        ini_files: Optional[_Iterable[Union[Path, str]]] = None,
        yaml_files: Optional[_Iterable[Union[Path, str]]] = None,
        json_files: Optional[_Iterable[Union[Path, str]]] = None,
        pydantic_settings: Optional[_Iterable[PydanticSettings]] = None,
    ) -> None: ...
    def __enter__(self) -> _Self: ...
    def __exit__(self, *exc_info: Any) -> None: ...
    def __getattr__(self, item: str) -> ConfigurationOption: ...
    def __getitem__(self, item: Union[str, Provider]) -> ConfigurationOption: ...
    def get_name(self) -> str: ...
    def set_name(self, name: str) -> _Self: ...
    def get_default(self) -> _Dict[Any, Any]: ...
    def set_default(self, default: _Dict[Any, Any]) -> _Self: ...
    def get_strict(self) -> bool: ...
    def set_strict(self, strict: bool) -> _Self: ...
    def get_children(self) -> _Dict[str, ConfigurationOption]: ...
    def set_children(self, children: _Dict[str, ConfigurationOption]) -> _Self: ...
    def get_ini_files(self) -> _List[Union[Path, str]]: ...
    def set_ini_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
    def get_yaml_files(self) -> _List[Union[Path, str]]: ...
    def set_yaml_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
    def get_json_files(self) -> _List[Union[Path, str]]: ...
    def set_json_files(self, files: _Iterable[Union[Path, str]]) -> _Self: ...
    def get_pydantic_settings(self) -> _List[PydanticSettings]: ...
    def set_pydantic_settings(self, settings: _Iterable[PydanticSettings]) -> _Self: ...
    def load(self, required: bool = False, envs_required: bool = False) -> None: ...
    def get(self, selector: str) -> Any: ...
    def set(self, selector: str, value: Any) -> OverridingContext[P]: ...
    def reset_cache(self) -> None: ...
    def update(self, value: Any) -> None: ...
    def from_ini(
        self,
        filepath: Union[Path, str],
        required: bool = False,
        envs_required: bool = False,
    ) -> None: ...
    def from_yaml(
        self,
        filepath: Union[Path, str],
        required: bool = False,
        loader: Optional[Any] = None,
        envs_required: bool = False,
    ) -> None: ...
    def from_json(
        self,
        filepath: Union[Path, str],
        required: bool = False,
        envs_required: bool = False,
    ) -> None: ...
    def from_pydantic(
        self, settings: PydanticSettings, required: bool = False, **kwargs: Any
    ) -> None: ...
    def from_dict(self, options: _Dict[str, Any], required: bool = False) -> None: ...
    def from_env(
        self,
        name: str,
        default: Optional[Any] = None,
        required: bool = False,
        as_: Optional[_Callable[..., Any]] = None,
    ) -> None: ...
    def from_value(self, value: Any) -> None: ...

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

class DelegatedFactory(Factory[T]): ...

class AbstractFactory(Factory[T]):
    def override(self, provider: Factory) -> OverridingContext[P]: ...

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

class FactoryAggregate(Aggregate[T]):
    def __getattr__(self, provider_name: Any) -> Factory[T]: ...
    @property
    def factories(self) -> _Dict[Any, Factory[T]]: ...
    def set_factories(
        self,
        provider_dict: Optional[Mapping[Any, Factory[T]]] = None,
        **provider_kwargs: Factory[T],
    ) -> FactoryAggregate[T]: ...

class BaseSingleton(Provider[T]):
    provided_type = Optional[Type]
    def __init__(
        self,
        provides: Optional[Union[_Callable[..., T], str]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @property
    def cls(self) -> Type[T]: ...
    @property
    def provides(self) -> Optional[_Callable[..., T]]: ...
    def set_provides(
        self, provides: Optional[Union[_Callable[..., T], str]]
    ) -> _Self: ...
    @property
    def args(self) -> Tuple[Injection]: ...
    def add_args(self, *args: Injection) -> _Self: ...
    def set_args(self, *args: Injection) -> _Self: ...
    def clear_args(self) -> _Self: ...
    @property
    def kwargs(self) -> _Dict[str, Injection]: ...
    def add_kwargs(self, **kwargs: Injection) -> _Self: ...
    def set_kwargs(self, **kwargs: Injection) -> _Self: ...
    def clear_kwargs(self) -> _Self: ...
    @property
    def attributes(self) -> _Dict[str, Injection]: ...
    def add_attributes(self, **kwargs: Injection) -> _Self: ...
    def set_attributes(self, **kwargs: Injection) -> _Self: ...
    def clear_attributes(self) -> _Self: ...
    def reset(self) -> SingletonResetContext[BS]: ...
    def full_reset(self) -> SingletonFullResetContext[BS]: ...

class Singleton(BaseSingleton[T]): ...
class DelegatedSingleton(Singleton[T]): ...
class ThreadSafeSingleton(Singleton[T]): ...
class DelegatedThreadSafeSingleton(ThreadSafeSingleton[T]): ...
class ThreadLocalSingleton(BaseSingleton[T]): ...
class ContextLocalSingleton(BaseSingleton[T]): ...
class DelegatedThreadLocalSingleton(ThreadLocalSingleton[T]): ...

class AbstractSingleton(BaseSingleton[T]):
    def override(self, provider: BaseSingleton) -> OverridingContext[P]: ...

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

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

class Dict(Provider[_Dict]):
    def __init__(
        self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
    ): ...
    @property
    def kwargs(self) -> _Dict[Any, Injection]: ...
    def add_kwargs(
        self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
    ) -> _Self: ...
    def set_kwargs(
        self, dict_: Optional[Mapping[Any, Injection]] = None, **kwargs: Injection
    ) -> _Self: ...
    def clear_kwargs(self) -> _Self: ...

class BaseResource(Provider[T]):
    @overload
    def __init__(
        self,
        provides: Optional[Type[resources.Resource[T]]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @overload
    def __init__(
        self,
        provides: Optional[Type[resources.AsyncResource[T]]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @overload
    def __init__(
        self,
        provides: Optional[_Callable[..., AbstractContextManager[T]]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @overload
    def __init__(
        self,
        provides: Optional[_Callable[..., AbstractAsyncContextManager[T]]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @overload
    def __init__(
        self,
        provides: Optional[_Callable[..., _Iterator[T]]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @overload
    def __init__(
        self,
        provides: Optional[_Callable[..., _AsyncIterator[T]]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @overload
    def __init__(
        self,
        provides: Optional[_Callable[..., _Coroutine[Injection, Injection, T]]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @overload
    def __init__(
        self,
        provides: Optional[Union[_Callable[..., T], str]] = None,
        *args: Injection,
        **kwargs: Injection,
    ) -> None: ...
    @property
    def provides(self) -> Optional[_Callable[..., Any]]: ...
    def set_provides(self, provides: Optional[Any]) -> _Self: ...
    @property
    def args(self) -> Tuple[Injection]: ...
    def add_args(self, *args: Injection) -> _Self: ...
    def set_args(self, *args: Injection) -> _Self: ...
    def clear_args(self) -> _Self: ...
    @property
    def kwargs(self) -> _Dict[str, Injection]: ...
    def add_kwargs(self, **kwargs: Injection) -> _Self: ...
    def set_kwargs(self, **kwargs: Injection) -> _Self: ...
    def clear_kwargs(self) -> _Self: ...
    @property
    def initialized(self) -> bool: ...
    def init(self) -> Optional[Awaitable[T]]: ...
    def shutdown(self) -> Optional[Awaitable]: ...

class Resource(BaseResource[T]): ...
class ContextLocalResource(BaseResource[T]):...

class Container(Provider[T]):
    def __init__(
        self,
        container_cls: Type[T],
        container: Optional[T] = None,
        **overriding_providers: Union[Provider, Any],
    ) -> None: ...
    def __getattr__(self, name: str) -> Provider: ...
    @property
    def container(self) -> T: ...
    def resolve_provider_name(self, provider: Provider) -> str: ...
    @property
    def parent(self) -> Optional[ProviderParent]: ...
    @property
    def parent_name(self) -> Optional[str]: ...
    def assign_parent(self, parent: ProviderParent) -> None: ...

class Selector(Provider[T_Any]):
    def __init__(
        self, selector: Optional[_Callable[..., Any]] = None, **providers: Provider
    ): ...
    def __getattr__(self, name: str) -> Provider[T_Any]: ...
    @property
    def selector(self) -> Optional[_Callable[..., Any]]: ...
    def set_selector(self, selector: Optional[_Callable[..., Any]]) -> _Self: ...
    @property
    def providers(self) -> _Dict[str, Provider[T_Any]]: ...
    def set_providers(self, **providers: Provider) -> _Self: ...

class ProvidedInstanceFluentInterface:
    def __getattr__(self, item: Any) -> AttributeGetter: ...
    def __getitem__(self, item: Any) -> ItemGetter: ...
    def call(self, *args: Injection, **kwargs: Injection) -> MethodCaller: ...
    @property
    def provides(self) -> Optional[Provider]: ...
    def set_provides(self, provides: Optional[Provider]) -> _Self: ...

class ProvidedInstance(Provider, ProvidedInstanceFluentInterface):
    def __init__(self, provides: Optional[Provider] = None) -> None: ...

class AttributeGetter(Provider, ProvidedInstanceFluentInterface):
    def __init__(
        self, provides: Optional[Provider] = None, name: Optional[str] = None
    ) -> None: ...
    @property
    def name(self) -> Optional[str]: ...
    def set_name(self, name: Optional[str]) -> _Self: ...

class ItemGetter(Provider, ProvidedInstanceFluentInterface):
    def __init__(
        self, provides: Optional[Provider] = None, name: Optional[str] = None
    ) -> None: ...
    @property
    def name(self) -> Optional[str]: ...
    def set_name(self, name: Optional[str]) -> _Self: ...

class MethodCaller(Provider, ProvidedInstanceFluentInterface):
    def __init__(
        self, provides: Optional[Provider] = None, *args: Injection, **kwargs: Injection
    ) -> None: ...

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

class BaseSingletonResetContext(Generic[T]):
    def __init__(self, provider: T): ...
    def __enter__(self) -> T: ...
    def __exit__(self, *_: Any) -> None: ...

class SingletonResetContext(BaseSingletonResetContext): ...
class SingletonFullResetContext(BaseSingletonResetContext): ...

CHILD_PROVIDERS: Tuple[Provider]

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[Any, Any]] = None) -> Any: ...
def deepcopy_args(
    provider: Provider[Any],
    args: Tuple[Any, ...],
    memo: Optional[_Dict[int, Any]] = None,
) -> Tuple[Any, ...]: ...
def deepcopy_kwargs(
    provider: Provider[Any],
    kwargs: _Dict[str, Any],
    memo: Optional[_Dict[int, Any]] = None,
) -> Dict[str, Any]: ...
def merge_dicts(dict1: _Dict[Any, Any], dict2: _Dict[Any, Any]) -> _Dict[Any, Any]: ...
def traverse(
    *providers: Provider, types: Optional[_Iterable[Type]] = None
) -> _Iterator[Provider]: ...

if yaml:
    class YamlLoader(yaml.SafeLoader): ...

else:
    class YamlLoader: ...

if pydantic:
    PydanticSettings = pydantic.BaseSettings
else:
    PydanticSettings = Any
