"""Tests for container async resources."""

import asyncio

from dependency_injector import containers, providers
from pytest import mark, raises


@mark.asyncio
async def test_init_and_shutdown_ordering():
    """Test init and shutdown resources.

    Methods .init_resources() and .shutdown_resources() should respect resources dependencies.
    Initialization should first initialize resources without dependencies and then provide
    these resources to other resources. Resources shutdown should follow the same rule: first
    shutdown resources without initialized dependencies and then continue correspondingly
    until all resources are shutdown.
    """
    initialized_resources = []
    shutdown_resources = []

    async def _resource(name, delay, **_):
        await asyncio.sleep(delay)
        initialized_resources.append(name)

        yield name

        await asyncio.sleep(delay)
        shutdown_resources.append(name)

    class Container(containers.DeclarativeContainer):
        resource1 = providers.Resource(
            _resource,
            name="r1",
            delay=0.03,
        )
        resource2 = providers.Resource(
            _resource,
            name="r2",
            delay=0.02,
            r1=resource1,
        )
        resource3 = providers.Resource(
            _resource,
            name="r3",
            delay=0.01,
            r2=resource2,
        )

    container = Container()

    await container.init_resources()
    assert initialized_resources == ["r1", "r2", "r3"]
    assert shutdown_resources == []

    await container.shutdown_resources()
    assert initialized_resources == ["r1", "r2", "r3"]
    assert shutdown_resources == ["r3", "r2", "r1"]

    await container.init_resources()
    assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
    assert shutdown_resources == ["r3", "r2", "r1"]

    await container.shutdown_resources()
    assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
    assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]


@mark.asyncio
async def test_shutdown_circular_dependencies_breaker():
    async def _resource(name, **_):
        yield name

    class Container(containers.DeclarativeContainer):
        resource1 = providers.Resource(
            _resource,
            name="r1",
        )
        resource2 = providers.Resource(
            _resource,
            name="r2",
            r1=resource1,
        )
        resource3 = providers.Resource(
            _resource,
            name="r3",
            r2=resource2,
        )

    container = Container()
    await container.init_resources()

    # Create circular dependency after initialization (r3 -> r2 -> r1 -> r3 -> ...)
    container.resource1.add_kwargs(r3=container.resource3)

    with raises(RuntimeError, match="Unable to resolve resources shutdown order"):
        await container.shutdown_resources()


@mark.asyncio
async def test_shutdown_sync_and_async_ordering():
    initialized_resources = []
    shutdown_resources = []

    def _sync_resource(name, **_):
        initialized_resources.append(name)
        yield name
        shutdown_resources.append(name)

    async def _async_resource(name, **_):
        initialized_resources.append(name)
        yield name
        shutdown_resources.append(name)

    class Container(containers.DeclarativeContainer):
        resource1 = providers.Resource(
            _sync_resource,
            name="r1",
        )
        resource2 = providers.Resource(
            _sync_resource,
            name="r2",
            r1=resource1,
        )
        resource3 = providers.Resource(
            _async_resource,
            name="r3",
            r2=resource2,
        )

    container = Container()

    await container.init_resources()
    assert initialized_resources == ["r1", "r2", "r3"]
    assert shutdown_resources == []

    await container.shutdown_resources()
    assert initialized_resources == ["r1", "r2", "r3"]
    assert shutdown_resources == ["r3", "r2", "r1"]

    await container.init_resources()
    assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
    assert shutdown_resources == ["r3", "r2", "r1"]

    await container.shutdown_resources()
    assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
    assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]