"""Tests for tool definition capture and sanitization in AI response events."""
from __future__ import annotations

import hashlib
import json
from typing import Any, Dict, List

from amplitude_ai.core.privacy import PrivacyConfig, _normalize_tool_definitions
from amplitude_ai.core.tracking import (
    PROP_TOOL_DEFINITIONS,
    PROP_TOOL_DEFINITIONS_COUNT,
    PROP_TOOL_DEFINITIONS_HASH,
)


# ---------------------------------------------------------------------------
# _normalize_tool_definitions
# ---------------------------------------------------------------------------


def test_normalize_openai_chat_format() -> None:
    tools: List[Dict[str, Any]] = [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "Get the current weather",
                "parameters": {"type": "object", "properties": {"city": {"type": "string"}}},
            },
        }
    ]
    result = _normalize_tool_definitions(tools)
    assert result == [
        {
            "name": "get_weather",
            "description": "Get the current weather",
            "parameters": {"type": "object", "properties": {"city": {"type": "string"}}},
        }
    ]


def test_normalize_anthropic_format() -> None:
    tools: List[Dict[str, Any]] = [
        {
            "name": "search",
            "description": "Search the web",
            "input_schema": {"type": "object", "properties": {"query": {"type": "string"}}},
        }
    ]
    result = _normalize_tool_definitions(tools)
    assert result == [
        {
            "name": "search",
            "description": "Search the web",
            "parameters": {"type": "object", "properties": {"query": {"type": "string"}}},
        }
    ]


def test_normalize_bedrock_format() -> None:
    tools: List[Dict[str, Any]] = [
        {
            "toolSpec": {
                "name": "calculator",
                "description": "Basic math",
                "inputSchema": {"type": "object", "properties": {"expr": {"type": "string"}}},
            }
        }
    ]
    result = _normalize_tool_definitions(tools)
    assert result == [
        {
            "name": "calculator",
            "description": "Basic math",
            "parameters": {"type": "object", "properties": {"expr": {"type": "string"}}},
        }
    ]


def test_normalize_gemini_format() -> None:
    tools: List[Dict[str, Any]] = [
        {
            "function_declarations": [
                {
                    "name": "lookup",
                    "description": "Look up a value",
                    "parameters": {"type": "object", "properties": {"key": {"type": "string"}}},
                },
                {
                    "name": "store",
                    "description": "Store a value",
                    "parameters": {"type": "object"},
                },
            ]
        }
    ]
    result = _normalize_tool_definitions(tools)
    assert len(result) == 2
    assert result[0]["name"] == "lookup"
    assert result[1]["name"] == "store"


def test_normalize_generic_format() -> None:
    tools: List[Dict[str, Any]] = [
        {"name": "do_thing", "description": "Does a thing", "parameters": None}
    ]
    result = _normalize_tool_definitions(tools)
    assert result == [{"name": "do_thing", "description": "Does a thing", "parameters": None}]


def test_normalize_empty_list() -> None:
    assert _normalize_tool_definitions([]) == []


def test_normalize_skips_non_dict() -> None:
    result = _normalize_tool_definitions(["not_a_dict", 42])  # type: ignore[list-item]
    assert result == []


def test_normalize_mixed_providers() -> None:
    tools: List[Dict[str, Any]] = [
        {"type": "function", "function": {"name": "openai_fn", "description": "OAI"}},
        {"name": "anthropic_fn", "description": "Ant", "input_schema": {}},
        {"name": "generic_fn", "description": "Gen"},
    ]
    result = _normalize_tool_definitions(tools)
    assert len(result) == 3
    assert [r["name"] for r in result] == ["openai_fn", "anthropic_fn", "generic_fn"]


# ---------------------------------------------------------------------------
# PrivacyConfig.sanitize_tool_definitions
# ---------------------------------------------------------------------------


def _make_tools() -> List[Dict[str, Any]]:
    return [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "description": "Get weather for a city",
                "parameters": {"type": "object", "properties": {"city": {"type": "string"}}},
            },
        },
        {
            "type": "function",
            "function": {
                "name": "send_email",
                "description": "Send an email",
                "parameters": {"type": "object", "properties": {"to": {"type": "string"}}},
            },
        },
    ]


def _expected_hash(tools: List[Dict[str, Any]]) -> str:
    normalized = _normalize_tool_definitions(tools)
    canonical = json.dumps(normalized, sort_keys=True, separators=(",", ":"))
    return hashlib.sha256(canonical.encode()).hexdigest()[:16]


def test_sanitize_full_mode_includes_content() -> None:
    pc = PrivacyConfig(privacy_mode=False)
    tools = _make_tools()
    result = pc.sanitize_tool_definitions(tools)

    assert result[PROP_TOOL_DEFINITIONS_COUNT] == 2
    assert result[PROP_TOOL_DEFINITIONS_HASH] == _expected_hash(tools)
    assert PROP_TOOL_DEFINITIONS in result
    parsed = json.loads(result[PROP_TOOL_DEFINITIONS])
    assert len(parsed) == 2
    assert parsed[0]["name"] == "get_weather"


def test_sanitize_metadata_only_excludes_content() -> None:
    pc = PrivacyConfig(privacy_mode=True)
    tools = _make_tools()
    result = pc.sanitize_tool_definitions(tools)

    assert result[PROP_TOOL_DEFINITIONS_COUNT] == 2
    assert PROP_TOOL_DEFINITIONS_HASH in result
    assert PROP_TOOL_DEFINITIONS not in result


def test_sanitize_content_mode_metadata_only() -> None:
    pc = PrivacyConfig(content_mode="metadata_only")
    tools = _make_tools()
    result = pc.sanitize_tool_definitions(tools)

    assert result[PROP_TOOL_DEFINITIONS_COUNT] == 2
    assert PROP_TOOL_DEFINITIONS not in result


def test_sanitize_none_returns_empty() -> None:
    pc = PrivacyConfig()
    result = pc.sanitize_tool_definitions(None)
    assert result == {}


def test_sanitize_empty_list_returns_empty() -> None:
    pc = PrivacyConfig()
    result = pc.sanitize_tool_definitions([])
    assert result == {}


def test_sanitize_hash_is_stable() -> None:
    """Same tools should produce the same hash regardless of call order."""
    pc = PrivacyConfig()
    tools = _make_tools()
    r1 = pc.sanitize_tool_definitions(tools)
    r2 = pc.sanitize_tool_definitions(tools)
    assert r1[PROP_TOOL_DEFINITIONS_HASH] == r2[PROP_TOOL_DEFINITIONS_HASH]


def test_sanitize_hash_changes_with_different_tools() -> None:
    pc = PrivacyConfig()
    tools_a = [{"type": "function", "function": {"name": "a", "description": "A"}}]
    tools_b = [{"type": "function", "function": {"name": "b", "description": "B"}}]
    r_a = pc.sanitize_tool_definitions(tools_a)
    r_b = pc.sanitize_tool_definitions(tools_b)
    assert r_a[PROP_TOOL_DEFINITIONS_HASH] != r_b[PROP_TOOL_DEFINITIONS_HASH]


def test_sanitize_truncates_large_definitions() -> None:
    pc = PrivacyConfig(privacy_mode=False)
    large_desc = "x" * 20000
    tools: List[Dict[str, Any]] = [
        {"type": "function", "function": {"name": "big", "description": large_desc}}
    ]
    result = pc.sanitize_tool_definitions(tools)
    assert len(result[PROP_TOOL_DEFINITIONS]) <= 10000
