"""Unit tests for langchain-clawprint tools."""

from __future__ import annotations

import json
from typing import Any, Dict
from unittest.mock import MagicMock, patch

import pytest

from langchain_clawprint import (
    ClawPrintCheckExchangeTool,
    ClawPrintClient,
    ClawPrintDomainsTool,
    ClawPrintGetAgentTool,
    ClawPrintHireAgentTool,
    ClawPrintRegisterTool,
    ClawPrintSearchTool,
    ClawPrintToolkit,
    ClawPrintTrustTool,
)
from langchain_clawprint._client import ClawPrintAPIError, validate_handle

# --- validate_handle ---


class TestValidateHandle:
    def test_valid_handle(self) -> None:
        assert validate_handle("codebot") == "codebot"

    def test_strips_at(self) -> None:
        assert validate_handle("@codebot") == "codebot"

    def test_lowercases(self) -> None:
        assert validate_handle("CodeBot") == "codebot"

    def test_strips_whitespace(self) -> None:
        assert validate_handle("  codebot  ") == "codebot"

    def test_allows_hyphens(self) -> None:
        assert validate_handle("my-code-bot") == "my-code-bot"

    def test_allows_digits(self) -> None:
        assert validate_handle("bot123") == "bot123"

    def test_empty_raises(self) -> None:
        with pytest.raises(ValueError, match="empty"):
            validate_handle("")

    def test_whitespace_only_raises(self) -> None:
        with pytest.raises(ValueError, match="empty"):
            validate_handle("   ")

    def test_invalid_chars_raises(self) -> None:
        with pytest.raises(ValueError, match="Invalid handle"):
            validate_handle("bot_with_underscores")

    def test_starts_with_hyphen_raises(self) -> None:
        with pytest.raises(ValueError, match="Invalid handle"):
            validate_handle("-bot")


# --- ClawPrintClient ---


class TestClawPrintClient:
    def test_init_defaults(self) -> None:
        client = ClawPrintClient(api_key="test_key")
        assert client.api_key == "test_key"
        assert client.base_url == "https://clawprint.io"
        assert client.timeout == 30

    def test_init_custom_base_url(self) -> None:
        client = ClawPrintClient(base_url="https://custom.api/")
        assert client.base_url == "https://custom.api"

    def test_env_api_key(self) -> None:
        with patch.dict("os.environ", {"CLAWPRINT_API_KEY": "env_key"}):
            client = ClawPrintClient()
            assert client.api_key == "env_key"

    def test_headers_no_auth(self) -> None:
        client = ClawPrintClient(api_key="test")
        headers = client._headers(auth_required=False)
        assert "Authorization" not in headers

    def test_headers_with_auth(self) -> None:
        client = ClawPrintClient(api_key="test")
        headers = client._headers(auth_required=True)
        assert headers["Authorization"] == "Bearer test"

    def test_headers_auth_required_no_key(self) -> None:
        client = ClawPrintClient()
        with pytest.raises(ClawPrintAPIError, match="requires an API key"):
            client._headers(auth_required=True)

    def test_empty_query_raises(self) -> None:
        client = ClawPrintClient()
        with pytest.raises(ValueError, match="cannot be empty"):
            client.search_agents("")

    def test_close(self) -> None:
        client = ClawPrintClient()
        client.close()


# --- Tools ---


def _mock_client(**overrides: Any) -> ClawPrintClient:
    """Build a ClawPrintClient with mocked HTTP methods."""
    client = MagicMock(spec=ClawPrintClient)
    for method, return_value in overrides.items():
        getattr(client, method).return_value = return_value
    return client


class TestSearchTool:
    def test_returns_json(self) -> None:
        data: Dict[str, Any] = {"agents": [{"handle": "bot"}]}
        client = _mock_client(search_agents=data)
        tool = ClawPrintSearchTool(client=client)
        result = tool.invoke({"query": "code review"})
        parsed = json.loads(result)
        assert parsed["agents"][0]["handle"] == "bot"

    def test_error_returns_string(self) -> None:
        client = _mock_client()
        client.search_agents.side_effect = ClawPrintAPIError(500, "boom")
        tool = ClawPrintSearchTool(client=client)
        result = tool.invoke({"query": "test"})
        assert "Error" in result


class TestGetAgentTool:
    def test_returns_agent(self) -> None:
        data: Dict[str, Any] = {"handle": "sentinel", "trust": 85}
        client = _mock_client(get_agent=data)
        tool = ClawPrintGetAgentTool(client=client)
        result = tool.invoke({"handle": "sentinel"})
        parsed = json.loads(result)
        assert parsed["trust"] == 85


class TestTrustTool:
    def test_returns_score(self) -> None:
        data: Dict[str, Any] = {"handle": "bot", "score": 90}
        client = _mock_client(get_trust=data)
        tool = ClawPrintTrustTool(client=client)
        result = tool.invoke({"handle": "bot"})
        parsed = json.loads(result)
        assert parsed["score"] == 90


class TestDomainsTool:
    def test_returns_domains(self) -> None:
        data: Dict[str, Any] = {"domains": ["code-review", "data-analysis"]}
        client = _mock_client(list_domains=data)
        tool = ClawPrintDomainsTool(client=client)
        result = tool.invoke({})
        parsed = json.loads(result)
        assert "code-review" in parsed["domains"]


class TestRegisterTool:
    def test_returns_card(self) -> None:
        data: Dict[str, Any] = {"handle": "new-bot", "api_key": "cp_live_xxx"}
        client = _mock_client(register_agent=data)
        tool = ClawPrintRegisterTool(client=client)
        result = tool.invoke(
            {
                "handle": "new-bot",
                "name": "New Bot",
                "description": "Does stuff",
                "domains": ["general"],
            }
        )
        parsed = json.loads(result)
        assert parsed["api_key"] == "cp_live_xxx"


class TestHireTool:
    def test_returns_request_id(self) -> None:
        data: Dict[str, Any] = {"request_id": "req_123", "status": "pending"}
        client = _mock_client(create_exchange_request=data)
        tool = ClawPrintHireAgentTool(client=client)
        result = tool.invoke({"domains": ["code-review"], "task": "Review my code"})
        parsed = json.loads(result)
        assert parsed["request_id"] == "req_123"


class TestCheckExchangeTool:
    def test_returns_status(self) -> None:
        data: Dict[str, Any] = {"request_id": "req_123", "status": "completed"}
        client = _mock_client(get_exchange_request=data)
        tool = ClawPrintCheckExchangeTool(client=client)
        result = tool.invoke({"request_id": "req_123"})
        parsed = json.loads(result)
        assert parsed["status"] == "completed"


# --- Toolkit ---


class TestToolkit:
    def test_get_tools_returns_seven(self) -> None:
        toolkit = ClawPrintToolkit(api_key="test")
        tools = toolkit.get_tools()
        assert len(tools) == 7

    def test_get_tool_by_name(self) -> None:
        toolkit = ClawPrintToolkit(api_key="test")
        tool = toolkit.get_tool("clawprint_search")
        assert tool.name == "clawprint_search"

    def test_get_tool_invalid_raises(self) -> None:
        toolkit = ClawPrintToolkit(api_key="test")
        with pytest.raises(KeyError, match="No tool named"):
            toolkit.get_tool("nonexistent")

    def test_tool_names(self) -> None:
        toolkit = ClawPrintToolkit(api_key="test")
        names = {t.name for t in toolkit.get_tools()}
        expected = {
            "clawprint_search",
            "clawprint_get_agent",
            "clawprint_trust",
            "clawprint_domains",
            "clawprint_register",
            "clawprint_hire",
            "clawprint_check_exchange",
        }
        assert names == expected
