# -*- coding: utf-8 -*-
"""
MCP Config Detector — Find MCP configuration files across all known AI clients.

Supports: Claude Desktop, Claude Code, VS Code Copilot, Cursor, Cline, Continue.
Cross-platform: Windows, macOS, Linux.

Merged from products/mcp_config_sync/detector.py (R009) into tools/ (R010).
"""
import json
import os
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Optional

if sys.platform == "win32":
    sys.stdout.reconfigure(encoding="utf-8", errors="replace")


@dataclass
class DetectedConfig:
    """A detected MCP configuration file."""
    client: str
    path: Path
    scope: str  # "global" or "project"
    root_key: str  # "mcpServers", "servers", etc.
    server_count: int = 0
    servers: list = field(default_factory=list)
    error: Optional[str] = None

    def to_dict(self):
        return {
            "client": self.client,
            "path": str(self.path),
            "scope": self.scope,
            "root_key": self.root_key,
            "server_count": self.server_count,
            "servers": self.servers,
            "error": self.error,
        }


# ============================================================================
# CLIENT DEFINITIONS
# ============================================================================

def _home() -> Path:
    return Path.home()


def _appdata() -> Optional[Path]:
    """Windows APPDATA directory."""
    appdata = os.environ.get("APPDATA")
    return Path(appdata) if appdata else None


def _xdg_config() -> Path:
    """XDG config directory (Linux)."""
    return Path(os.environ.get("XDG_CONFIG_HOME", _home() / ".config"))


CLIENT_CONFIGS = [
    # Claude Desktop
    {
        "client": "Claude Desktop",
        "scope": "global",
        "root_key": "mcpServers",
        "paths": lambda: [
            _appdata() / "Claude" / "claude_desktop_config.json" if _appdata() else None,
            _home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json",
            _xdg_config() / "Claude" / "claude_desktop_config.json",
        ],
    },
    # Claude Code (global)
    {
        "client": "Claude Code",
        "scope": "global",
        "root_key": "mcpServers",
        "paths": lambda: [
            _home() / ".claude.json",
            _home() / ".claude" / "settings.json",
        ],
    },
    # Claude Code (project — searched from cwd)
    {
        "client": "Claude Code",
        "scope": "project",
        "root_key": "mcpServers",
        "paths": lambda: [
            Path.cwd() / ".mcp.json",
            Path.cwd() / ".claude" / "settings.local.json",
        ],
    },
    # VS Code Copilot
    {
        "client": "VS Code Copilot",
        "scope": "project",
        "root_key": "servers",
        "paths": lambda: [
            Path.cwd() / ".vscode" / "mcp.json",
        ],
    },
    # VS Code (user-level)
    {
        "client": "VS Code Copilot",
        "scope": "global",
        "root_key": "servers",
        "paths": lambda: [
            _appdata() / "Code" / "User" / "settings.json" if _appdata() else None,
            _home() / "Library" / "Application Support" / "Code" / "User" / "settings.json",
            _xdg_config() / "Code" / "User" / "settings.json",
        ],
    },
    # Cursor
    {
        "client": "Cursor",
        "scope": "project",
        "root_key": "mcpServers",
        "paths": lambda: [
            Path.cwd() / ".cursor" / "mcp.json",
        ],
    },
    # Cursor (global)
    {
        "client": "Cursor",
        "scope": "global",
        "root_key": "mcpServers",
        "paths": lambda: [
            _home() / ".cursor" / "mcp.json",
        ],
    },
    # Cline
    {
        "client": "Cline",
        "scope": "project",
        "root_key": "mcpServers",
        "paths": lambda: [
            Path.cwd() / ".cline" / "mcp_settings.json",
        ],
    },
    # Continue
    {
        "client": "Continue",
        "scope": "global",
        "root_key": "mcpServers",
        "paths": lambda: [
            _home() / ".continue" / "config.json",
        ],
    },
]


# ============================================================================
# DETECTION
# ============================================================================

def _extract_servers(data: dict, root_key: str, client: str) -> list:
    """Extract server names from config data, handling different formats."""
    if root_key in data:
        servers_section = data[root_key]
        if isinstance(servers_section, dict):
            return list(servers_section.keys())
        if isinstance(servers_section, list):
            return [s.get("name", f"server_{i}") for i, s in enumerate(servers_section)]

    # VS Code: may nest under "mcp.servers" or "mcp" key in settings.json
    if client == "VS Code Copilot":
        mcp = data.get("mcp", {})
        if isinstance(mcp, dict):
            svrs = mcp.get("servers", {})
            if isinstance(svrs, dict):
                return list(svrs.keys())

    return []


def detect_configs(project_dir: Optional[Path] = None) -> List[DetectedConfig]:
    """
    Scan filesystem for MCP configuration files.

    Args:
        project_dir: Override project directory (default: cwd)

    Returns:
        List of DetectedConfig objects for each found config.
    """
    original_cwd = Path.cwd()
    if project_dir:
        os.chdir(project_dir)

    found = []

    try:
        for client_def in CLIENT_CONFIGS:
            client = client_def["client"]
            scope = client_def["scope"]
            root_key = client_def["root_key"]

            try:
                paths = client_def["paths"]()
            except Exception:
                continue

            for path in paths:
                if path is None:
                    continue
                path = Path(path)

                if not path.exists():
                    continue

                dc = DetectedConfig(
                    client=client,
                    path=path.resolve(),
                    scope=scope,
                    root_key=root_key,
                )

                try:
                    text = path.read_text(encoding="utf-8")
                    data = json.loads(text)
                    servers = _extract_servers(data, root_key, client)
                    dc.server_count = len(servers)
                    dc.servers = servers
                except json.JSONDecodeError as e:
                    dc.error = f"Invalid JSON: {e}"
                except Exception as e:
                    dc.error = str(e)

                found.append(dc)
    finally:
        if project_dir:
            os.chdir(original_cwd)

    return found


def print_detection_report(configs: List[DetectedConfig]) -> None:
    """Print a human-readable detection report."""
    if not configs:
        print("No MCP configurations found.")
        print("\nExpected locations:")
        for cd in CLIENT_CONFIGS:
            try:
                for p in cd["paths"]():
                    if p:
                        print(f"  {cd['client']} ({cd['scope']}): {p}")
            except Exception:
                pass
        return

    print(f"Found {len(configs)} MCP configuration(s):\n")
    total_servers = 0

    for i, dc in enumerate(configs, 1):
        status = "OK" if not dc.error else f"ERROR: {dc.error}"
        print(f"  [{i}] {dc.client} ({dc.scope})")
        print(f"      Path: {dc.path}")
        print(f"      Root key: {dc.root_key}")
        print(f"      Servers: {dc.server_count} — {status}")
        if dc.servers:
            for s in dc.servers:
                print(f"        - {s}")
        total_servers += dc.server_count
        print()

    print(f"Total: {len(configs)} configs, {total_servers} servers across {len(set(dc.client for dc in configs))} clients")
