# #region Test.SupersetClientRegistry [C:3] [TYPE Module] [SEMANTICS test,registry,superset,client,async]
# @BRIEF Tests for core/utils/client_registry.py — get_client, get_superset_client, get_semaphore, get_auth_lock, shutdown.
# @RELATION BINDS_TO -> [SupersetClientRegistryModule]

from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

from unittest.mock import AsyncMock, MagicMock, patch
import asyncio
import pytest


class TestBuildEnvId:
    """_build_env_id — stable identifier from env config."""

    def test_env_with_id(self):
        from src.core.utils.client_registry import _build_env_id
        env = MagicMock()
        env.id = "env-1"
        env.base_url = "http://superset:8088"
        result = _build_env_id(env)
        assert "env-1" in result
        assert "superset" in result

    def test_env_without_id_uses_object_id(self):
        from src.core.utils.client_registry import _build_env_id
        env = {"base_url": "http://x.com", "username": "admin"}
        result = _build_env_id(env)
        assert "x.com" in result
        assert "admin" in result

    def test_dict_env_fallback(self):
        from src.core.utils.client_registry import _build_env_id
        env = {"base_url": "http://test:8080", "username": "user"}
        result = _build_env_id(env)
        assert result


@pytest.mark.asyncio
class TestGetClient:
    """get_client — create or return cached client."""

    async def test_creates_new_client_from_model(self):
        from src.core.utils.client_registry import get_client, shutdown

        env = MagicMock()
        env.id = "env-1"
        env.url = "http://superset:8088"
        env.username = "admin"
        env.password = "pass"
        env.verify_ssl = False
        env.timeout = 30

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_client_cls:
            mock_client = AsyncMock()
            mock_client_cls.return_value = mock_client

            client = await get_client(env)
            assert client is mock_client
            mock_client_cls.assert_called_once()

        await shutdown()

    async def test_returns_cached_client(self):
        from src.core.utils.client_registry import get_client, shutdown, _registry

        env = MagicMock()
        env.id = "env-1"
        env.url = "http://superset:8088"
        env.username = "admin"
        env.password = "pass"
        env.verify_ssl = False
        env.timeout = 30

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_client_cls:
            mock_client = AsyncMock()
            mock_client_cls.return_value = mock_client

            client1 = await get_client(env)
            client2 = await get_client(env)
            assert client1 is client2
            assert mock_client_cls.call_count == 1

        await shutdown()

    async def test_dict_env(self):
        from src.core.utils.client_registry import get_client, shutdown

        env = {"base_url": "http://superset:8088", "username": "admin", "password": "pass"}

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_client_cls:
            mock_client = AsyncMock()
            mock_client_cls.return_value = mock_client

            client = await get_client(env)
            assert client is mock_client

        await shutdown()

    async def test_unknown_env_type(self):
        from src.core.utils.client_registry import get_client, shutdown

        env = 42  # not a model or dict

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_client_cls:
            mock_client = AsyncMock()
            mock_client_cls.return_value = mock_client

            client = await get_client(env)
            assert client is mock_client

        await shutdown()


@pytest.mark.asyncio
class TestGetSupersetClient:
    """get_superset_client — wraps AsyncAPIClient in SupersetClient."""

    async def test_returns_superset_client(self):
        from src.core.utils.client_registry import get_superset_client, shutdown

        env = MagicMock()
        env.id = "env-1"
        env.url = "http://superset:8088"
        env.username = "admin"
        env.password = "pass"
        env.verify_ssl = False
        env.timeout = 30

        mock_shared = AsyncMock()

        with (
            patch("src.core.utils.client_registry.get_client", return_value=mock_shared),
            patch("src.core.superset_client.SupersetClient") as mock_sc_cls,
        ):
            mock_sc = MagicMock()
            mock_sc_cls.return_value = mock_sc

            client = await get_superset_client(env)
            assert client is mock_sc

        await shutdown()


@pytest.mark.asyncio
class TestGetSemaphoreAndLock:
    """get_semaphore and get_auth_lock — return shared resources."""

    async def test_get_semaphore(self):
        from src.core.utils.client_registry import get_semaphore, get_client, shutdown

        env = MagicMock()
        env.id = "env-1"
        env.url = "http://superset:8088"
        env.username = "admin"
        env.password = "pass"
        env.verify_ssl = False
        env.timeout = 30

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
            mock_cls.return_value = AsyncMock()
            await get_client(env)
            sem = await get_semaphore(env)
            assert sem is not None

        await shutdown()

    async def test_get_auth_lock(self):
        from src.core.utils.client_registry import get_auth_lock, get_client, shutdown

        env = MagicMock()
        env.id = "env-1"
        env.url = "http://superset:8088"
        env.username = "admin"
        env.password = "pass"
        env.verify_ssl = False
        env.timeout = 30

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
            mock_cls.return_value = AsyncMock()
            await get_client(env)
            lock = await get_auth_lock(env)
            assert lock is not None

        await shutdown()

    async def test_get_semaphore_creates_client_if_missing(self):
        from src.core.utils.client_registry import get_semaphore, shutdown, _registry

        env = MagicMock()
        env.id = "env-1"
        env.url = "http://superset:8088"
        env.username = "admin"
        env.password = "pass"
        env.verify_ssl = False
        env.timeout = 30

        _registry.clear()

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
            mock_cls.return_value = AsyncMock()
            sem = await get_semaphore(env)
            assert sem is not None

        await shutdown()


@pytest.mark.asyncio
class TestShutdown:
    """shutdown — close all clients and clear registry."""

    async def test_shutdown_closes_clients(self):
        from src.core.utils.client_registry import get_client, shutdown

        env = MagicMock()
        env.id = "env-1"
        env.url = "http://superset:8088"
        env.username = "admin"
        env.password = "pass"
        env.verify_ssl = False
        env.timeout = 30

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
            mock_client = AsyncMock()
            mock_cls.return_value = mock_client
            await get_client(env)
            await shutdown()
            mock_client.aclose.assert_called_once()

    async def test_shutdown_empty_registry(self):
        from src.core.utils.client_registry import shutdown, _registry
        _registry.clear()
        await shutdown()  # should not raise

    async def test_shutdown_handles_close_error(self):
        from src.core.utils.client_registry import get_client, shutdown

        env = MagicMock()
        env.id = "env-1"
        env.url = "http://superset:8088"
        env.username = "admin"
        env.password = "pass"
        env.verify_ssl = False
        env.timeout = 30

        with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
            mock_client = AsyncMock()
            mock_client.aclose.side_effect = ConnectionError("Close failed")
            mock_cls.return_value = mock_client
            await get_client(env)
            await shutdown()  # should not raise
# #endregion Test.SupersetClientRegistry
