"Integration tests for REPL tool system."

from unittest.mock import patch

import pytest
from rich.console import Console

from henchman.cli.repl import Repl, ReplConfig
from henchman.core.agent import Agent
from henchman.core.events import EventType
from henchman.providers.base import (
    FinishReason,
    ModelProvider,
    StreamChunk,
    ToolCall,
)
from henchman.tools.builtins.shell import ShellTool
from henchman.tools.registry import ToolRegistry


class MockProviderWithToolSupport(ModelProvider):
    """Mock provider that can simulate tool calls."""

    def __init__(self, simulate_tool_call: bool = False):
        self.simulate_tool_call = simulate_tool_call

    @property
    def name(self) -> str:
        return "mock"

    async def chat_completion_stream(self, messages, tools=None, **kwargs):
        if self.simulate_tool_call and tools:
            # Simulate a tool call request
            yield StreamChunk(
                content="",
                tool_calls=[
                    ToolCall(id="test_tool_call", name="shell", arguments={"command": "echo test"})
                ],
                finish_reason=FinishReason.TOOL_CALLS,
            )
            # Simulate continuation after tool result
            yield StreamChunk(content="Tool executed successfully", finish_reason=FinishReason.STOP)
        else:
            # Regular response
            yield StreamChunk(content="Hello, I'm a mock provider", finish_reason=FinishReason.STOP)


class TestReplToolIntegration:
    """Integration tests for REPL tool system."""

    def test_repl_initializes_with_tool_registry(self):
        """Test that REPL initializes with tool registry containing built-ins."""
        console = Console()
        provider = MockProviderWithToolSupport()
        config = ReplConfig()

        repl = Repl(provider=provider, console=console, config=config)

        # REPL registers built-in tools by default, so registry is not empty
        assert len(repl.tool_registry._tools) > 0
        assert isinstance(repl.tool_registry, ToolRegistry)

    def test_repl_agent_initialized_with_tools(self):
        """Test that agent starts with tools (REPL connects them automatically)."""
        console = Console()
        provider = MockProviderWithToolSupport()
        config = ReplConfig()

        repl = Repl(provider=provider, console=console, config=config)

        # Agent should be initialized
        assert repl.agent is not None
        assert isinstance(repl.agent, Agent)
        # REPL now connects tools from registry to agent automatically
        assert len(repl.agent.tools) > 0

    def test_tool_registration_affects_agent(self):
        """Test that registering tools gives them to agent (REPL connects them)."""
        console = Console()
        provider = MockProviderWithToolSupport()
        config = ReplConfig()

        repl = Repl(provider=provider, console=console, config=config)

        # Count initial tools
        _ = len(repl.tool_registry._tools)
        initial_agent_tool_count = len(repl.agent.tools)

        # Note: Can't register new tool since all built-ins are already registered
        # This test verifies the connection exists
        assert initial_agent_tool_count > 0
        # Agent (Tech Lead) has base tools plus delegate_task
        base_tool_count = len(repl.tool_registry.get_declarations())
        assert len(repl.agent.tools) >= base_tool_count

    def test_agent_with_tools_can_act_as_chatbot(self):
        """Test that agent with tools can still act as a simple chatbot."""
        console = Console()
        provider = MockProviderWithToolSupport(simulate_tool_call=False)
        config = ReplConfig()

        repl = Repl(provider=provider, console=console, config=config)

        # Agent has tools (connected by REPL)
        assert len(repl.agent.tools) > 0

        # Even with tools available, if provider doesn't request tool calls,
        # agent should act as chatbot
        # This is verified by the agent having tools but provider not simulating calls

    def test_agent_with_tools_can_make_tool_calls(self):
        """Test that agent with tools can receive tool call requests."""
        console = Console()
        provider = MockProviderWithToolSupport(simulate_tool_call=True)
        config = ReplConfig()

        repl = Repl(provider=provider, console=console, config=config)

        # Agent should have tools (connected by REPL)
        assert len(repl.agent.tools) > 0

        # With a provider that simulates tool calls and tools available,
        # the agent should be able to handle tool calls
        # (Actual execution tested separately)


class TestToolSystemE2E:
    """End-to-end tests for complete tool execution flow."""

    @pytest.mark.skip(reason="Async mocking issues need to be resolved")
    async def test_tool_execution_flow(self):
        """Test complete tool execution flow."""
        console = Console()

        # Create a provider that will request a tool call
        class ToolCallProvider(ModelProvider):
            @property
            def name(self) -> str:
                return "toolcall"

            async def chat_completion_stream(self, messages, tools=None, **kwargs):
                # First response: request tool call
                yield StreamChunk(
                    content="",
                    tool_calls=[
                        ToolCall(
                            id="test_call_1", name="shell", arguments={"command": "echo hello"}
                        )
                    ],
                    finish_reason=FinishReason.TOOL_CALLS,
                )
                # Second response: continue with result
                yield StreamChunk(
                    content="Tool executed successfully.", finish_reason=FinishReason.STOP
                )

        provider = ToolCallProvider()
        config = ReplConfig()

        repl = Repl(provider=provider, console=console, config=config)

        # Shell tool is already registered by REPL initialization
        # Connect tools to agent (already done by REPL)

        # Mock the tool execution to avoid actually running shell commands
        with patch.object(ShellTool, "execute") as mock_execute:
            # Create a proper async mock that returns a ToolResult
            from henchman.tools.base import ToolResult

            async def mock_execute_func(**_):
                return ToolResult(content="hello\n", success=True, display=None, error=None)

            mock_execute.side_effect = mock_execute_func

            # Run agent
            events = []
            async for event in repl.agent.run("Run a command"):
                events.append(event.type)

                # Handle tool call
                if event.type == EventType.TOOL_CALL_REQUEST:
                    # Execute tool
                    result = await repl.tool_registry.execute(event.data.name, event.data.arguments)
                    # Submit result back to agent
                    await repl.agent.submit_tool_result(event.data.id, result.content)

            # Verify flow
            assert EventType.TOOL_CALL_REQUEST in events
            # Should have at least tool call request and completion
            assert len(events) >= 2

    def test_builtin_tools_available(self):
        """Test that all built-in tools are available in REPL."""
        console = Console()
        provider = MockProviderWithToolSupport()
        config = ReplConfig()

        repl = Repl(provider=provider, console=console, config=config)

        # Check that common built-in tools are registered
        tool_names = list(repl.tool_registry._tools.keys())

        # Should have shell tool (and other built-ins)
        assert "shell" in tool_names
        assert "ls" in tool_names
        assert "read_file" in tool_names

        # Should have reasonable number of tools
        assert len(tool_names) >= 5


if __name__ == "__main__":
    # Run integration tests directly

    print("Running integration tests...")

    # Create test instance
    integration = TestReplToolIntegration()
    e2e = TestToolSystemE2E()

    # Run tests
    integration.test_repl_initializes_with_tool_registry()
    print("✓ test_repl_initializes_with_tool_registry")

    integration.test_repl_agent_initialized_with_tools()
    print("✓ test_repl_agent_initialized_with_tools")

    integration.test_tool_registration_affects_agent()
    print("✓ test_tool_registration_affects_agent")

    integration.test_agent_with_tools_can_act_as_chatbot()
    print("✓ test_agent_with_tools_can_act_as_chatbot")

    integration.test_agent_with_tools_can_make_tool_calls()
    print("✓ test_agent_with_tools_can_make_tool_calls")

    print("⏭️  test_tool_execution_flow (skipped - async mocking issues)")

    integration.test_builtin_tools_available()
    print("✓ test_builtin_tools_available")

    print("\nAll integration tests passed!")
