"""Tests for ToolMiddleware."""

import pytest
from pydantic import BaseModel, Field

from yier_agents.src.tool import Tool, ToolContext, ToolOutput
from yier_agents.src.message import ToolCall, ToolResult
from yier_agents.src.middleware import ToolMiddleware


# Define test tool parameters
class AddParams(BaseModel):
    """Parameters for add tool."""
    a: int = Field(..., description="First number")
    b: int = Field(..., description="Second number")


async def add_execute(params: AddParams, ctx: ToolContext) -> ToolOutput:
    """Execute add operation."""
    result = params.a + params.b
    return ToolOutput(content=str(result))


class ErrorParams(BaseModel):
    """Parameters for error tool."""
    message: str = Field(..., description="Error message to raise")


async def error_execute(params: ErrorParams, ctx: ToolContext) -> ToolOutput:
    """Execute error operation - always raises."""
    raise RuntimeError(params.message)


# Test fixtures
@pytest.fixture
def add_tool():
    """Create an add tool."""
    return Tool(
        name="add",
        description="Add two numbers",
        parameters=AddParams,
        execute=add_execute,
    )


@pytest.fixture
def error_tool():
    """Create an error tool."""
    return Tool(
        name="error",
        description="Always raises an error",
        parameters=ErrorParams,
        execute=error_execute,
    )


@pytest.fixture
def middleware(add_tool, error_tool):
    """Create a middleware instance with test tools."""
    return ToolMiddleware(tools=[add_tool, error_tool])


@pytest.fixture
def tool_context():
    """Create a test tool context."""
    return ToolContext(
        session_id="test-session",
        message_id="test-message",
        call_id="test-call",
    )


@pytest.mark.asyncio
async def test_middleware_execute_success(middleware, tool_context):
    """Test successful tool execution."""
    tool_call = ToolCall(
        id="call-1",
        name="add",
        arguments={"a": 3, "b": 5},
    )

    result = await middleware.execute_tool_call(tool_call, tool_context)

    assert isinstance(result, ToolResult)
    assert result.call_id == "call-1"
    assert result.name == "add"
    assert result.content == "8"
    assert result.is_error is False


@pytest.mark.asyncio
async def test_middleware_tool_not_found(middleware, tool_context):
    """Test tool not found error."""
    tool_call = ToolCall(
        id="call-2",
        name="nonexistent",
        arguments={},
    )

    result = await middleware.execute_tool_call(tool_call, tool_context)

    assert isinstance(result, ToolResult)
    assert result.call_id == "call-2"
    assert result.name == "nonexistent"
    assert "not found" in result.content.lower()
    assert result.is_error is True


@pytest.mark.asyncio
async def test_middleware_invalid_arguments(middleware, tool_context):
    """Test parameter validation failure."""
    tool_call = ToolCall(
        id="call-3",
        name="add",
        arguments={"a": "not-a-number", "b": 5},  # Invalid type
    )

    result = await middleware.execute_tool_call(tool_call, tool_context)

    assert isinstance(result, ToolResult)
    assert result.call_id == "call-3"
    assert result.name == "add"
    assert result.is_error is True
    # Should contain validation error information
    assert "validation" in result.content.lower() or "error" in result.content.lower()


@pytest.mark.asyncio
async def test_middleware_execution_error(middleware, tool_context):
    """Test execution error handling."""
    tool_call = ToolCall(
        id="call-4",
        name="error",
        arguments={"message": "Test error message"},
    )

    result = await middleware.execute_tool_call(tool_call, tool_context)

    assert isinstance(result, ToolResult)
    assert result.call_id == "call-4"
    assert result.name == "error"
    assert result.is_error is True
    assert "Test error message" in result.content
