from __future__ import annotations

import pytest
from punq import Container

from iisi_app_core import (
    adapter,
    clear_default_container,
    component,
    controller,
    get_default_container,
    policy,
    register,
    repository,
    seed,
    service,
    set_default_container,
    usecase,
)


def setup_function() -> None:
    clear_default_container()


def test_set_get_clear_default_container() -> None:
    container = Container()
    set_default_container(container)
    assert get_default_container() is container
    clear_default_container()
    with pytest.raises(RuntimeError):
        get_default_container()


def test_register_with_explicit_interface_registers_interface_and_concrete() -> None:
    container = Container()

    class Interface:
        pass

    @register(interface=Interface, container=container)
    class Implementation:
        pass

    assert isinstance(container.resolve(Interface), Implementation)
    assert isinstance(container.resolve(Implementation), Implementation)


def test_register_infers_interface_from_first_non_object_base() -> None:
    container = Container()

    class Base:
        pass

    @register(container=container)
    class Implementation(Base):
        pass

    assert isinstance(container.resolve(Base), Implementation)
    assert isinstance(container.resolve(Implementation), Implementation)


def test_register_raises_when_interface_cannot_be_inferred() -> None:
    container = Container()

    with pytest.raises(ValueError):

        @register(container=container)
        class NoBase:
            pass


def test_register_raises_when_no_container_available() -> None:
    clear_default_container()

    class Interface:
        pass

    with pytest.raises(RuntimeError):

        @register(interface=Interface)
        class Implementation:
            pass


@pytest.mark.parametrize(
    "alias",
    [component, adapter, controller, policy, repository, service, usecase, seed],
)
def test_aliases_behave_like_register(alias) -> None:
    container = Container()

    class Interface:
        pass

    @alias(interface=Interface, container=container)
    class Implementation:
        pass

    assert isinstance(container.resolve(Interface), Implementation)
