"""
Tests for Google ADK compatibility - ensuring tool schemas don't use anyOf alongside other fields.
"""

import pytest
import asyncio
import json
from wikipedia_mcp.server import create_server
from tests.tool_helpers import get_tool, get_tools


class TestGoogleADKCompatibility:
    """Test that all tools generate Google ADK compatible schemas."""

    @pytest.mark.asyncio
    async def test_all_tools_schemas_compatible(self):
        """Test that all tool schemas are compatible with Google ADK agents."""
        server = create_server()
        tools = await get_tools(server)

        # Tools that previously had anyOf issues
        problematic_tools = [
            "summarize_article_for_query",
            "summarize_article_section",
            "extract_key_facts",
        ]

        for expected_tool in problematic_tools:
            assert expected_tool in tools, f"Expected tool '{expected_tool}' to be registered"

        for tool_name in tools:
            tool_obj = await get_tool(server, tool_name)
            schema = tool_obj.parameters

            # Check each parameter for anyOf usage
            for param_name, param_schema in schema.get("properties", {}).items():
                assert (
                    "anyOf" not in param_schema
                ), f"Tool '{tool_name}' parameter '{param_name}' uses anyOf which is incompatible with Google ADK"

                # Verify that if a default is provided, the schema is simple
                if "default" in param_schema:
                    # Should only have: type, default, title (and optionally description)
                    allowed_keys = {"type", "default", "title", "description"}
                    actual_keys = set(param_schema.keys())
                    unexpected_keys = actual_keys - allowed_keys
                    assert (
                        not unexpected_keys
                    ), f"Tool '{tool_name}' parameter '{param_name}' has unexpected keys: {unexpected_keys}"

    @pytest.mark.asyncio
    async def test_all_tools_have_readonly_annotations(self):
        """All tools should include MCP behavior annotations for clients."""
        server = create_server()
        tools = await get_tools(server)

        for tool_name in tools:
            tool_obj = await get_tool(server, tool_name)
            annotations = tool_obj.annotations
            assert annotations is not None, f"Tool '{tool_name}' must define annotations"
            assert annotations.readOnlyHint is True
            assert annotations.idempotentHint is True
            assert annotations.openWorldHint is True
            assert annotations.destructiveHint is False

    @pytest.mark.asyncio
    async def test_all_canonical_tools_have_wikipedia_aliases(self):
        """Each canonical tool should expose a wikipedia_* alias."""
        server = create_server()
        tools = await get_tools(server)

        canonical_tools = [
            "search_wikipedia",
            "test_wikipedia_connectivity",
            "get_article",
            "get_summary",
            "summarize_article_for_query",
            "summarize_article_section",
            "extract_key_facts",
            "get_related_topics",
            "get_sections",
            "get_links",
            "get_coordinates",
        ]

        for tool_name in canonical_tools:
            assert tool_name in tools
            assert f"wikipedia_{tool_name}" in tools

    @pytest.mark.asyncio
    async def test_all_tools_have_object_output_schema(self):
        """Output schemas should be explicit object schemas."""
        server = create_server()
        tools = await get_tools(server)

        for tool_name in tools:
            tool_obj = await get_tool(server, tool_name)
            assert isinstance(tool_obj.output_schema, dict)
            assert tool_obj.output_schema.get("type") == "object"

    @pytest.mark.asyncio
    async def test_summarize_article_for_query_schema(self):
        """Test specific schema for summarize_article_for_query tool."""
        server = create_server()
        tool_obj = await get_tool(server, "summarize_article_for_query")
        schema = tool_obj.parameters

        # Check max_length parameter specifically
        max_length_param = schema["properties"]["max_length"]
        assert max_length_param["type"] == "integer"
        assert max_length_param["default"] == 250
        assert "anyOf" not in max_length_param
        if "title" in max_length_param:
            assert max_length_param["title"] == "Max Length"

        # Check required parameters
        assert set(schema["required"]) == {"title", "query"}

    @pytest.mark.asyncio
    async def test_summarize_article_section_schema(self):
        """Test specific schema for summarize_article_section tool."""
        server = create_server()
        tool_obj = await get_tool(server, "summarize_article_section")
        schema = tool_obj.parameters

        # Check max_length parameter specifically
        max_length_param = schema["properties"]["max_length"]
        assert max_length_param["type"] == "integer"
        assert max_length_param["default"] == 150
        assert "anyOf" not in max_length_param
        if "title" in max_length_param:
            assert max_length_param["title"] == "Max Length"

        # Check required parameters
        assert set(schema["required"]) == {"title", "section_title"}

    @pytest.mark.asyncio
    async def test_extract_key_facts_schema(self):
        """Test specific schema for extract_key_facts tool."""
        server = create_server()
        tool_obj = await get_tool(server, "extract_key_facts")
        schema = tool_obj.parameters

        # Check topic_within_article parameter specifically
        topic_param = schema["properties"]["topic_within_article"]
        assert topic_param["type"] == "string"
        assert topic_param["default"] == ""
        assert "anyOf" not in topic_param
        if "title" in topic_param:
            assert topic_param["title"] == "Topic Within Article"

        # Check count parameter
        count_param = schema["properties"]["count"]
        assert count_param["type"] == "integer"
        assert count_param["default"] == 5

        # Check required parameters
        assert set(schema["required"]) == {"title"}

    def test_empty_string_to_none_conversion(self):
        """Test the conversion logic for empty string to None."""

        # This tests the logic used in extract_key_facts tool
        def convert_topic(topic_within_article: str):
            return topic_within_article if topic_within_article.strip() else None

        # Test cases
        assert convert_topic("") is None
        assert convert_topic("   ") is None  # whitespace only
        assert convert_topic("general") == "general"
        assert convert_topic("history") == "history"
        assert convert_topic(" history ") == " history "  # preserve as-is

    @pytest.mark.asyncio
    async def test_tool_compatibility_json_serialization(self):
        """Test that all tool schemas can be properly JSON serialized for Google ADK."""
        server = create_server()
        tools = await get_tools(server)

        for tool_name in tools:
            tool_obj = await get_tool(server, tool_name)
            schema = tool_obj.parameters

            # Should be able to serialize to JSON without issues
            json_str = json.dumps(schema)

            # Should be able to deserialize back
            parsed_schema = json.loads(json_str)
            assert parsed_schema == schema

            # Ensure no complex nested anyOf structures
            def check_no_anyof_recursively(obj):
                if isinstance(obj, dict):
                    for key, value in obj.items():
                        if key == "anyOf":
                            # If anyOf exists, it should be the only schema key
                            schema_keys = set(obj.keys()) - {
                                "title",
                                "description",
                                "default",
                            }
                            if len(schema_keys) > 1:
                                return False
                        if not check_no_anyof_recursively(value):
                            return False
                elif isinstance(obj, list):
                    for item in obj:
                        if not check_no_anyof_recursively(item):
                            return False
                return True

            assert check_no_anyof_recursively(
                schema
            ), f"Tool '{tool_name}' has complex anyOf structure incompatible with Google ADK"
