"""
Tests for ApeTool - LangChain BaseTool adapter.
"""

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

from ape_langchain.tool import ApeTool
from ape_langchain.core import ApeModule, ApeExecutionError


@patch("ape_langchain.tool.compile_ape")
@patch("ape_langchain.tool.validate_ape")
def test_ape_tool_initialization(mock_validate, mock_compile):
    """Test ApeTool initialization with mocked Ape core."""
    # Mock module and signature
    mock_signature = Mock()
    mock_signature.name = "add"
    mock_signature.inputs = {"a": "str", "b": "str"}
    mock_signature.output = "str"
    mock_signature.description = "Add two numbers"
    
    mock_module = Mock(spec=ApeModule)
    mock_module.get_function_signature.return_value = mock_signature
    mock_module.list_functions.return_value = ["add", "subtract"]
    
    mock_compile.return_value = mock_module
    mock_validate.return_value = None
    
    # Create tool
    tool = ApeTool(
        ape_file="test.ape",
        function="add",
        description="Add operation"
    )
    
    assert tool.name == "add"
    assert tool.description == "Add operation"
    assert tool.ape_file == "test.ape"
    assert tool.function == "add"
    
    mock_compile.assert_called_once_with("test.ape")
    mock_validate.assert_called_once_with(mock_module)


@patch("ape_langchain.tool.compile_ape")
@patch("ape_langchain.tool.validate_ape")
@patch("ape_langchain.tool.execute_ape_function")
def test_ape_tool_run_success(mock_execute, mock_validate, mock_compile):
    """Test successful ApeTool execution."""
    # Setup mocks
    mock_signature = Mock()
    mock_signature.name = "add"
    mock_signature.inputs = {"a": "str", "b": "str"}
    mock_signature.output = "str"
    mock_signature.description = None
    
    mock_module = Mock(spec=ApeModule)
    mock_module.get_function_signature.return_value = mock_signature
    mock_module.list_functions.return_value = ["add"]
    
    mock_compile.return_value = mock_module
    mock_validate.return_value = None
    mock_execute.return_value = "8"
    
    # Create and run tool
    tool = ApeTool(ape_file="test.ape", function="add")
    result = tool._run(a="5", b="3")
    
    assert result == "8"
    mock_execute.assert_called_once_with(mock_module, "add", a="5", b="3")


@patch("ape_langchain.tool.compile_ape")
@patch("ape_langchain.tool.validate_ape")
def test_ape_tool_run_validation_error(mock_validate, mock_compile):
    """Test ApeTool execution with input validation error."""
    # Setup mocks
    mock_signature = Mock()
    mock_signature.name = "add"
    mock_signature.inputs = {"a": "str", "b": "str"}
    mock_signature.output = "str"
    mock_signature.description = None
    
    mock_module = Mock(spec=ApeModule)
    mock_module.get_function_signature.return_value = mock_signature
    mock_module.list_functions.return_value = ["add"]
    
    mock_compile.return_value = mock_module
    mock_validate.return_value = None
    
    # Create tool
    tool = ApeTool(ape_file="test.ape", function="add")
    
    # Run with missing parameter
    result = tool._run(a="5")
    
    assert "Error:" in result
    assert "Missing required parameters" in result


@patch("ape_langchain.tool.compile_ape")
@patch("ape_langchain.tool.validate_ape")
@patch("ape_langchain.tool.execute_ape_function")
def test_ape_tool_run_execution_error(mock_execute, mock_validate, mock_compile):
    """Test ApeTool execution with runtime error."""
    # Setup mocks
    mock_signature = Mock()
    mock_signature.name = "divide"
    mock_signature.inputs = {"a": "str", "b": "str"}
    mock_signature.output = "str"
    mock_signature.description = None
    
    mock_module = Mock(spec=ApeModule)
    mock_module.get_function_signature.return_value = mock_signature
    mock_module.list_functions.return_value = ["divide"]
    
    mock_compile.return_value = mock_module
    mock_validate.return_value = None
    mock_execute.side_effect = ApeExecutionError("Division by zero")
    
    # Create and run tool
    tool = ApeTool(ape_file="test.ape", function="divide")
    result = tool._run(a="10", b="0")
    
    assert "Execution error:" in result
    assert "Division by zero" in result


@patch("ape_langchain.tool.compile_ape")
@patch("ape_langchain.tool.validate_ape")
def test_ape_tool_async_not_implemented(mock_validate, mock_compile):
    """Test that async execution raises NotImplementedError."""
    # Setup mocks
    mock_signature = Mock()
    mock_signature.name = "add"
    mock_signature.inputs = {"a": "str", "b": "str"}
    mock_signature.output = "str"
    mock_signature.description = None
    
    mock_module = Mock(spec=ApeModule)
    mock_module.get_function_signature.return_value = mock_signature
    mock_module.list_functions.return_value = ["add"]
    
    mock_compile.return_value = mock_module
    mock_validate.return_value = None
    
    # Create tool
    tool = ApeTool(ape_file="test.ape", function="add")
    
    # Async should raise
    with pytest.raises(NotImplementedError, match="async execution"):
        import asyncio
        asyncio.run(tool._arun(a="5", b="3"))


@patch("ape_langchain.tool.compile_ape")
@patch("ape_langchain.tool.validate_ape")
def test_ape_tool_get_json_schema(mock_validate, mock_compile):
    """Test JSON Schema export from ApeTool."""
    # Setup mocks
    mock_signature = Mock()
    mock_signature.name = "calculate"
    mock_signature.inputs = {"x": "str", "y": "str"}
    mock_signature.output = "str"
    mock_signature.description = "Calculate something"
    
    mock_module = Mock(spec=ApeModule)
    mock_module.get_function_signature.return_value = mock_signature
    mock_module.list_functions.return_value = ["calculate"]
    
    mock_compile.return_value = mock_module
    mock_validate.return_value = None
    
    # Create tool
    tool = ApeTool(ape_file="test.ape", function="calculate")
    schema = tool.get_json_schema()
    
    assert schema["type"] == "function"
    assert schema["function"]["name"] == "calculate"
    assert "x" in schema["function"]["parameters"]["properties"]
    assert "y" in schema["function"]["parameters"]["properties"]
