"""Tests for mcpgateway-sdk client."""

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

from mcpgateway_sdk.client import GatewayClient


class TestGatewayClient:
    @pytest.mark.asyncio
    async def test_execute_tool(self):
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {"result": {"records": []}, "tool_name": "test"}
        mock_resp.raise_for_status = MagicMock()

        with patch.object(client._client, "post", new_callable=AsyncMock, return_value=mock_resp):
            result = await client.execute_tool("SALESFORCE__opps", {"query": "test"})
            assert result == {"records": []}

    @pytest.mark.asyncio
    async def test_search_tools(self):
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {"tools": [{"name": "tool_a"}]}
        mock_resp.raise_for_status = MagicMock()

        with patch.object(client._client, "post", new_callable=AsyncMock, return_value=mock_resp):
            result = await client.search_tools("pipeline")
            assert len(result) == 1

    @pytest.mark.asyncio
    async def test_cache_get_found(self):
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {"value": {"data": 42}}
        mock_resp.raise_for_status = MagicMock()

        with patch.object(client._client, "get", new_callable=AsyncMock, return_value=mock_resp):
            result = await client.cache_get("my_key")
            assert result == {"data": 42}

    @pytest.mark.asyncio
    async def test_cache_get_not_found(self):
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")
        mock_resp = MagicMock()
        mock_resp.status_code = 404

        with patch.object(client._client, "get", new_callable=AsyncMock, return_value=mock_resp):
            result = await client.cache_get("missing_key")
            assert result is None

    @pytest.mark.asyncio
    async def test_cache_set(self):
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")
        mock_resp = MagicMock()
        mock_resp.status_code = 204
        mock_resp.raise_for_status = MagicMock()

        with patch.object(client._client, "put", new_callable=AsyncMock, return_value=mock_resp):
            await client.cache_set("my_key", {"data": 42}, ttl=300)
            # No exception = success

    @pytest.mark.asyncio
    async def test_cache_get_graceful_degradation(self):
        """Cache get returns None on HTTP errors."""
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")

        import httpx

        with patch.object(
            client._client,
            "get",
            new_callable=AsyncMock,
            side_effect=httpx.ConnectError("Redis down"),
        ):
            result = await client.cache_get("key")
            assert result is None

    @pytest.mark.asyncio
    async def test_execute_tool_raises_on_error_field(self):
        """execute_tool raises RuntimeError when response contains error."""
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {
            "result": None,
            "tool_name": "SALESFORCE__opps",
            "error": "Tool failed: connection refused",
        }
        mock_resp.raise_for_status = MagicMock()

        with patch.object(client._client, "post", new_callable=AsyncMock, return_value=mock_resp):
            with pytest.raises(RuntimeError, match="Tool execution failed"):
                await client.execute_tool("SALESFORCE__opps", {"query": "test"})

    @pytest.mark.asyncio
    async def test_execute_tool_no_error_when_field_is_none(self):
        """execute_tool returns result normally when error is None."""
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {
            "result": {"records": [{"id": 1}]},
            "tool_name": "SALESFORCE__opps",
            "error": None,
        }
        mock_resp.raise_for_status = MagicMock()

        with patch.object(client._client, "post", new_callable=AsyncMock, return_value=mock_resp):
            result = await client.execute_tool("SALESFORCE__opps", {"query": "test"})
            assert result == {"records": [{"id": 1}]}

    @pytest.mark.asyncio
    async def test_execute_tool_no_error_when_field_is_empty_string(self):
        """execute_tool returns result normally when error is empty string."""
        client = GatewayClient(token="test_token", base_url="http://localhost:8000")
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {
            "result": {"data": "ok"},
            "tool_name": "test",
            "error": "",
        }
        mock_resp.raise_for_status = MagicMock()

        with patch.object(client._client, "post", new_callable=AsyncMock, return_value=mock_resp):
            result = await client.execute_tool("TEST__tool", {})
            assert result == {"data": "ok"}

    @pytest.mark.asyncio
    async def test_auto_configures_from_env(self):
        """Client reads token and URL from environment variables."""
        with patch.dict(
            "os.environ",
            {
                "MCPGATEWAY_TOKEN": "env_token_123",
                "MCPGATEWAY_URL": "http://gateway:8000",
            },
        ):
            client = GatewayClient()
            assert client._token == "env_token_123"
            assert client._base_url == "http://gateway:8000"

    @pytest.mark.asyncio
    async def test_context_manager(self):
        """Client supports async context manager."""
        async with GatewayClient(token="test", base_url="http://localhost:8000") as client:
            assert client._token == "test"
