"""Tests for tool base classes and registry."""

import pytest
import time
from xyglue.tools.base import (
    Tool, ToolResult, ToolError, ToolValidationError,
    ToolParam, ToolRegistry, MockTool,
)


class TestToolResult:
    def test_success_result(self):
        r = ToolResult(success=True, output="data")
        assert r.success is True
        assert r.output == "data"

    def test_ok_classmethod(self):
        r = ToolResult.ok("result data")
        assert r.success is True
        assert r.output == "result data"

    def test_fail_classmethod(self):
        r = ToolResult.fail("something broke")
        assert r.success is False
        assert r.error == "something broke"

    def test_to_dict(self):
        r = ToolResult(success=True, output="data", metadata={"key": "val"})
        d = r.to_dict()
        assert d["success"] is True
        assert d["output"] == "data"


class TestToolError:
    def test_error_creation(self):
        e = ToolError("bad thing", recoverable=True)
        assert str(e) == "bad thing"
        assert e.recoverable is True

    def test_to_result(self):
        e = ToolError("failed", recoverable=False)
        r = e.to_result()
        assert r.success is False
        assert r.error == "failed"


class TestToolValidationError:
    def test_validation_error(self):
        e = ToolValidationError(["missing param 'url'"])
        assert "url" in str(e)


class TestToolParam:
    def test_string_param(self):
        p = ToolParam(name="url", param_type="string", required=True)
        assert p.validate_value("https://example.com") is None

    def test_required_param_missing(self):
        p = ToolParam(name="url", param_type="string", required=True)
        issue = p.validate_value(None)
        assert issue is not None

    def test_int_param(self):
        p = ToolParam(name="count", param_type="int")
        assert p.validate_value(42) is None

    def test_int_param_wrong_type(self):
        p = ToolParam(name="count", param_type="int")
        issue = p.validate_value("not a number")
        assert issue is not None


class TestMockTool:
    async def test_mock_success(self):
        tool = MockTool(tool_name="test")
        result = await tool.execute({"key": "val"})
        assert result.success is True

    async def test_mock_failure(self):
        tool = MockTool(
            tool_name="fail",
            responses=[ToolResult.fail("forced failure")],
        )
        result = await tool.execute({})
        assert result.success is False
        assert "forced failure" in result.error

    async def test_mock_validate(self):
        tool = MockTool(tool_name="test")
        issues = await tool.validate({"key": "val"})
        assert issues == []

    async def test_mock_with_latency(self):
        tool = MockTool(tool_name="slow", latency_ms=100)
        start = time.time()
        result = await tool.execute({})
        elapsed = (time.time() - start) * 1000
        assert result.success is True
        assert elapsed >= 80

    async def test_mock_fail_after(self):
        tool = MockTool(tool_name="flaky", fail_after=2)
        r1 = await tool.execute({})
        assert r1.success is True
        r2 = await tool.execute({})
        assert r2.success is True
        r3 = await tool.execute({})
        assert r3.success is False

    async def test_mock_reset(self):
        tool = MockTool(tool_name="test")
        await tool.execute({})
        assert tool.call_count == 1
        tool.reset()
        assert tool.call_count == 0

    async def test_mock_tracks_calls(self):
        tool = MockTool(tool_name="test")
        await tool.execute({"a": 1})
        await tool.execute({"b": 2})
        assert len(tool.calls) == 2


class TestToolRegistry:
    def test_register_and_get(self):
        reg = ToolRegistry()
        tool = MockTool(tool_name="test")
        reg.register(tool)
        assert reg.get("test") is tool

    def test_get_missing(self):
        reg = ToolRegistry()
        assert reg.get("nonexistent") is None

    def test_list_names(self):
        reg = ToolRegistry()
        reg.register(MockTool(tool_name="b_tool"))
        reg.register(MockTool(tool_name="a_tool"))
        names = reg.list_names()
        assert names == ["a_tool", "b_tool"]

    def test_list_by_category(self):
        reg = ToolRegistry()
        t1 = MockTool(tool_name="t1", category="web")
        t2 = MockTool(tool_name="t2", category="data")
        reg.register(t1)
        reg.register(t2)
        web_tools = reg.list_by_category("web")
        assert len(web_tools) == 1
        assert web_tools[0].name == "t1"

    def test_count(self):
        reg = ToolRegistry()
        reg.register(MockTool(tool_name="a"))
        reg.register(MockTool(tool_name="b"))
        assert reg.count == 2
