"""
Integration tests for code mode functionality.

These tests verify the end-to-end functionality of code mode with MCPClient.
"""

import pytest

from mcp_use.client.client import MCPClient


class TestCodeModeIntegration:
    """Integration tests for code mode with MCPClient."""

    def test_client_initialization_with_code_mode(self):
        """Test that MCPClient can be initialized with code_mode=True."""
        client = MCPClient(code_mode=True)

        assert client.code_mode is True
        assert client._code_executor is None  # Lazy initialization

    def test_client_initialization_without_code_mode(self):
        """Test that MCPClient defaults to code_mode=False."""
        client = MCPClient()

        assert client.code_mode is False

    @pytest.mark.asyncio
    async def test_execute_code_requires_code_mode(self):
        """Test that execute_code raises error when code_mode is False."""
        client = MCPClient(code_mode=False)

        with pytest.raises(RuntimeError, match="Code execution mode is not enabled"):
            await client.execute_code("return 1")

    @pytest.mark.asyncio
    async def test_execute_code_with_code_mode_enabled(self):
        """Test that execute_code works when code_mode is True."""
        client = MCPClient(code_mode=True)

        result = await client.execute_code("return 42")

        assert result["error"] is None
        assert result["result"] == 42
        assert "execution_time" in result
        assert "logs" in result

    @pytest.mark.asyncio
    async def test_search_tools_empty_sessions(self):
        """Test search_tools with no active MCP sessions returns code_mode tools."""
        client = MCPClient(code_mode=True)

        result = await client.search_tools()

        # Code mode tools should be present even without MCP sessions
        assert result["meta"]["namespaces"] == ["code_mode"]
        tool_names = [t["name"] for t in result["results"]]
        assert "execute_code" in tool_names
        assert "search_tools" in tool_names

    def test_from_dict_with_code_mode(self):
        """Test creating client from dict with code_mode."""
        config = {"mcpServers": {}}

        client = MCPClient.from_dict(config, code_mode=True)

        assert client.code_mode is True

    def test_from_config_file_with_code_mode(self, tmp_path):
        """Test creating client from config file with code_mode."""
        # Create a temporary config file
        config_file = tmp_path / "config.json"
        config_file.write_text('{"mcpServers": {}}')

        client = MCPClient.from_config_file(str(config_file), code_mode=True)

        assert client.code_mode is True


class TestCodeModeExecutionIntegration:
    """Integration tests for actual code execution."""

    @pytest.mark.asyncio
    async def test_execute_simple_calculation(self):
        """Test executing a simple calculation."""
        client = MCPClient(code_mode=True)

        result = await client.execute_code("""
a = 10
b = 20
return a + b
""")

        assert result["error"] is None
        assert result["result"] == 30

    @pytest.mark.asyncio
    async def test_execute_with_print_statements(self):
        """Test that print statements are captured in logs."""
        client = MCPClient(code_mode=True)

        result = await client.execute_code("""
print("Starting calculation")
x = 5 * 5
print(f"Result is {x}")
return x
""")

        assert result["error"] is None
        assert result["result"] == 25
        assert "Starting calculation" in result["logs"]
        assert "Result is 25" in result["logs"]

    @pytest.mark.asyncio
    async def test_execute_with_data_processing(self):
        """Test executing code that processes data."""
        client = MCPClient(code_mode=True)

        result = await client.execute_code("""
# Process a list of numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(f"Processing {len(numbers)} numbers")

# Filter even numbers
evens = [n for n in numbers if n % 2 == 0]
print(f"Found {len(evens)} even numbers")

# Calculate statistics
total = sum(evens)
average = total / len(evens)

return {
    "evens": evens,
    "total": total,
    "average": average
}
""")

        assert result["error"] is None
        assert result["result"]["evens"] == [2, 4, 6, 8, 10]
        assert result["result"]["total"] == 30
        assert result["result"]["average"] == 6.0
        assert len(result["logs"]) >= 2

    @pytest.mark.asyncio
    async def test_execute_with_error(self):
        """Test error handling in code execution."""
        client = MCPClient(code_mode=True)

        result = await client.execute_code("""
x = 1 / 0  # This will raise an error
return x
""")

        assert result["error"] is not None
        assert "division" in result["error"].lower() or "zero" in result["error"].lower()

    @pytest.mark.asyncio
    async def test_code_executor_lazy_initialization(self):
        """Test that CodeExecutor is lazily initialized."""
        client = MCPClient(code_mode=True)

        assert client._code_executor is None

        await client.execute_code("return 1")

        assert client._code_executor is not None

    @pytest.mark.asyncio
    async def test_multiple_executions_reuse_executor(self):
        """Test that multiple executions reuse the same executor."""
        client = MCPClient(code_mode=True)

        result1 = await client.execute_code("return 1")
        executor1 = client._code_executor

        result2 = await client.execute_code("return 2")
        executor2 = client._code_executor

        assert result1["result"] == 1
        assert result2["result"] == 2
        assert executor1 is executor2  # Same instance


class TestSearchToolsIntegration:
    """Integration tests for search_tools functionality."""

    @pytest.mark.asyncio
    async def test_search_tools_with_different_detail_levels(self):
        """Test search_tools with different detail levels returns correct structure."""
        client = MCPClient(code_mode=True)

        # Test different detail levels - code_mode tools should be present
        names_result = await client.search_tools("", detail_level="names")
        descriptions_result = await client.search_tools("", detail_level="descriptions")
        full_result = await client.search_tools("", detail_level="full")

        # All should have code_mode tools
        assert len(names_result["results"]) >= 2
        assert len(descriptions_result["results"]) >= 2
        assert len(full_result["results"]) >= 2

        # Check detail level differences in structure
        # "names" should have minimal info
        names_tool = names_result["results"][0]
        assert "name" in names_tool
        assert "server" in names_tool

        # "descriptions" should include description
        desc_tool = descriptions_result["results"][0]
        assert "name" in desc_tool
        assert "description" in desc_tool

        # "full" should include input_schema
        full_tool = full_result["results"][0]
        assert "name" in full_tool
        assert "description" in full_tool
        assert "input_schema" in full_tool
