"""Tests for the MCPTestClient core."""

from __future__ import annotations

import pytest

from mcp_forge import MCPTestClient, ToolInfo, ToolResult


class TestClientConnection:
    """Test server connection and initialization."""

    @pytest.mark.asyncio
    async def test_connects_to_echo_server(self, echo_client):
        """Client connects and initializes with the echo server."""
        assert echo_client.server_name == "echo-server"

    @pytest.mark.asyncio
    async def test_server_has_capabilities(self, echo_client):
        """Server reports capabilities after initialization."""
        caps = echo_client.capabilities
        assert caps is not None
        assert caps.tools is True


class TestToolDiscovery:
    """Test tool listing and discovery."""

    @pytest.mark.asyncio
    async def test_list_tools(self, echo_client):
        """List tools returns expected tools."""
        tools = await echo_client.list_tools()
        assert len(tools) == 4
        names = [t.name for t in tools]
        assert "echo" in names
        assert "add" in names
        assert "greet" in names
        assert "fail" in names

    @pytest.mark.asyncio
    async def test_tool_has_schema(self, echo_client):
        """Each tool has an input schema."""
        tools = await echo_client.list_tools()
        for tool in tools:
            assert isinstance(tool.input_schema, dict)
            assert tool.input_schema.get("type") == "object"

    @pytest.mark.asyncio
    async def test_get_tool(self, echo_client):
        """Get a specific tool by name."""
        tool = await echo_client.get_tool("echo")
        assert tool is not None
        assert tool.name == "echo"
        assert "message" in tool.param_names

    @pytest.mark.asyncio
    async def test_get_nonexistent_tool(self, echo_client):
        """Getting a nonexistent tool returns None."""
        tool = await echo_client.get_tool("nonexistent")
        assert tool is None

    @pytest.mark.asyncio
    async def test_get_tool_schema(self, echo_client):
        """Get input schema for a specific tool."""
        schema = await echo_client.get_tool_schema("add")
        assert schema["type"] == "object"
        assert "a" in schema["properties"]
        assert "b" in schema["properties"]

    @pytest.mark.asyncio
    async def test_get_all_schemas(self, echo_client):
        """Get all tool schemas as a dict."""
        schemas = await echo_client.get_all_schemas()
        assert "echo" in schemas
        assert "add" in schemas
        assert len(schemas) == 4


class TestToolCalls:
    """Test calling tools."""

    @pytest.mark.asyncio
    async def test_echo(self, echo_client):
        """Echo tool returns the input message."""
        result = await echo_client.call_tool("echo", {"message": "hello"})
        assert result.is_success
        assert result.text == "hello"

    @pytest.mark.asyncio
    async def test_add(self, echo_client):
        """Add tool returns the sum."""
        result = await echo_client.call_tool("add", {"a": 3, "b": 4})
        assert result.is_success
        assert result.text == "7"

    @pytest.mark.asyncio
    async def test_greet_informal(self, echo_client):
        """Greet tool with informal mode."""
        result = await echo_client.call_tool("greet", {"name": "Alice"})
        assert result.is_success
        assert "Hello, Alice!" in result.text

    @pytest.mark.asyncio
    async def test_greet_formal(self, echo_client):
        """Greet tool with formal mode."""
        result = await echo_client.call_tool(
            "greet", {"name": "Alice", "formal": True}
        )
        assert result.is_success
        assert "Good day" in result.text

    @pytest.mark.asyncio
    async def test_error_tool(self, echo_client):
        """Fail tool returns an error."""
        result = await echo_client.call_tool("fail", {"reason": "test error"})
        assert result.is_error
        assert "test error" in result.text

    @pytest.mark.asyncio
    async def test_unknown_tool(self, echo_client):
        """Calling unknown tool raises RuntimeError."""
        with pytest.raises(RuntimeError, match="Unknown tool"):
            await echo_client.call_tool("nonexistent", {})

    @pytest.mark.asyncio
    async def test_result_has_content_blocks(self, echo_client):
        """Tool result has proper content blocks."""
        result = await echo_client.call_tool("echo", {"message": "test"})
        assert len(result.content) == 1
        assert result.content[0].type == "text"
        assert result.content[0].text == "test"


class TestToolResultAPI:
    """Test ToolResult convenience methods."""

    def test_is_success(self):
        from mcp_forge._types import ContentBlock

        result = ToolResult(
            content=[ContentBlock(type="text", text="ok")],
            is_error=False,
        )
        assert result.is_success is True
        assert result.is_error is False

    def test_is_error(self):
        from mcp_forge._types import ContentBlock

        result = ToolResult(
            content=[ContentBlock(type="text", text="bad")],
            is_error=True,
        )
        assert result.is_success is False
        assert result.is_error is True

    def test_text_concatenation(self):
        from mcp_forge._types import ContentBlock

        result = ToolResult(
            content=[
                ContentBlock(type="text", text="line1"),
                ContentBlock(type="text", text="line2"),
            ]
        )
        assert result.text == "line1\nline2"

    def test_to_dict(self):
        from mcp_forge._types import ContentBlock

        result = ToolResult(
            content=[ContentBlock(type="text", text="hello")],
            is_error=False,
        )
        d = result.to_dict()
        assert d["is_error"] is False
        assert len(d["content"]) == 1
        assert d["content"][0]["text"] == "hello"
