"""
End-to-end tests for MCP tool integration.

These tests verify that the complete MCP integration pipeline works:
1. Configuration loading
2. MCP tool discovery
3. Tool registration and availability
4. Reply engine integration

Note: These tests are marked as @pytest.mark.e2e and may not run in basic CI environments.
They are intended for local development and git hook testing.
"""

import sys
import os
import json
import tempfile
from pathlib import Path
import pytest

# Add project root to path
sys.path.insert(0, str(Path(__file__).parent.parent))

from src.jarvis.tools.registry import discover_mcp_tools, generate_tools_description
from src.jarvis.config import load_settings


@pytest.mark.e2e
def test_configuration_loading():
    """Test that MCP configuration is properly loaded."""
    print("🔧 Testing MCP configuration loading...")

    try:
        cfg = load_settings()
        mcps = getattr(cfg, 'mcps', {})

        print(f"   Found {len(mcps)} configured MCP servers")
        for server_name, server_config in mcps.items():
            command = server_config.get('command', 'unknown')
            print(f"   - {server_name}: {command}")

        # Assert that we have at least some MCP configuration
        assert isinstance(mcps, dict), "MCP configuration should be a dictionary"
        print("   ✅ Configuration loading successful")

    except Exception as e:
        print(f"   ❌ Failed to load configuration: {e}")
        assert False, f"Failed to load configuration: {e}"


@pytest.mark.e2e
def test_mcp_discovery_with_mock():
    """Test MCP discovery with mocked servers."""
    print("🔍 Testing MCP tool discovery (mocked)...")

    # Create a fake MCP configuration
    fake_mcps = {
        "test-server": {
            "command": "echo",
            "args": ["test"]
        }
    }

    # Mock the MCPClient to avoid actual server connections
    from unittest.mock import patch, Mock

    class FakeMCPClient:
        def __init__(self, config):
            self.config = config

        def list_tools(self, server_name):
            return [
                {"name": "read", "description": "Read a file"},
                {"name": "write", "description": "Write a file"},
                {"name": "list", "description": "List directory contents"}
            ]

    try:
        with patch('src.jarvis.tools.registry.MCPClient', FakeMCPClient):
            mcp_tools, _errors = discover_mcp_tools(fake_mcps)

        expected_tools = {
            "test-server__read",
            "test-server__write",
            "test-server__list"
        }

        actual_tools = set(mcp_tools.keys())

        assert actual_tools == expected_tools, f"Tool mismatch. Expected: {expected_tools}, Got: {actual_tools}"
        print(f"   ✅ Successfully discovered {len(mcp_tools)} tools")
        for tool_name in mcp_tools:
            print(f"      - {tool_name}")

    except Exception as e:
        print(f"   ❌ Discovery failed: {e}")
        import traceback
        traceback.print_exc()
        assert False, f"Discovery failed: {e}"


@pytest.mark.e2e
def test_tool_registration_in_descriptions():
    """Test that discovered tools appear in tool descriptions."""
    print("📝 Testing tool description generation...")

    from src.jarvis.tools.registry import ToolSpec

    # Create mock MCP tools
    mock_mcp_tools = {
            "server1__tool1": ToolSpec(
                name="server1__tool1",
                description="Test tool 1 from server1",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": []
                }
            ),
            "server2__tool2": ToolSpec(
                name="server2__tool2",
                description="Test tool 2 from server2",
                inputSchema={
                    "type": "object",
                    "properties": {},
                    "required": []
                }
            )
        }

    try:
        allowed_tools = ["screenshot", "webSearch", "server1__tool1", "server2__tool2"]
        description = generate_tools_description(allowed_tools, mock_mcp_tools)

        # Check that MCP tools appear in description
        success = True
        for tool_name in mock_mcp_tools:
            if tool_name not in description:
                print(f"   ❌ Tool {tool_name} not found in description")
                success = False

        assert success, "Not all MCP tools appear in descriptions"
        print("   ✅ All MCP tools appear in descriptions")
        print(f"   📄 Description length: {len(description)} characters")

    except Exception as e:
        print(f"   ❌ Description generation failed: {e}")
        assert False, f"Description generation failed: {e}"


@pytest.mark.e2e
def test_tool_name_format():
    """Test that tool names follow the server__toolname format."""
    print("🏷️  Testing tool naming convention...")

    from unittest.mock import patch

    class FakeMCPClient:
        def __init__(self, config):
            self.config = config

        def list_tools(self, server_name):
            if server_name == "my-server":
                return [
                    {"name": "tool_with_underscores", "description": "Test tool"},
                    {"name": "tool-with-dashes", "description": "Another test tool"},
                    {"name": "simpletool", "description": "Simple tool"}
                ]
            return []

    try:
        with patch('src.jarvis.tools.registry.MCPClient', FakeMCPClient):
            mcps_config = {"my-server": {"command": "test"}}
            mcp_tools, _errors = discover_mcp_tools(mcps_config)

        expected_names = {
            "my-server__tool_with_underscores",
            "my-server__tool-with-dashes",
            "my-server__simpletool"
        }

        actual_names = set(mcp_tools.keys())

        assert actual_names == expected_names, f"Naming mismatch. Expected: {expected_names}, Got: {actual_names}"
        print("   ✅ Tool naming convention is correct")
        for name in actual_names:
            print(f"      - {name}")

    except Exception as e:
        print(f"   ❌ Naming test failed: {e}")
        assert False, f"Naming test failed: {e}"


@pytest.mark.e2e
def test_error_handling():
    """Test that MCP errors are handled gracefully."""
    print("⚠️  Testing error handling...")

    from unittest.mock import patch

    class FaultyMCPClient:
        def __init__(self, config):
            pass

        def list_tools(self, server_name):
            if server_name == "good-server":
                return [{"name": "working_tool", "description": "This works"}]
            elif server_name == "bad-server":
                raise Exception("Server connection failed")
            return []

    try:
        with patch('src.jarvis.tools.registry.MCPClient', FaultyMCPClient):
            mcps_config = {
                "good-server": {"command": "good"},
                "bad-server": {"command": "bad"},
                "empty-server": {"command": "empty"}
            }
            mcp_tools, mcp_errors = discover_mcp_tools(mcps_config)

        # Should only get tools from the good server
        if len(mcp_tools) == 1 and "good-server__working_tool" in mcp_tools:
            print("   ✅ Error handling works correctly")
            print("      - Good server tools discovered")
            print("      - Bad server errors handled gracefully")
        else:
            print(f"   ❌ Expected 1 tool, got {len(mcp_tools)}: {list(mcp_tools.keys())}")
            assert False, f"Expected 1 tool, got {len(mcp_tools)}: {list(mcp_tools.keys())}"

    except Exception as e:
        print(f"   ❌ Error handling test failed: {e}")
        assert False, f"Error handling test failed: {e}"
