"""Tests for ToolRegistry, ToolDefinition, and builtins."""

import os

import pytest

from proxym.tools import (
    ToolCategory,
    ToolCapability,
    ToolDefinition,
    ToolInstance,
    ToolRegistry,
)


class TestToolDefinition:
    def test_summary_keys(self):
        t = ToolDefinition(name="test-tool", category=ToolCategory.IDE, binary="nonexistent-xyz")
        s = t.summary()
        assert set(s.keys()) == {
            "name", "category", "binary", "available",
            "capabilities", "modes", "default_model", "default_instance",
            "instances", "description",
        }

    def test_instance_summary(self):
        instance = ToolInstance(
            name="docker",
            kind="docker",
            command=["docker", "compose", "up", "-d"],
            service="vscode",
            profile="vscode",
            container_name="proxym-vscode",
            description="Docker-backed VS Code",
        )
        s = instance.summary()
        assert s["name"] == "docker"
        assert s["kind"] == "docker"
        assert s["service"] == "vscode"
        assert s["available"] is False or isinstance(s["available"], bool)

    def test_is_available_false_for_missing_binary(self):
        t = ToolDefinition(name="x", category=ToolCategory.IDE, binary="nonexistent-xyz-binary")
        assert t.is_available is False

    def test_is_available_false_for_empty_binary(self):
        t = ToolDefinition(name="x", category=ToolCategory.BROWSER, binary="")
        assert t.is_available is False

    def test_force_available_true(self):
        t = ToolDefinition(name="x", category=ToolCategory.IDE, binary="nonexistent-xyz", force_available=True)
        assert t.is_available is True

    def test_force_available_false(self):
        """force_available=False overrides even if binary exists on PATH."""
        t = ToolDefinition(name="x", category=ToolCategory.IDE, binary="python3", force_available=False)
        assert t.is_available is False

    def test_capabilities_in_summary(self):
        t = ToolDefinition(
            name="x", category=ToolCategory.AGENT_CLI,
            capabilities=[ToolCapability.CODE_EDIT, ToolCapability.CHAT],
        )
        s = t.summary()
        assert s["capabilities"] == ["code_edit", "chat"]


class TestToolRegistry:
    def setup_method(self):
        # Reset singleton for test isolation
        ToolRegistry._instance = None

    def test_singleton(self):
        r1 = ToolRegistry.get()
        r2 = ToolRegistry.get()
        assert r1 is r2

    def test_builtin_count(self):
        r = ToolRegistry.get()
        tools = r.list_tools()
        assert len(tools) == 14

    def test_builtin_names(self):
        r = ToolRegistry.get()
        names = {t.name for t in r.list_tools()}
        expected = {"aider", "claude-code", "windsurf", "cursor", "vscode", "roo-code", "cline", "browser", "human", "codex", "goose", "open-interpreter", "plandex", "llm-cli"}
        assert names == expected

    def test_get_tool(self):
        r = ToolRegistry.get()
        t = r.get_tool("aider")
        assert t is not None
        assert t.category == ToolCategory.AGENT_CLI

    def test_get_tool_missing(self):
        r = ToolRegistry.get()
        assert r.get_tool("nonexistent") is None

    def test_register_custom(self):
        r = ToolRegistry.get()
        custom = ToolDefinition(name="my-tool", category=ToolCategory.CUSTOM, binary="mytool")
        r.register(custom)
        assert r.get_tool("my-tool") is not None
        assert len(r.list_tools()) == 15  # 14 builtins + 1

    def test_unregister(self):
        r = ToolRegistry.get()
        r.register(ToolDefinition(name="tmp", category=ToolCategory.CUSTOM))
        assert r.unregister("tmp") is True
        assert r.get_tool("tmp") is None

    def test_unregister_missing(self):
        r = ToolRegistry.get()
        assert r.unregister("nonexistent") is False

    def test_list_by_category(self):
        r = ToolRegistry.get()
        cli_tools = r.list_tools(category=ToolCategory.AGENT_CLI)
        names = {t.name for t in cli_tools}
        assert "aider" in names
        assert "claude-code" in names
        assert "vscode" not in names

    def test_summary_structure(self):
        r = ToolRegistry.get()
        s = r.summary()
        assert "total" in s
        assert "available" in s
        assert "tools" in s
        assert s["total"] == 14
        assert isinstance(s["tools"], list)

    def test_builtin_tools_expose_instances(self):
        r = ToolRegistry.get()
        vscode = r.get_tool("vscode")
        assert vscode is not None
        assert vscode.default_instance == "docker"
        assert any(i.kind == "docker" for i in vscode.instances)

    def test_docker_backed_tool_can_be_available_without_binary(self, monkeypatch):
        monkeypatch.setattr("proxym.tools._instance.shutil.which", lambda cmd: "/usr/bin/docker" if cmd == "docker" else None)
        monkeypatch.setattr("proxym.tools._definition.shutil.which", lambda cmd: "/usr/bin/docker" if cmd == "docker" else None)
        ToolRegistry._instance = None
        r = ToolRegistry.get()
        assert r.get_tool("browser").is_available is True

    def test_sorted_by_name(self):
        r = ToolRegistry.get()
        tools = r.list_tools()
        names = [t.name for t in tools]
        assert names == sorted(names)

    def test_env_override_star(self, monkeypatch):
        monkeypatch.setenv("PROXYM_TOOLS_AVAILABLE", "*")
        ToolRegistry._instance = None
        r = ToolRegistry.get()
        for t in r.list_tools():
            assert t.is_available is True, f"{t.name} should be available"

    def test_env_override_specific(self, monkeypatch):
        monkeypatch.setenv("PROXYM_TOOLS_AVAILABLE", "aider,claude-code")
        monkeypatch.setattr("proxym.tools._instance.shutil.which", lambda _: None)
        monkeypatch.setattr("proxym.tools._definition.shutil.which", lambda _: None)
        ToolRegistry._instance = None
        r = ToolRegistry.get()
        assert r.get_tool("aider").is_available is True
        assert r.get_tool("claude-code").is_available is True
        # browser has no binary and no override → still unavailable
        assert r.get_tool("browser").is_available is False

    def test_env_override_empty(self, monkeypatch):
        monkeypatch.setenv("PROXYM_TOOLS_AVAILABLE", "")
        monkeypatch.setattr("proxym.tools._instance.shutil.which", lambda _: None)
        monkeypatch.setattr("proxym.tools._definition.shutil.which", lambda _: None)
        ToolRegistry._instance = None
        r = ToolRegistry.get()
        # No override applied; browser with empty binary stays unavailable
        assert r.get_tool("browser").is_available is False
