"""
Tests for AsyncDaisiClient.
"""

from unittest.mock import AsyncMock, patch

import pytest

from daisi.aio import AsyncDaisiClient
from daisi.config import DaisiConfig


class TestAsyncDaisiClientInitialization:
    """Test AsyncDaisiClient initialization."""

    def test_init_with_client_key(self) -> None:
        """Test initialization with client key."""
        client = AsyncDaisiClient(client_key="test-key")
        assert client.config.client_key == "test-key"

    def test_init_with_full_config(self) -> None:
        """Test initialization with full configuration."""
        client = AsyncDaisiClient(
            client_key="test-key",
            orc_address="custom.orc.com",
            orc_port=8443,
            use_ssl=False,
        )
        assert client.config.client_key == "test-key"
        assert client.config.orc_address == "custom.orc.com"
        assert client.config.orc_port == 8443
        assert client.config.use_ssl is False

    def test_all_clients_initialized(self) -> None:
        """Test all sub-clients are initialized."""
        client = AsyncDaisiClient(client_key="test-key")

        assert client.auth is not None
        assert client.session is not None
        assert client.inference is not None
        assert client.models is not None
        assert client.hosts is not None
        assert client.peers is not None
        assert client.settings is not None
        assert client.host_commands is not None
        assert client.app_commands is not None

    def test_all_clients_share_config(self) -> None:
        """Test all sub-clients share the same config."""
        client = AsyncDaisiClient(client_key="test-key")

        assert client.auth.config == client.config
        assert client.session.config == client.config
        assert client.inference.config == client.config
        assert client.models.config == client.config
        assert client.hosts.config == client.config
        assert client.peers.config == client.config
        assert client.settings.config == client.config
        assert client.host_commands.config == client.config
        assert client.app_commands.config == client.config


class TestAsyncDaisiClientClose:
    """Test AsyncDaisiClient close method."""

    @pytest.mark.asyncio
    async def test_close_all_clients(self) -> None:
        """Test close method closes all sub-clients."""
        client = AsyncDaisiClient(client_key="test-key")

        # Mock all client close methods
        client.auth.close = AsyncMock()
        client.session.close = AsyncMock()
        client.inference.close = AsyncMock()
        client.models.close = AsyncMock()
        client.hosts.close = AsyncMock()
        client.peers.close = AsyncMock()
        client.settings.close = AsyncMock()
        client.host_commands.close = AsyncMock()
        client.app_commands.close = AsyncMock()

        await client.close()

        client.auth.close.assert_called_once()
        client.session.close.assert_called_once()
        client.inference.close.assert_called_once()
        client.models.close.assert_called_once()
        client.hosts.close.assert_called_once()
        client.peers.close.assert_called_once()
        client.settings.close.assert_called_once()
        client.host_commands.close.assert_called_once()
        client.app_commands.close.assert_called_once()


class TestAsyncDaisiClientContextManager:
    """Test AsyncDaisiClient async context manager."""

    @pytest.mark.asyncio
    async def test_async_context_manager_entry(self) -> None:
        """Test async context manager returns client on entry."""
        async with AsyncDaisiClient(client_key="test-key") as client:
            assert isinstance(client, AsyncDaisiClient)

    @pytest.mark.asyncio
    async def test_async_context_manager_closes_on_exit(self) -> None:
        """Test async context manager closes client on exit."""
        with patch.object(
            AsyncDaisiClient, "close", new_callable=AsyncMock
        ) as mock_close:
            async with AsyncDaisiClient(client_key="test-key"):
                pass

            mock_close.assert_called_once()

    @pytest.mark.asyncio
    async def test_async_context_manager_closes_on_exception(self) -> None:
        """Test async context manager closes client even on exception."""
        with patch.object(
            AsyncDaisiClient, "close", new_callable=AsyncMock
        ) as mock_close:
            with pytest.raises(ValueError):
                async with AsyncDaisiClient(client_key="test-key"):
                    raise ValueError("Test exception")

            mock_close.assert_called_once()
