"""Live MCP client-server integration tests (subprocess-spawned server).

Hit a real MCP echo-server subprocess — excluded from CI via `-m 'not integration'`.
Run locally with:

    pdm run pytest tests/performance/mcp/test_mcp_client_server_live.py -v -m integration
"""

import sys
from pathlib import Path

import pytest

# Skip all tests if MCP is not available
try:
    import mcp  # noqa: F401

    MCP_AVAILABLE = True
except ImportError:
    MCP_AVAILABLE = False

pytestmark = pytest.mark.skipif(
    not MCP_AVAILABLE, reason="MCP package not installed"
)

# Path to the echo server (co-located with this file).
ECHO_SERVER_PATH = Path(__file__).parent / "echo_server.py"


# =============================================================================
# Subprocess-based integration tests - marked for opt-in execution
# These tests spawn actual MCP servers and test real communication
# =============================================================================


@pytest.mark.integration
class TestRealMCPClientServerCommunication:
    """Tests for actual MCP client-server communication via subprocess.

    These tests are slower and require subprocess spawning. Run with:
        pytest -m integration
    """

    @pytest.fixture
    def echo_server_config(self):
        """Configuration for the echo test server."""
        return {
            "name": "echo-test",
            "command": sys.executable,
            "args": [str(ECHO_SERVER_PATH)],
        }

    @pytest.mark.asyncio
    async def test_connect_to_echo_server(self, echo_server_config):
        """Test connecting to the echo server subprocess."""
        from local_deep_research.mcp.client import MCPClient

        client = MCPClient(echo_server_config, timeout=60.0)

        async with client.connect() as connected:
            assert connected._connected is True

    @pytest.mark.asyncio
    async def test_list_tools_from_echo_server(self, echo_server_config):
        """Test listing tools from the running echo server."""
        from local_deep_research.mcp.client import MCPClient

        client = MCPClient(echo_server_config, timeout=60.0)

        async with client.connect() as connected:
            tools = await connected.list_tools()

            # Verify we got the expected tools
            tool_names = [t["name"] for t in tools]
            assert "echo" in tool_names
            assert "add_numbers" in tool_names
            assert "get_info" in tool_names

    @pytest.mark.asyncio
    async def test_call_echo_tool(self, echo_server_config):
        """Test calling the echo tool on the real server."""
        from local_deep_research.mcp.client import MCPClient

        client = MCPClient(echo_server_config, timeout=60.0)

        async with client.connect() as connected:
            result = await connected.call_tool(
                "echo", {"message": "Hello, MCP!"}
            )

            assert result["status"] == "success"
            # The content should contain the echoed message
            assert "Hello, MCP!" in result["content"]

    @pytest.mark.asyncio
    async def test_call_add_numbers_tool(self, echo_server_config):
        """Test calling the add_numbers tool."""
        from local_deep_research.mcp.client import MCPClient

        client = MCPClient(echo_server_config, timeout=60.0)

        async with client.connect() as connected:
            result = await connected.call_tool("add_numbers", {"a": 5, "b": 3})

            assert result["status"] == "success"
            # Result should contain 8
            assert "8" in result["content"]

    @pytest.mark.asyncio
    async def test_call_get_info_tool(self, echo_server_config):
        """Test calling the get_info tool."""
        from local_deep_research.mcp.client import MCPClient

        client = MCPClient(echo_server_config, timeout=60.0)

        async with client.connect() as connected:
            result = await connected.call_tool("get_info", {})

            assert result["status"] == "success"
            assert "echo-test-server" in result["content"]

    @pytest.mark.asyncio
    async def test_multiple_tool_calls_sequential(self, echo_server_config):
        """Test multiple sequential tool calls on one connection."""
        from local_deep_research.mcp.client import MCPClient

        client = MCPClient(echo_server_config, timeout=60.0)

        async with client.connect() as connected:
            # Call multiple tools sequentially
            result1 = await connected.call_tool("echo", {"message": "First"})
            result2 = await connected.call_tool(
                "add_numbers", {"a": 10, "b": 20}
            )
            result3 = await connected.call_tool("echo", {"message": "Third"})

            assert result1["status"] == "success"
            assert "First" in result1["content"]
            assert result2["status"] == "success"
            assert "30" in result2["content"]
            assert result3["status"] == "success"
            assert "Third" in result3["content"]

    @pytest.mark.asyncio
    async def test_connection_context_manager_cleanup(self, echo_server_config):
        """Test that connection is properly cleaned up after context exit."""
        from local_deep_research.mcp.client import MCPClient

        client = MCPClient(echo_server_config, timeout=60.0)

        async with client.connect() as connected:
            assert connected._connected is True

        # After context exit, should be disconnected
        assert client._connected is False
        assert client._session is None


@pytest.mark.integration
class TestMCPClientServerErrorHandling:
    """Tests for error handling in real client-server communication."""

    @pytest.fixture
    def echo_server_config(self):
        """Configuration for the echo test server."""
        return {
            "name": "echo-test",
            "command": sys.executable,
            "args": [str(ECHO_SERVER_PATH)],
        }

    @pytest.mark.asyncio
    async def test_tool_error_handling(self, echo_server_config):
        """Test handling of errors from tools."""
        from local_deep_research.mcp.client import MCPClient

        client = MCPClient(echo_server_config, timeout=60.0)

        async with client.connect() as connected:
            # Request the tool to fail intentionally
            result = await connected.call_tool(
                "fail_on_demand",
                {"should_fail": True, "error_message": "Test failure"},
            )

            # The MCP SDK returns error results, not exceptions for tool errors
            # Check if the result indicates an error
            assert (
                result["status"] == "error"
                or "error" in result.get("content", "").lower()
            )

    @pytest.mark.asyncio
    async def test_connection_to_invalid_command(self):
        """Test connection failure for invalid command."""
        from local_deep_research.mcp.client import MCPClient, MCPClientError

        config = {
            "name": "invalid",
            "command": "python",
            "args": ["/nonexistent/path/to/server.py"],
        }

        client = MCPClient(config, timeout=10.0)

        with pytest.raises(MCPClientError):
            async with client.connect():
                pass  # Should not reach here
