import inspect
import typing
from collections.abc import Awaitable, Callable, Iterator
from typing import TYPE_CHECKING, Final, cast, overload

from wirio._service_lookup._typed_type import TypedType
from wirio._utils._extra_dependencies import ExtraDependencies
from wirio.exceptions import (
    NoKeyedSingletonServiceRegisteredError,
    NoSingletonServiceRegisteredError,
)
from wirio.service_descriptor import ServiceDescriptor
from wirio.service_lifetime import ServiceLifetime
from wirio.service_provider import ServiceProvider
from wirio.wirio_undefined import WirioUndefined

if TYPE_CHECKING:
    from fastapi import FastAPI

    from wirio.integrations._fastapi_dependency_injection import (
        FastApiDependencyInjection,
    )
else:
    FastAPI = None
    FastApiDependencyInjection = None


class ServiceCollection:
    """Collection of service descriptors provided during configuration."""

    _descriptors: Final[list[ServiceDescriptor]]

    def __init__(self) -> None:
        self._descriptors = []
        self._validate_on_build = True

    def build_service_provider(
        self, validate_scopes: bool = True, validate_on_build: bool = True
    ) -> ServiceProvider:
        """Create a :class:`ServiceProvider` containing services from the provided :class:`ServiceCollection`."""
        return ServiceProvider(
            descriptors=self._descriptors,
            validate_scopes=validate_scopes,
            validate_on_build=validate_on_build,
        )

    @overload
    def add_transient[TService](self, service_type: type[TService], /) -> None: ...

    @overload
    def add_transient[TService](
        self,
        service_type: type[TService],
        implementation_factory: Callable[..., Awaitable[TService]],
        /,
    ) -> None: ...

    @overload
    def add_transient[TService](
        self,
        service_type: type[TService],
        implementation_factory: Callable[..., TService],
        /,
    ) -> None: ...

    @overload
    def add_transient[TService](
        self,
        implementation_factory: Callable[..., Awaitable[TService]],
        /,
    ) -> None: ...

    @overload
    def add_transient[TService](
        self,
        implementation_factory: Callable[..., TService],
        /,
    ) -> None: ...

    @overload
    def add_transient[TService](
        self,
        service_type: type[TService],
        implementation_type: type,
        /,
    ) -> None: ...

    def add_transient[TService](
        self,
        service_type_or_implementation_factory: type[TService]
        | Callable[..., Awaitable[TService]]
        | Callable[..., TService],
        implementation_factory_or_implementation_type_or_none: Callable[
            ..., Awaitable[TService]
        ]
        | Callable[..., TService]
        | type
        | None = None,
        /,
    ) -> None:
        """Add a transient service."""
        self._add_from_overloaded_constructor(
            lifetime=ServiceLifetime.TRANSIENT,
            service_type_or_implementation_factory=service_type_or_implementation_factory,
            implementation_factory_or_implementation_type_or_implementation_instance_or_none=implementation_factory_or_implementation_type_or_none,
        )

    @overload
    def add_singleton[TService](self, service_type: type[TService], /) -> None: ...

    @overload
    def add_singleton[TService](
        self,
        service_type: type[TService],
        implementation_factory: Callable[..., Awaitable[TService]],
        /,
    ) -> None: ...

    @overload
    def add_singleton[TService](
        self,
        service_type: type[TService],
        implementation_factory: Callable[..., TService],
        /,
    ) -> None: ...

    @overload
    def add_singleton[TService](
        self,
        implementation_factory: Callable[..., Awaitable[TService]],
        /,
    ) -> None: ...

    @overload
    def add_singleton[TService](
        self,
        implementation_factory: Callable[..., TService],
        /,
    ) -> None: ...

    @overload
    def add_singleton[TService](
        self,
        service_type: type[TService],
        implementation_type: type,
        /,
    ) -> None: ...

    @overload
    def add_singleton[TService](
        self,
        service_type: type[TService],
        implementation_instance: object,
        /,
    ) -> None: ...

    def add_singleton[TService](
        self,
        service_type_or_implementation_factory: type[TService]
        | Callable[..., Awaitable[TService]]
        | Callable[..., TService],
        implementation_factory_or_implementation_type_or_implementation_instance_or_none: Callable[
            ..., Awaitable[TService]
        ]
        | Callable[..., TService]
        | type
        | object
        | None = None,
        /,
    ) -> None:
        """Add a singleton service."""
        self._add_from_overloaded_constructor(
            lifetime=ServiceLifetime.SINGLETON,
            service_type_or_implementation_factory=service_type_or_implementation_factory,
            implementation_factory_or_implementation_type_or_implementation_instance_or_none=implementation_factory_or_implementation_type_or_implementation_instance_or_none,
        )

    @overload
    def add_scoped[TService](self, service_type: type[TService], /) -> None: ...

    @overload
    def add_scoped[TService](
        self,
        service_type: type[TService],
        implementation_factory: Callable[..., Awaitable[TService]],
        /,
    ) -> None: ...

    @overload
    def add_scoped[TService](
        self,
        service_type: type[TService],
        implementation_factory: Callable[..., TService],
        /,
    ) -> None: ...

    @overload
    def add_scoped[TService](
        self,
        implementation_factory: Callable[..., Awaitable[TService]],
        /,
    ) -> None: ...

    @overload
    def add_scoped[TService](
        self,
        implementation_factory: Callable[..., TService],
        /,
    ) -> None: ...

    @overload
    def add_scoped[TService](
        self,
        service_type: type[TService],
        implementation_type: type,
        /,
    ) -> None: ...

    def add_scoped[TService](
        self,
        service_type_or_implementation_factory: type[TService]
        | Callable[..., Awaitable[TService]]
        | Callable[..., TService],
        implementation_factory_or_implementation_type_or_none: Callable[
            ..., Awaitable[TService]
        ]
        | Callable[..., TService]
        | type
        | None = None,
        /,
    ) -> None:
        """Add a scoped service."""
        self._add_from_overloaded_constructor(
            lifetime=ServiceLifetime.SCOPED,
            service_type_or_implementation_factory=service_type_or_implementation_factory,
            implementation_factory_or_implementation_type_or_implementation_instance_or_none=implementation_factory_or_implementation_type_or_none,
        )

    @overload
    def add_keyed_transient[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_transient[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type: type[TService],
        implementation_factory: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ],
        /,
    ) -> None: ...

    @overload
    def add_keyed_transient[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type: type[TService],
        implementation_factory: Callable[[TKey | None, *TFactoryParameter], TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_transient[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        implementation_factory: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ],
        /,
    ) -> None: ...

    @overload
    def add_keyed_transient[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        implementation_factory: Callable[[TKey | None, *TFactoryParameter], TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_transient[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        implementation_type: type,
        /,
    ) -> None: ...

    def add_keyed_transient[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type_or_implementation_factory: type[TService]
        | Callable[[TKey | None, *TFactoryParameter], Awaitable[TService]]
        | Callable[[TKey | None, *TFactoryParameter], TService],
        implementation_factory_or_implementation_type_or_none: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ]
        | Callable[[TKey | None, *TFactoryParameter], TService]
        | type
        | None = None,
        /,
    ) -> None:
        """Add a keyed transient service."""
        self._add_from_overloaded_constructor(
            lifetime=ServiceLifetime.TRANSIENT,
            service_type_or_implementation_factory=service_type_or_implementation_factory,
            implementation_factory_or_implementation_type_or_implementation_instance_or_none=implementation_factory_or_implementation_type_or_none,
            service_key=service_key,
        )

    @overload
    def add_keyed_singleton[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type: type[TService],
        implementation_factory: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ],
        /,
    ) -> None: ...

    @overload
    def add_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type: type[TService],
        implementation_factory: Callable[[TKey | None, *TFactoryParameter], TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        implementation_factory: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ],
        /,
    ) -> None: ...

    @overload
    def add_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        implementation_factory: Callable[[TKey | None, *TFactoryParameter], TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_singleton[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        implementation_type: type,
        /,
    ) -> None: ...

    @overload
    def add_keyed_singleton[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        implementation_instance: object,
        /,
    ) -> None: ...

    def add_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type_or_implementation_factory: type[TService]
        | Callable[[TKey | None, *TFactoryParameter], Awaitable[TService]]
        | Callable[[TKey | None, *TFactoryParameter], TService],
        implementation_factory_or_implementation_type_or_implementation_instance_or_none: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ]
        | Callable[[TKey | None, *TFactoryParameter], TService]
        | type
        | object
        | None = None,
        /,
    ) -> None:
        """Add a keyed singleton service."""
        self._add_from_overloaded_constructor(
            lifetime=ServiceLifetime.SINGLETON,
            service_type_or_implementation_factory=service_type_or_implementation_factory,
            implementation_factory_or_implementation_type_or_implementation_instance_or_none=implementation_factory_or_implementation_type_or_implementation_instance_or_none,
            service_key=service_key,
        )

    @overload
    def add_keyed_scoped[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_scoped[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type: type[TService],
        implementation_factory: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ],
        /,
    ) -> None: ...

    @overload
    def add_keyed_scoped[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type: type[TService],
        implementation_factory: Callable[[TKey | None, *TFactoryParameter], TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_scoped[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        implementation_factory: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ],
        /,
    ) -> None: ...

    @overload
    def add_keyed_scoped[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        implementation_factory: Callable[[TKey | None, *TFactoryParameter], TService],
        /,
    ) -> None: ...

    @overload
    def add_keyed_scoped[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        implementation_type: type,
        /,
    ) -> None: ...

    def add_keyed_scoped[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type_or_implementation_factory: type[TService]
        | Callable[[TKey | None, *TFactoryParameter], Awaitable[TService]]
        | Callable[[TKey | None, *TFactoryParameter], TService],
        implementation_factory_or_implementation_type_or_none: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ]
        | Callable[..., TService]
        | type
        | None = None,
        /,
    ) -> None:
        """Add a keyed scoped service."""
        self._add_from_overloaded_constructor(
            lifetime=ServiceLifetime.SCOPED,
            service_type_or_implementation_factory=service_type_or_implementation_factory,
            implementation_factory_or_implementation_type_or_implementation_instance_or_none=implementation_factory_or_implementation_type_or_none,
            service_key=service_key,
        )

    @overload
    def add_auto_activated_singleton[TService](
        self, service_type: type[TService], /
    ) -> None: ...

    @overload
    def add_auto_activated_singleton[TService](
        self,
        service_type: type[TService],
        implementation_factory: Callable[..., Awaitable[TService]],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_singleton[TService](
        self,
        service_type: type[TService],
        implementation_factory: Callable[..., TService],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_singleton[TService](
        self,
        implementation_factory: Callable[..., Awaitable[TService]],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_singleton[TService](
        self,
        implementation_factory: Callable[..., TService],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_singleton[TService](
        self,
        service_type: type[TService],
        implementation_type: type,
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_singleton[TService](
        self,
        service_type: type[TService],
        implementation_instance: object,
        /,
    ) -> None: ...

    def add_auto_activated_singleton[TService](
        self,
        service_type_or_implementation_factory: type[TService]
        | Callable[..., Awaitable[TService]]
        | Callable[..., TService],
        implementation_factory_or_implementation_type_or_implementation_instance_or_none: Callable[
            ..., Awaitable[TService]
        ]
        | Callable[..., TService]
        | type
        | object
        | None = None,
        /,
    ) -> None:
        """Add an auto-activated singleton service.

        An auto-activated singleton service is instantiated when the service provider is built (eagerly), rather than when it's first requested (lazily).
        """
        self._add_from_overloaded_constructor(
            lifetime=ServiceLifetime.SINGLETON,
            service_type_or_implementation_factory=service_type_or_implementation_factory,
            implementation_factory_or_implementation_type_or_implementation_instance_or_none=implementation_factory_or_implementation_type_or_implementation_instance_or_none,
            auto_activate=True,
        )

    @overload
    def add_auto_activated_keyed_singleton[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type: type[TService],
        implementation_factory: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type: type[TService],
        implementation_factory: Callable[[TKey | None, *TFactoryParameter], TService],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        implementation_factory: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        implementation_factory: Callable[[TKey | None, *TFactoryParameter], TService],
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_keyed_singleton[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        implementation_type: type,
        /,
    ) -> None: ...

    @overload
    def add_auto_activated_keyed_singleton[TKey, TService](
        self,
        service_key: TKey | None,  # pyright: ignore[reportInvalidTypeVarUse]
        service_type: type[TService],
        implementation_instance: object,
        /,
    ) -> None: ...

    def add_auto_activated_keyed_singleton[TKey, TService, *TFactoryParameter](
        self,
        service_key: TKey | None,
        service_type_or_implementation_factory: type[TService]
        | Callable[[TKey | None, *TFactoryParameter], Awaitable[TService]]
        | Callable[[TKey | None, *TFactoryParameter], TService],
        implementation_factory_or_implementation_type_or_implementation_instance_or_none: Callable[
            [TKey | None, *TFactoryParameter], Awaitable[TService]
        ]
        | Callable[[TKey | None, *TFactoryParameter], TService]
        | type
        | object
        | None = None,
        /,
    ) -> None:
        """Add an auto-activated keyed singleton service.

        An auto-activated keyed singleton service is instantiated when the service provider is built (eagerly), rather than when it's first requested (lazily).
        """
        self._add_from_overloaded_constructor(
            lifetime=ServiceLifetime.SINGLETON,
            service_type_or_implementation_factory=service_type_or_implementation_factory,
            implementation_factory_or_implementation_type_or_implementation_instance_or_none=implementation_factory_or_implementation_type_or_implementation_instance_or_none,
            service_key=service_key,
            auto_activate=True,
        )

    def enable_singleton_auto_activation(self, service_type: type) -> None:
        """Mark a registered singleton service as auto-activated.

        An auto-activated singleton service is instantiated when the service provider is built (eagerly), rather than when it's first requested (lazily).
        """
        typed_service_type = TypedType.from_type(service_type)
        is_descriptor_found = False

        for descriptor in self._descriptors:
            if (
                descriptor.service_type == typed_service_type
                and descriptor.lifetime == ServiceLifetime.SINGLETON
            ):
                is_descriptor_found = True
                descriptor.auto_activate = True

        if not is_descriptor_found:
            raise NoSingletonServiceRegisteredError(typed_service_type)

    def enable_keyed_singleton_auto_activation(
        self, service_key: object | None, service_type: type
    ) -> None:
        """Mark a registered keyed singleton service as auto-activated.

        An auto-activated keyed singleton service is instantiated when the service provider is built (eagerly), rather than when it's first requested (lazily).
        """
        typed_service_type = TypedType.from_type(service_type)
        is_descriptor_found = False

        for descriptor in self._descriptors:
            if (
                descriptor.is_keyed_service
                and descriptor.lifetime == ServiceLifetime.SINGLETON
                and descriptor.service_type == typed_service_type
                and descriptor.service_key == service_key
            ):
                is_descriptor_found = True
                descriptor.auto_activate = True

        if not is_descriptor_found:
            raise NoKeyedSingletonServiceRegisteredError(
                service_type=typed_service_type, service_key_type=type(service_key)
            )

    def configure_fastapi(self, app: FastAPI) -> None:
        """Configure the FastAPI application to use dependency injection using the services from this service collection."""
        self._import_fastapi()
        FastApiDependencyInjection.setup(app, self)

    def _add_from_overloaded_constructor[TService](
        self,
        lifetime: ServiceLifetime,
        service_type_or_implementation_factory: type[TService]
        | Callable[..., Awaitable[TService]]
        | Callable[..., TService],
        implementation_factory_or_implementation_type_or_implementation_instance_or_none: Callable[
            ..., Awaitable[TService]
        ]
        | Callable[..., TService]
        | type
        | object
        | None = None,
        service_key: object | None = WirioUndefined.INSTANCE,
        auto_activate: bool = False,
    ) -> None:
        service_type_to_add: type[TService] | None = None
        implementation_factory_to_add: (
            Callable[..., Awaitable[TService]] | Callable[..., TService] | None
        ) = None
        implementation_type_to_add: type | None = None
        implementation_instance_to_add: object | None = None

        if isinstance(service_type_or_implementation_factory, type):
            service_type_to_add = service_type_or_implementation_factory

        if isinstance(
            implementation_factory_or_implementation_type_or_implementation_instance_or_none,
            type,
        ):
            implementation_type_to_add = implementation_factory_or_implementation_type_or_implementation_instance_or_none
        elif (
            service_type_to_add is not None
            and implementation_factory_or_implementation_type_or_implementation_instance_or_none
            is not None
        ):
            if callable(
                implementation_factory_or_implementation_type_or_implementation_instance_or_none
            ):
                implementation_factory_to_add = cast(
                    "Callable[..., Awaitable[TService]] | Callable[..., TService]",
                    implementation_factory_or_implementation_type_or_implementation_instance_or_none,
                )
            else:
                implementation_instance_to_add = implementation_factory_or_implementation_type_or_implementation_instance_or_none
        elif (
            service_type_to_add is None
            and implementation_factory_or_implementation_type_or_implementation_instance_or_none
            is None
        ):
            implementation_factory_to_add = service_type_or_implementation_factory

        self._add(
            lifetime=lifetime,
            service_type=service_type_to_add,
            implementation_factory=implementation_factory_to_add,
            implementation_type=implementation_type_to_add,
            implementation_instance=implementation_instance_to_add,
            service_key=service_key,
            auto_activate=auto_activate,
        )

    def _add[TService](  # noqa: PLR0913
        self,
        lifetime: ServiceLifetime,
        service_type: type[TService] | None,
        implementation_factory: Callable[..., Awaitable[TService]]
        | Callable[..., TService]
        | None,
        implementation_type: type | None,
        implementation_instance: object | None,
        service_key: object | None,
        auto_activate: bool,
    ) -> None:
        provided_service_type = self._get_provided_service_type(
            service_type, implementation_factory
        )
        is_service_key_provided = service_key is not WirioUndefined.INSTANCE
        service_key_to_add = service_key if is_service_key_provided else None
        service_descriptor: ServiceDescriptor | None = None

        if implementation_instance is not None:
            service_descriptor = ServiceDescriptor.from_implementation_instance(
                service_type=provided_service_type,
                implementation_instance=implementation_instance,
                service_key=service_key_to_add,
                lifetime=lifetime,
                auto_activate=auto_activate,
            )
        elif implementation_factory is None:
            if implementation_type is not None and (
                not issubclass(implementation_type, provided_service_type)
                or provided_service_type == implementation_type
            ):
                error_message = (
                    f"{implementation_type} is not subclass of {provided_service_type}"
                )
                raise TypeError(error_message)

            implementation_type_to_add = (
                implementation_type
                if implementation_type is not None
                else provided_service_type
            )
            service_descriptor = ServiceDescriptor.from_implementation_type(
                service_type=provided_service_type,
                implementation_type=implementation_type_to_add,
                service_key=service_key_to_add,
                lifetime=lifetime,
                auto_activate=auto_activate,
            )
        elif inspect.iscoroutinefunction(implementation_factory):
            if is_service_key_provided:
                service_descriptor = (
                    ServiceDescriptor.from_keyed_async_implementation_factory(
                        service_type=provided_service_type,
                        implementation_factory=implementation_factory,
                        service_key=service_key_to_add,
                        lifetime=lifetime,
                        auto_activate=auto_activate,
                    )
                )
            else:
                service_descriptor = (
                    ServiceDescriptor.from_async_implementation_factory(
                        service_type=provided_service_type,
                        implementation_factory=implementation_factory,
                        lifetime=lifetime,
                        auto_activate=auto_activate,
                    )
                )
        else:  # noqa: PLR5501
            if is_service_key_provided:
                service_descriptor = (
                    ServiceDescriptor.from_keyed_sync_implementation_factory(
                        service_type=provided_service_type,
                        implementation_factory=implementation_factory,
                        service_key=service_key_to_add,
                        lifetime=lifetime,
                        auto_activate=auto_activate,
                    )
                )
            else:
                service_descriptor = ServiceDescriptor.from_sync_implementation_factory(
                    service_type=provided_service_type,
                    implementation_factory=implementation_factory,
                    lifetime=lifetime,
                    auto_activate=auto_activate,
                )

        assert service_descriptor is not None
        self._descriptors.append(service_descriptor)

    def _get_provided_service_type[TService](
        self,
        service_type: type[TService] | None = None,
        implementation_factory: Callable[..., Awaitable[TService]]
        | Callable[..., TService]
        | None = None,
    ) -> type:
        if service_type is not None:
            return service_type

        assert implementation_factory is not None

        type_hints: dict[str, type] = typing.get_type_hints(implementation_factory)
        return_type = type_hints.get("return")

        if return_type is None:
            error_message = "Missing return type hints from 'implementation_factory'"
            raise ValueError(error_message)

        return return_type

    def _import_fastapi(self) -> None:
        ExtraDependencies.import_fastapi()
        global FastApiDependencyInjection  # noqa: PLW0603
        from wirio.integrations._fastapi_dependency_injection import (  # noqa: PLC0415
            FastApiDependencyInjection,
        )

    def __iter__(self) -> Iterator[ServiceDescriptor]:
        return iter(self._descriptors)
