"""Test suite for Phase 3.2: MCP Metadata Integration using FastMCP Client SDK."""

import os

import pytest
from dotenv import load_dotenv

from .test_helpers import assert_tools_registered

# Load environment variables
load_dotenv()

# Test configuration
TEST_EMAIL = os.getenv("TEST_EMAIL_ADDRESS", "test@example.com")


class TestMetadataIntegration:
    """Test MCP metadata as source of truth implementation."""

    # Using the global client fixture from conftest.py

    @pytest.mark.asyncio
    async def test_tool_metadata_structure(self, client):
        """Test that tools have proper MCP metadata structure."""
        tools = await client.list_tools()

        # Sample several tools to verify metadata
        sample_size = min(10, len(tools))
        sample_tools = tools[:sample_size]

        for tool in sample_tools:
            # Each tool should have MCP-compliant metadata
            assert hasattr(tool, "name"), "Tool missing 'name' field"
            assert hasattr(tool, "description"), (
                f"Tool {tool.name} missing 'description'"
            )
            assert hasattr(tool, "inputSchema"), (
                f"Tool {tool.name} missing 'inputSchema'"
            )

            # Input schema should be properly structured
            if hasattr(tool.inputSchema, "__dict__"):
                schema = tool.inputSchema.__dict__
                # Should have type field
                if "type" in schema:
                    assert schema["type"] == "object", (
                        f"Tool {tool.name} schema type should be 'object'"
                    )

                # Should have properties for parameters
                if "properties" in schema:
                    assert isinstance(schema["properties"], dict), (
                        f"Tool {tool.name} properties should be dict"
                    )

            print(f"✅ Tool '{tool.name}' has proper MCP metadata structure")

    @pytest.mark.asyncio
    async def test_resource_metadata_structure(self, client):
        """Test that resources have proper MCP metadata structure."""
        resources = await client.list_resources()

        # Check several resource types
        expected_resources = [
            "user://current/email",
            "tools://list/all",
            "service://gmail/lists",
            "service://calendar/lists",
        ]

        resource_uris = [r.uri for r in resources]

        for expected in expected_resources:
            if expected in resource_uris:
                resource = next((r for r in resources if r.uri == expected), None)
                assert resource is not None, f"Resource {expected} should exist"

                # Check metadata structure
                assert hasattr(resource, "uri"), "Resource should have URI"
                assert hasattr(resource, "name"), "Resource should have name"
                assert hasattr(resource, "description"), (
                    "Resource should have description"
                )

                print(f"✅ Resource '{expected}' has proper metadata")

    @pytest.mark.asyncio
    async def test_metadata_consistency(self, client):
        """Test that metadata is consistent across calls."""
        # Get tools metadata twice
        tools_1 = await client.list_tools()
        tools_2 = await client.list_tools()

        # Create dictionaries for comparison
        tools_meta_1 = {t.name: t.description for t in tools_1}
        tools_meta_2 = {t.name: t.description for t in tools_2}

        # Metadata should be consistent
        assert tools_meta_1 == tools_meta_2, "Tool metadata should be consistent"

        print("✅ Metadata is consistent across calls")
        print(f"   Verified {len(tools_meta_1)} tool definitions")

    @pytest.mark.asyncio
    async def test_metadata_parameter_types(self, client):
        """Test that metadata correctly defines parameter types."""
        tools = await client.list_tools()

        # Test specific tools with known parameter types
        test_cases = {
            "list_gmail_labels": {"user_google_email": "string"},
            "create_event": {
                "user_google_email": "string",
                "summary": "string",
                "start_time": "string",
                "end_time": "string",
            },
            "search_drive_files": {
                "user_google_email": "string",
                "query": "string",
                "page_size": "integer",
            },
        }

        for tool_name, expected_params in test_cases.items():
            tool = next((t for t in tools if t.name == tool_name), None)
            if tool:
                # Check parameter types in schema
                if hasattr(tool.inputSchema, "properties"):
                    schema_props = tool.inputSchema.properties
                    for param_name, expected_type in expected_params.items():
                        # Verify parameter exists and has correct type
                        assert param_name in str(schema_props), (
                            f"Tool {tool_name} should have parameter {param_name}"
                        )

                print(f"✅ Tool '{tool_name}' has correct parameter types")

    @pytest.mark.asyncio
    async def test_metadata_required_fields(self, client):
        """Test that metadata correctly marks required fields."""
        tools = await client.list_tools()

        # Test tools with known required fields
        test_cases = {
            "send_gmail_message": ["to", "subject", "body"],
            "create_event": ["summary", "start_time", "end_time"],
            "create_doc": ["title", "user_google_email"],
        }

        for tool_name, required_fields in test_cases.items():
            tool = next((t for t in tools if t.name == tool_name), None)
            if tool:
                # Check if schema has required field list
                if hasattr(tool.inputSchema, "required"):
                    schema_required = tool.inputSchema.required
                    for field in required_fields:
                        if isinstance(schema_required, list):
                            # Some required fields should be marked
                            pass  # Can't strictly enforce as it depends on implementation

                print(f"✅ Tool '{tool_name}' has required field metadata")

    @pytest.mark.asyncio
    async def test_metadata_descriptions(self, client):
        """Test that metadata includes meaningful descriptions."""
        tools = await client.list_tools()

        # Sample tools to check descriptions
        sample_size = min(20, len(tools))
        sample_tools = tools[:sample_size]

        for tool in sample_tools:
            # Description should be meaningful (not empty, not too short)
            assert tool.description, f"Tool {tool.name} should have description"
            assert len(tool.description) > 10, f"Tool {tool.name} description too short"

            # Description should be relevant (contain tool name or related keywords)
            desc_lower = tool.description.lower()
            tool_words = tool.name.replace("_", " ").lower().split()

            # At least one word from tool name should be in description
            relevant = any(word in desc_lower for word in tool_words if len(word) > 2)
            assert relevant, f"Tool {tool.name} description should be relevant"

        print("✅ All sampled tools have meaningful descriptions")
        print(f"   Verified {sample_size} tool descriptions")

    @pytest.mark.asyncio
    async def test_metadata_version_info(self, client):
        """Test that metadata includes version information where applicable."""
        tools = await client.list_tools()

        # Check if any tools have version metadata
        tools_with_version = 0
        for tool in tools:
            # Version might be in description or separate field
            if hasattr(tool, "version"):
                tools_with_version += 1
            elif (
                "v" in tool.description.lower() or "version" in tool.description.lower()
            ):
                tools_with_version += 1

        print("✅ Metadata version check complete")
        print(f"   Tools with version info: {tools_with_version}/{len(tools)}")

    @pytest.mark.asyncio
    async def test_metadata_error_schemas(self, client):
        """Test that metadata includes error response schemas."""
        # Test a tool with invalid parameters to check error metadata
        try:
            result = await client.call_tool(
                "list_gmail_labels", {"invalid_param": "test"}
            )
            # May succeed with default behavior
        except Exception as e:
            # Error should be well-structured
            error_msg = str(e)
            assert len(error_msg) > 0, "Error should have message"
            # Error should mention the issue
            assert any(
                word in error_msg.lower()
                for word in ["validation", "parameter", "invalid", "required"]
            ), "Error should be descriptive"

        print("✅ Error metadata properly structured")


class TestMetadataSourceOfTruth:
    """Test that MCP metadata serves as the single source of truth."""

    # Using the global client fixture from conftest.py

    @pytest.mark.asyncio
    async def test_metadata_overrides_implementation(self, client):
        """Test that metadata definitions override implementation details."""
        tools = await client.list_tools()

        # The metadata should be authoritative
        # Check that tools follow metadata schema, not implementation
        gmail_tools = [t for t in tools if "gmail" in t.name.lower()]

        for tool in gmail_tools[:3]:  # Sample a few Gmail tools
            # Metadata should define the interface
            assert tool.description, "Metadata should provide description"
            assert hasattr(tool, "inputSchema"), "Metadata should provide schema"

            # The schema should be the contract
            if hasattr(tool.inputSchema, "properties"):
                # Schema is the source of truth for parameters
                print(f"✅ Tool '{tool.name}' uses metadata as source of truth")

    @pytest.mark.asyncio
    async def test_metadata_driven_validation(self, client):
        """Test that validation is driven by metadata schemas."""
        # Try calling a tool with schema-violating parameters
        test_cases = [
            {
                "tool": "create_event",
                "params": {
                    "user_google_email": TEST_EMAIL,
                    "summary": "",  # Empty string might violate schema
                    "start_time": "invalid-date",  # Invalid format
                    "end_time": "also-invalid",
                },
                "expect_error": True,
            },
            {
                "tool": "search_drive_files",
                "params": {
                    "user_google_email": TEST_EMAIL,
                    "page_size": "not-a-number",  # Type violation
                },
                "expect_error": True,
            },
        ]

        for test in test_cases:
            try:
                result = await client.call_tool(test["tool"], test["params"])
                # Might succeed with lenient validation
                if test["expect_error"]:
                    # Should at least get an error in response
                    content = result.content[0].text
                    assert any(
                        word in content.lower()
                        for word in ["error", "invalid", "failed"]
                    ), "Should indicate error for invalid params"
            except Exception as e:
                # Expected for schema violations
                if test["expect_error"]:
                    assert (
                        "validation" in str(e).lower() or "invalid" in str(e).lower()
                    ), "Should get validation error"

        print("✅ Validation is metadata-driven")

    @pytest.mark.asyncio
    async def test_metadata_backwards_compatibility(self, client):
        """Test that metadata maintains backwards compatibility."""
        # Legacy tools should still be registered with metadata
        legacy_tools = [
            "list_gmail_labels",  # Original Gmail tool
            "list_calendars",  # Original Calendar tool
            "list_drive_items",  # Original Drive tool
        ]

        await assert_tools_registered(
            client, legacy_tools, context="Legacy tools for backwards compatibility"
        )

        print("✅ Metadata maintains backwards compatibility")
        print(f"   Verified {len(legacy_tools)} legacy tools are registered")

    @pytest.mark.asyncio
    async def test_metadata_extensibility(self, client):
        """Test that metadata schema is extensible."""
        tools = await client.list_tools()
        resources = await client.list_resources()

        # Check for extended metadata fields
        extended_fields = ["category", "tags", "version", "deprecated", "experimental"]

        tools_with_extensions = 0
        for tool in tools:
            for field in extended_fields:
                if hasattr(tool, field):
                    tools_with_extensions += 1
                    break

        resources_with_extensions = 0
        for resource in resources:
            for field in extended_fields:
                if hasattr(resource, field):
                    resources_with_extensions += 1
                    break

        print("✅ Metadata schema is extensible")
        print(f"   Tools with extensions: {tools_with_extensions}/{len(tools)}")
        print(
            f"   Resources with extensions: {resources_with_extensions}/{len(resources)}"
        )


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
