"""Tests for tool execution timeout (Phase 14.1)."""

from __future__ import annotations

import time

from openjarvis.core.events import EventBus, EventType
from openjarvis.core.types import ToolCall, ToolResult
from openjarvis.tools._stubs import BaseTool, ToolExecutor, ToolSpec


class SlowTool(BaseTool):
    """A tool that sleeps for a configurable duration."""

    tool_id = "slow_tool"

    def __init__(self, delay: float = 5.0):
        self._delay = delay

    @property
    def spec(self) -> ToolSpec:
        return ToolSpec(
            name="slow_tool",
            description="A tool that takes a long time.",
            timeout_seconds=1.0,  # 1-second timeout
        )

    def execute(self, **params) -> ToolResult:
        time.sleep(self._delay)
        return ToolResult(tool_name="slow_tool", content="Done", success=True)


class FastTool(BaseTool):
    """A tool that returns immediately."""

    tool_id = "fast_tool"

    @property
    def spec(self) -> ToolSpec:
        return ToolSpec(
            name="fast_tool",
            description="A fast tool.",
            timeout_seconds=10.0,
        )

    def execute(self, **params) -> ToolResult:
        return ToolResult(
            tool_name="fast_tool",
            content=f"Result: {params.get('input', '')}",
            success=True,
        )


class TestToolTimeout:
    def test_fast_tool_succeeds(self):
        executor = ToolExecutor([FastTool()])
        call = ToolCall(id="1", name="fast_tool", arguments='{"input": "hello"}')
        result = executor.execute(call)
        assert result.success
        assert "hello" in result.content

    def test_slow_tool_times_out(self):
        executor = ToolExecutor([SlowTool(delay=5.0)])
        call = ToolCall(id="1", name="slow_tool", arguments="{}")
        result = executor.execute(call)
        assert not result.success
        assert "timed out" in result.content

    def test_timeout_event_emitted(self):
        bus = EventBus(record_history=True)
        executor = ToolExecutor([SlowTool(delay=5.0)], bus=bus)
        call = ToolCall(id="1", name="slow_tool", arguments="{}")
        executor.execute(call)

        timeout_events = [
            e for e in bus.history if e.event_type == EventType.TOOL_TIMEOUT
        ]
        assert len(timeout_events) == 1
        assert timeout_events[0].data["tool"] == "slow_tool"

    def test_default_timeout_used(self):
        """When ToolSpec has no timeout, the executor default is used."""

        class NoTimeoutTool(BaseTool):
            tool_id = "no_timeout"

            @property
            def spec(self):
                return ToolSpec(
                    name="no_timeout",
                    description="test",
                    timeout_seconds=0,
                )

            def execute(self, **params):
                return ToolResult(tool_name="no_timeout", content="ok")

        executor = ToolExecutor([NoTimeoutTool()], default_timeout=60.0)
        call = ToolCall(id="1", name="no_timeout", arguments="{}")
        result = executor.execute(call)
        assert result.success

    def test_timeout_seconds_on_toolspec(self):
        spec = ToolSpec(name="test", description="test", timeout_seconds=42.0)
        assert spec.timeout_seconds == 42.0

    def test_unknown_tool(self):
        executor = ToolExecutor([FastTool()])
        call = ToolCall(id="1", name="nonexistent", arguments="{}")
        result = executor.execute(call)
        assert not result.success
        assert "Unknown tool" in result.content
