from typing import Annotated

import pytest

from zayt.di.container import Container
from zayt.di.error import ServiceAlreadyRegisteredError, ServiceNotFoundError
from zayt.di.inject import Inject


class NamedService:
    pass


class ServiceWithNamedDep:
    dependent: Annotated[NamedService, Inject("1")]


class Interface:
    pass


class Impl1(Interface):
    pass


class Impl2(Interface):
    pass


async def test_dependency_with_name(ioc: Container):
    ioc.register(NamedService, name="1")
    ioc.register(ServiceWithNamedDep)

    instance = await ioc.get(ServiceWithNamedDep)
    dependent = instance.dependent
    assert isinstance(dependent, NamedService)


async def test_unnamed_dependency_with_named_service_should_fail(ioc: Container):
    class Service:
        pass

    class DependentService:
        dependent: Annotated[Service, Inject("1")]

    ioc.register(NamedService)
    ioc.register(DependentService)

    with pytest.raises(ServiceNotFoundError):
        await ioc.get(DependentService)


async def test_named_dependency_with_unnamed_service_should_fail(ioc: Container):
    class Service:
        pass

    class DependentService:
        dependent: Annotated[Service, Inject]

    ioc.register(NamedService, name="1")
    ioc.register(DependentService)

    with pytest.raises(ServiceNotFoundError):
        await ioc.get(DependentService)


async def test_register_two_services_with_the_same_name_should_fail(ioc: Container):
    ioc.register(NamedService)
    with pytest.raises(ServiceAlreadyRegisteredError):
        ioc.register(NamedService)


async def test_dependencies_with_same_interface(ioc: Container):
    ioc.register(Impl1, provides=Interface, name="impl1")
    ioc.register(Impl2, provides=Interface, name="impl2")

    service1 = await ioc.get(Interface, name="impl1")
    service2 = await ioc.get(Interface, name="impl2")

    assert service1 is not service2
