"""Extended tests for timmy.tools — covers tool tracking, stats, and create_* functions."""

from unittest.mock import MagicMock, patch

import pytest

from timmy.tools import (
    _TOOL_USAGE,
    AgentTools,
    PersonaTools,
    ToolStats,
    _track_tool_usage,
    calculator,
    create_aider_tool,
    get_tool_stats,
)


class TestToolTracking:
    """Test _track_tool_usage and get_tool_stats."""

    def setup_method(self):
        _TOOL_USAGE.clear()

    def test_track_tool_usage(self):
        _track_tool_usage("agent-1", "web_search")
        assert "agent-1" in _TOOL_USAGE
        assert len(_TOOL_USAGE["agent-1"]) == 1
        assert _TOOL_USAGE["agent-1"][0]["tool"] == "web_search"
        assert _TOOL_USAGE["agent-1"][0]["success"] is True

    def test_track_multiple_calls(self):
        _track_tool_usage("agent-1", "tool_a")
        _track_tool_usage("agent-1", "tool_b")
        _track_tool_usage("agent-1", "tool_a", success=False)
        assert len(_TOOL_USAGE["agent-1"]) == 3

    def test_get_tool_stats_specific_agent(self):
        _track_tool_usage("agent-x", "read_file")
        _track_tool_usage("agent-x", "write_file")

        stats = get_tool_stats("agent-x")
        assert stats["agent_id"] == "agent-x"
        assert stats["total_calls"] == 2
        assert set(stats["tools_used"]) == {"read_file", "write_file"}

    def test_get_tool_stats_no_data(self):
        stats = get_tool_stats("nonexistent")
        assert stats["total_calls"] == 0
        assert stats["tools_used"] == []

    def test_get_tool_stats_all_agents(self):
        _track_tool_usage("a1", "t1")
        _track_tool_usage("a2", "t2")
        _track_tool_usage("a2", "t3")

        stats = get_tool_stats()
        assert "a1" in stats
        assert stats["a1"]["total_calls"] == 1
        assert stats["a2"]["total_calls"] == 2

    def test_recent_calls_capped_at_10(self):
        for i in range(15):
            _track_tool_usage("agent-y", f"tool_{i}")

        stats = get_tool_stats("agent-y")
        assert len(stats["recent_calls"]) == 10

    def teardown_method(self):
        _TOOL_USAGE.clear()


class TestToolStats:
    """Test ToolStats dataclass."""

    def test_defaults(self):
        ts = ToolStats(tool_name="calc")
        assert ts.call_count == 0
        assert ts.last_used is None
        assert ts.errors == 0


class TestAgentTools:
    """Test AgentTools dataclass and backward compat alias."""

    def test_persona_tools_alias(self):
        assert PersonaTools is AgentTools


class TestCalculatorExtended:
    """Extended tests for the calculator tool."""

    def test_division(self):
        assert calculator("10 / 3") == str(10 / 3)

    def test_exponents(self):
        assert calculator("2**10") == "1024"

    def test_math_functions(self):
        import math

        assert calculator("math.sqrt(144)") == "12.0"
        assert calculator("math.pi") == str(math.pi)
        assert calculator("math.log(100, 10)") == str(math.log(100, 10))

    def test_builtins_blocked(self):
        result = calculator("__import__('os').system('ls')")
        assert "Error" in result

    def test_abs_allowed(self):
        assert calculator("abs(-5)") == "5"

    def test_round_allowed(self):
        assert calculator("round(3.14159, 2)") == "3.14"

    def test_min_max_allowed(self):
        assert calculator("min(1, 2, 3)") == "1"
        assert calculator("max(1, 2, 3)") == "3"

    def test_invalid_expression(self):
        result = calculator("not valid python")
        assert "Error" in result

    def test_division_by_zero(self):
        result = calculator("1/0")
        assert "Error" in result


class TestCreateToolFunctions:
    """Test that create_*_tools functions check availability."""

    def test_create_research_tools_no_agno(self):
        with patch("timmy.tools._AGNO_TOOLS_AVAILABLE", False):
            with patch("timmy.tools._ImportError", ImportError("no agno")):
                with pytest.raises(ImportError):
                    from timmy.tools import create_research_tools

                    create_research_tools()

    def test_create_code_tools_no_agno(self):
        with patch("timmy.tools._AGNO_TOOLS_AVAILABLE", False):
            with patch("timmy.tools._ImportError", ImportError("no agno")):
                with pytest.raises(ImportError):
                    from timmy.tools import create_code_tools

                    create_code_tools()

    def test_create_data_tools_no_agno(self):
        with patch("timmy.tools._AGNO_TOOLS_AVAILABLE", False):
            with patch("timmy.tools._ImportError", ImportError("no agno")):
                with pytest.raises(ImportError):
                    from timmy.tools import create_data_tools

                    create_data_tools()

    def test_create_writing_tools_no_agno(self):
        with patch("timmy.tools._AGNO_TOOLS_AVAILABLE", False):
            with patch("timmy.tools._ImportError", ImportError("no agno")):
                with pytest.raises(ImportError):
                    from timmy.tools import create_writing_tools

                    create_writing_tools()


class TestAiderTool:
    """Test AiderTool created by create_aider_tool."""

    def test_create_aider_tool(self, tmp_path):
        tool = create_aider_tool(tmp_path)
        assert hasattr(tool, "run_aider")
        assert tool.base_dir == tmp_path

    @patch("subprocess.run")
    def test_aider_success(self, mock_run, tmp_path):
        tool = create_aider_tool(tmp_path)
        mock_run.return_value = MagicMock(returncode=0, stdout="Changes applied")
        result = tool.run_aider("add fibonacci function")
        assert "Changes applied" in result

    @patch("subprocess.run")
    def test_aider_error(self, mock_run, tmp_path):
        tool = create_aider_tool(tmp_path)
        mock_run.return_value = MagicMock(returncode=1, stderr="something broke")
        result = tool.run_aider("bad prompt")
        assert "error" in result.lower()

    @patch("subprocess.run", side_effect=FileNotFoundError)
    def test_aider_not_installed(self, mock_run, tmp_path):
        tool = create_aider_tool(tmp_path)
        result = tool.run_aider("test")
        assert "not installed" in result.lower()

    @patch("subprocess.run")
    def test_aider_timeout(self, mock_run, tmp_path):
        import subprocess

        mock_run.side_effect = subprocess.TimeoutExpired(cmd="aider", timeout=120)
        tool = create_aider_tool(tmp_path)
        result = tool.run_aider("slow task")
        assert "timed out" in result.lower()
