"""
Tests for EPIC 4 — MCP Prompts & Client Integration

Run:
    python -m pytest tests/test_epic4_mcp.py -v
"""

from __future__ import annotations

import asyncio
import json
import os
import tempfile
from pathlib import Path
from unittest.mock import patch

import pytest


# ---------------------------------------------------------------------------
# Helper
# ---------------------------------------------------------------------------

def _run(coro):
    """Run an async coroutine synchronously."""
    return asyncio.run(coro)


# ===================================================================
# TASK 4.1 — MCP Prompts
# ===================================================================

class TestPromptRegistry:
    """Verify the prompt registry is complete and well-formed."""

    def test_registry_has_four_prompts(self):
        from supreme_max.mcp_server.prompts import PROMPT_REGISTRY
        assert len(PROMPT_REGISTRY) == 4

    def test_registry_names(self):
        from supreme_max.mcp_server.prompts import PROMPT_REGISTRY
        names = {p["name"] for p in PROMPT_REGISTRY}
        assert names == {
            "security_scan_workflow",
            "pr_security_check",
            "false_positive_analysis",
            "vulnerability_remediation",
        }

    def test_registry_entries_have_required_keys(self):
        from supreme_max.mcp_server.prompts import PROMPT_REGISTRY
        for entry in PROMPT_REGISTRY:
            assert "name" in entry
            assert "description" in entry
            assert "handler" in entry
            assert callable(entry["handler"])


class TestSecurityScanWorkflow:
    """Test the security_scan_workflow prompt."""

    def test_returns_string(self):
        from supreme_max.mcp_server.prompts import security_scan_workflow
        result = _run(security_scan_workflow())
        assert isinstance(result, str)
        assert len(result) > 100

    def test_references_workflow_tools(self):
        from supreme_max.mcp_server.prompts import security_scan_workflow
        result = _run(security_scan_workflow())
        assert "analyze_project" in result
        assert "run_security_scan" in result
        assert "analyze_findings" in result
        assert "generate_report" in result

    def test_contains_all_steps(self):
        from supreme_max.mcp_server.prompts import security_scan_workflow
        result = _run(security_scan_workflow())
        for step in ["1.", "2.", "3.", "4.", "5.", "6."]:
            assert step in result


class TestPrSecurityCheck:
    """Test the pr_security_check prompt."""

    def test_returns_string(self):
        from supreme_max.mcp_server.prompts import pr_security_check
        result = _run(pr_security_check())
        assert isinstance(result, str)
        assert len(result) > 100

    def test_references_quick_mode(self):
        from supreme_max.mcp_server.prompts import pr_security_check
        result = _run(pr_security_check())
        assert "quick" in result.lower()
        assert "CRITICAL" in result
        assert "HIGH" in result

    def test_typical_response_example(self):
        from supreme_max.mcp_server.prompts import pr_security_check
        result = _run(pr_security_check())
        assert "SQL injection" in result
        assert "XSS" in result


class TestFalsePositiveAnalysis:
    """Test the false_positive_analysis prompt."""

    def test_returns_string(self):
        from supreme_max.mcp_server.prompts import false_positive_analysis
        result = _run(false_positive_analysis())
        assert isinstance(result, str)
        assert len(result) > 100

    def test_references_fp_fields(self):
        from supreme_max.mcp_server.prompts import false_positive_analysis
        result = _run(false_positive_analysis())
        assert "likely_false_positive" in result
        assert "fp_reason" in result
        assert "confidence" in result
        assert "priority_score" in result

    def test_confidence_ranges(self):
        from supreme_max.mcp_server.prompts import false_positive_analysis
        result = _run(false_positive_analysis())
        assert "0.8" in result
        assert "0.5" in result


class TestVulnerabilityRemediation:
    """Test the vulnerability_remediation prompt."""

    def test_returns_string(self):
        from supreme_max.mcp_server.prompts import vulnerability_remediation
        result = _run(vulnerability_remediation())
        assert isinstance(result, str)
        assert len(result) > 100

    def test_contains_sql_injection_fix(self):
        from supreme_max.mcp_server.prompts import vulnerability_remediation
        result = _run(vulnerability_remediation())
        assert "SQL Injection" in result
        assert "?" in result  # parameterized query marker

    def test_contains_xss_fix(self):
        from supreme_max.mcp_server.prompts import vulnerability_remediation
        result = _run(vulnerability_remediation())
        assert "XSS" in result
        assert "textContent" in result

    def test_contains_hardcoded_secrets_fix(self):
        from supreme_max.mcp_server.prompts import vulnerability_remediation
        result = _run(vulnerability_remediation())
        assert "Hardcoded Secrets" in result
        assert "os.getenv" in result

    def test_contains_path_traversal_fix(self):
        from supreme_max.mcp_server.prompts import vulnerability_remediation
        result = _run(vulnerability_remediation())
        assert "Path Traversal" in result
        assert "is_relative_to" in result

    def test_references_rescan(self):
        from supreme_max.mcp_server.prompts import vulnerability_remediation
        result = _run(vulnerability_remediation())
        assert "run_security_scan" in result


# ===================================================================
# TASK 4.2 — MCP Client Integration (setup.py)
# ===================================================================

class TestSetupMcpClient:
    """Test the setup_mcp_client dispatcher."""

    def test_supported_clients_list(self):
        from supreme_max.mcp_server.setup import SUPPORTED_CLIENTS
        assert "claude-desktop" in SUPPORTED_CLIENTS
        assert "cline" in SUPPORTED_CLIENTS
        assert "cursor" in SUPPORTED_CLIENTS
        assert "copilot" in SUPPORTED_CLIENTS

    def test_unsupported_client_raises(self):
        from supreme_max.mcp_server.setup import setup_mcp_client
        with pytest.raises(ValueError, match="Unsupported MCP client"):
            setup_mcp_client("unknown-client")


class TestSetupClaudeDesktop:
    """Test Claude Desktop auto-setup writes correct config."""

    def test_creates_config(self):
        from supreme_max.mcp_server.setup import setup_claude_desktop, _claude_desktop_config_path

        with tempfile.TemporaryDirectory() as tmpdir:
            fake_path = Path(tmpdir) / "claude_desktop_config.json"
            with patch(
                "supreme2l.mcp_server.setup._claude_desktop_config_path",
                return_value=fake_path,
            ):
                result = setup_claude_desktop()

            assert fake_path.is_file()
            data = json.loads(fake_path.read_text())
            assert "mcpServers" in data
            assert "supreme_max" in data["mcpServers"]
            srv = data["mcpServers"]["supreme_max"]
            assert "command" in srv
            assert srv["args"] == ["-m", "supreme2l.mcp_server"]
            assert result["client"] == "claude-desktop"

    def test_merges_with_existing(self):
        from supreme_max.mcp_server.setup import setup_claude_desktop

        with tempfile.TemporaryDirectory() as tmpdir:
            fake_path = Path(tmpdir) / "claude_desktop_config.json"
            # Pre-existing config with another server
            existing = {
                "mcpServers": {
                    "other-tool": {"command": "other", "args": []}
                }
            }
            fake_path.write_text(json.dumps(existing))

            with patch(
                "supreme2l.mcp_server.setup._claude_desktop_config_path",
                return_value=fake_path,
            ):
                setup_claude_desktop()

            data = json.loads(fake_path.read_text())
            assert "other-tool" in data["mcpServers"], "Existing server should be preserved"
            assert "supreme_max" in data["mcpServers"], "Our server should be added"


class TestSetupCline:
    """Test Cline auto-setup."""

    def test_creates_config(self):
        from supreme_max.mcp_server.setup import setup_cline

        with tempfile.TemporaryDirectory() as tmpdir:
            fake_path = Path(tmpdir) / "cline_mcp_settings.json"
            with patch(
                "supreme2l.mcp_server.setup._cline_config_path",
                return_value=fake_path,
            ):
                result = setup_cline()

            assert fake_path.is_file()
            data = json.loads(fake_path.read_text())
            assert "supreme_max" in data["mcpServers"]
            assert result["client"] == "cline"


class TestSetupCursor:
    """Test Cursor auto-setup."""

    def test_creates_config(self):
        import os
        from supreme_max.mcp_server.setup import setup_cursor

        with tempfile.TemporaryDirectory() as tmpdir:
            # Change to temp directory and mock cursor detection
            saved_cwd = os.getcwd()
            os.chdir(tmpdir)
            try:
                with patch(
                    "supreme2l.mcp_server.setup.detect_cursor_installation",
                    return_value=Path("/usr/bin/cursor"),
                ):
                    result = setup_cursor()
            finally:
                os.chdir(saved_cwd)

            config_path = Path(tmpdir) / ".cursor" / "mcp.json"
            assert config_path.is_file()
            data = json.loads(config_path.read_text())
            assert "supreme_max" in data["mcpServers"]
            assert result["client"] == "cursor"
            assert result["success"] is True


class TestSetupCopilot:
    """Test VS Code Copilot auto-setup."""

    def test_creates_config(self):
        from supreme_max.mcp_server.setup import setup_copilot

        with tempfile.TemporaryDirectory() as tmpdir:
            fake_path = Path(tmpdir) / "mcp.json"
            with patch(
                "supreme2l.mcp_server.setup._copilot_config_path",
                return_value=fake_path,
            ):
                result = setup_copilot()

            assert fake_path.is_file()
            data = json.loads(fake_path.read_text())
            assert "servers" in data
            assert "supreme_max" in data["servers"]
            assert data["servers"]["supreme_max"]["type"] == "stdio"
            assert result["client"] == "copilot"


class TestCliIntegration:
    """Test the CLI wiring."""

    def test_mcp_group_registered(self):
        from click.testing import CliRunner
        from supreme_max.cli import main

        runner = CliRunner()
        result = runner.invoke(main, ["mcp", "--help"])
        assert result.exit_code == 0
        assert "setup" in result.output

    def test_mcp_setup_help(self):
        from click.testing import CliRunner
        from supreme_max.cli import main

        runner = CliRunner()
        result = runner.invoke(main, ["mcp", "setup", "--help"])
        assert result.exit_code == 0
        assert "claude-desktop" in result.output
        assert "cline" in result.output
        assert "cursor" in result.output
        assert "copilot" in result.output

    def test_mcp_setup_claude_desktop(self):
        from click.testing import CliRunner
        from supreme_max.cli import main

        with tempfile.TemporaryDirectory() as tmpdir:
            fake_path = Path(tmpdir) / "config.json"
            with patch(
                "supreme2l.mcp_server.setup._claude_desktop_config_path",
                return_value=fake_path,
            ):
                runner = CliRunner()
                result = runner.invoke(main, ["mcp", "setup", "--client", "claude-desktop"])

            assert result.exit_code == 0
            assert "configured successfully" in result.output
            assert fake_path.is_file()


# ===================================================================
# Example config files validation
# ===================================================================

class TestExampleConfigs:
    """Validate all shipped example config files are valid JSON."""

    EXAMPLES_DIR = Path(__file__).resolve().parent.parent / "examples" / "mcp-clients"

    @pytest.mark.parametrize("relpath", [
        "claude-desktop/config_unix.json",
        "claude-desktop/config_windows.json",
        "vscode-cline/config.json",
        "vscode-copilot/config.json",
        "cursor/mcp.json",
    ])
    def test_example_is_valid_json(self, relpath):
        path = self.EXAMPLES_DIR / relpath
        assert path.is_file(), f"Missing example config: {path}"
        data = json.loads(path.read_text(encoding="utf-8"))
        # All should reference supreme2l
        flat = json.dumps(data)
        assert "supreme_max" in flat

    def test_claude_desktop_unix_structure(self):
        data = json.loads(
            (self.EXAMPLES_DIR / "claude-desktop" / "config_unix.json").read_text()
        )
        assert "mcpServers" in data
        assert "supreme_max" in data["mcpServers"]
        assert data["mcpServers"]["supreme_max"]["command"] == "python3"

    def test_claude_desktop_windows_structure(self):
        data = json.loads(
            (self.EXAMPLES_DIR / "claude-desktop" / "config_windows.json").read_text()
        )
        assert data["mcpServers"]["supreme_max"]["command"] == "python"

    def test_copilot_uses_servers_key(self):
        data = json.loads(
            (self.EXAMPLES_DIR / "vscode-copilot" / "config.json").read_text()
        )
        assert "servers" in data
        assert data["servers"]["supreme_max"]["type"] == "stdio"
