"""
Supreme 2 MAX — MCP Client Auto-Setup

Generates MCP client configuration files so that AI assistants
(Claude Desktop, VS Code Cline, Cursor, VS Code Copilot) can discover
the Supreme 2 MAX MCP server automatically.

CLI entry-point
~~~~~~~~~~~~~~~
``supreme2l-mcp setup --client <name>``

Supported clients: ``claude-desktop``, ``cline``, ``cursor``, ``copilot``.
"""

from __future__ import annotations

import json
import os
import platform
import shutil
import sys
from pathlib import Path
from typing import Any, Dict, Optional


# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------

_APP_NAME = "supreme_max"

# Try to locate the Python that is running this process — this is what
# the MCP server should be invoked with.
_PYTHON_PATH = sys.executable

# The MCP server module entry-point.
_SERVER_MODULE = "supreme2l.mcp_server"


def _detect_python_path() -> str:
    """Return the absolute path to the current Python interpreter."""
    return str(Path(_PYTHON_PATH).resolve())


def _server_command() -> list[str]:
    """Build the command list to start the MCP server."""
    return [_detect_python_path(), "-m", _SERVER_MODULE]


def _server_command_str() -> str:
    """Build a single shell command string."""
    return " ".join(_server_command())


# ---------------------------------------------------------------------------
# Platform helpers
# ---------------------------------------------------------------------------

def _is_windows() -> bool:
    return platform.system() == "Windows"


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


def _ensure_parent(path: Path) -> Path:
    """Create parent directories if they don't exist."""
    path.parent.mkdir(parents=True, exist_ok=True)
    return path


def detect_cursor_installation() -> Optional[Path]:
    """Best-effort detection of Cursor IDE installation."""
    candidates: list[Path] = []
    system = platform.system()

    if system == "Windows":
        local_appdata = Path(os.getenv("LOCALAPPDATA", ""))
        program_files = Path(os.getenv("ProgramFiles", ""))
        program_files_x86 = Path(os.getenv("ProgramFiles(x86)", ""))
        candidates.extend(
            [
                local_appdata / "Programs" / "Cursor" / "Cursor.exe",
                program_files / "Cursor" / "Cursor.exe",
                program_files_x86 / "Cursor" / "Cursor.exe",
            ]
        )
    elif system == "Darwin":
        candidates.append(Path("/Applications/Cursor.app"))
    else:
        candidates.extend(
            [
                Path("/usr/bin/cursor"),
                Path("/usr/local/bin/cursor"),
                Path("/opt/cursor/cursor"),
            ]
        )

    for path in candidates:
        if path and path.exists():
            return path

    # Fall back to PATH detection.
    cursor_exe = shutil.which("cursor")
    if cursor_exe:
        return Path(cursor_exe)

    return None


def _write_json(path: Path, data: Dict[str, Any]) -> Path:
    """Write *data* as pretty-printed JSON, creating parent dirs."""
    _ensure_parent(path)
    path.write_text(
        json.dumps(data, indent=2, ensure_ascii=False) + "\n",
        encoding="utf-8",
    )
    return path


def _merge_mcp_config(
    path: Path,
    server_key: str,
    server_config: Dict[str, Any],
) -> Path:
    """Merge *server_config* into an existing MCP config file.

    If the file already exists, only the ``mcpServers.<server_key>`` key
    is added or updated — other servers are left untouched.
    """
    existing: Dict[str, Any] = {}
    if path.is_file():
        try:
            existing = json.loads(path.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, OSError):
            # Corrupt file — back up and start fresh.
            backup = path.with_suffix(".bak")
            shutil.copy2(path, backup)

    servers = existing.setdefault("mcpServers", {})
    servers[server_key] = server_config

    return _write_json(path, existing)


# ---------------------------------------------------------------------------
# Server config snippets
# ---------------------------------------------------------------------------

def _stdio_server_config() -> Dict[str, Any]:
    """Standard stdio transport configuration."""
    return {
        "command": _detect_python_path(),
        "args": ["-m", _SERVER_MODULE],
        "env": {},
    }


# ---------------------------------------------------------------------------
# Claude Desktop
# ---------------------------------------------------------------------------

def _claude_desktop_config_path() -> Path:
    """Platform-specific Claude Desktop config location."""
    if _is_windows():
        appdata = Path.home() / "AppData" / "Roaming"
        return appdata / "Claude" / "claude_desktop_config.json"
    elif platform.system() == "Darwin":
        return (
            Path.home()
            / "Library"
            / "Application Support"
            / "Claude"
            / "claude_desktop_config.json"
        )
    else:
        # Linux / XDG
        xdg = Path.home() / ".config"
        return xdg / "Claude" / "claude_desktop_config.json"


def setup_claude_desktop() -> Dict[str, Any]:
    """Configure Claude Desktop to use the Supreme 2 MAX MCP server.

    Returns
    -------
    dict
        Status dict with ``config_path`` and ``message``.
    """
    config_path = _claude_desktop_config_path()
    server_cfg = _stdio_server_config()

    _merge_mcp_config(config_path, _APP_NAME, server_cfg)

    return {
        "client": "claude-desktop",
        "config_path": str(config_path),
        "message": (
            f"Claude Desktop configured successfully. "
            f"Config written to {config_path}. "
            f"Restart Claude Desktop to activate."
        ),
    }


# ---------------------------------------------------------------------------
# VS Code Cline
# ---------------------------------------------------------------------------

def _cline_config_path() -> Path:
    """Cline stores MCP settings in VS Code's global settings dir."""
    if _is_windows():
        base = Path.home() / "AppData" / "Roaming" / "Code" / "User"
    elif platform.system() == "Darwin":
        base = (
            Path.home()
            / "Library"
            / "Application Support"
            / "Code"
            / "User"
        )
    else:
        base = Path.home() / ".config" / "Code" / "User"

    return base / "globalStorage" / "saoudrizwan.claude-dev" / "settings" / "cline_mcp_settings.json"


def setup_cline() -> Dict[str, Any]:
    """Configure VS Code Cline extension for Supreme 2 MAX.

    Returns
    -------
    dict
        Status dict with ``config_path`` and ``message``.
    """
    config_path = _cline_config_path()
    server_cfg = _stdio_server_config()

    _merge_mcp_config(config_path, _APP_NAME, server_cfg)

    return {
        "client": "cline",
        "config_path": str(config_path),
        "message": (
            f"VS Code Cline configured successfully. "
            f"Config written to {config_path}. "
            f"Reload VS Code to activate."
        ),
    }


# ---------------------------------------------------------------------------
# VS Code Copilot
# ---------------------------------------------------------------------------

def _copilot_config_path() -> Path:
    """Copilot MCP settings in the workspace .vscode directory."""
    # Workspace-level config — placed in the current working directory.
    return Path.cwd() / ".vscode" / "mcp.json"


def setup_copilot() -> Dict[str, Any]:
    """Configure VS Code Copilot for Supreme 2 MAX.

    Creates a workspace-level ``.vscode/mcp.json``.

    Returns
    -------
    dict
        Status dict with ``config_path`` and ``message``.
    """
    config_path = _copilot_config_path()

    config_data: Dict[str, Any] = {}
    if config_path.is_file():
        try:
            config_data = json.loads(config_path.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, OSError):
            backup = config_path.with_suffix(".bak")
            shutil.copy2(config_path, backup)

    servers = config_data.setdefault("servers", {})
    servers[_APP_NAME] = {
        "type": "stdio",
        "command": _detect_python_path(),
        "args": ["-m", _SERVER_MODULE],
    }

    _write_json(config_path, config_data)

    return {
        "client": "copilot",
        "config_path": str(config_path),
        "message": (
            f"VS Code Copilot configured successfully. "
            f"Config written to {config_path}. "
            f"Reload VS Code to activate."
        ),
    }


# ---------------------------------------------------------------------------
# Cursor
# ---------------------------------------------------------------------------

def setup_cursor() -> Dict[str, Any]:
    """Auto-configure Cursor IDE integration."""
    cursor_path = detect_cursor_installation()
    if not cursor_path:
        return {
            "client": "cursor",
            "config_path": "",
            "success": False,
            "message": "Cursor IDE not found. Please install from https://cursor.sh",
        }

    workspace = Path.cwd()
    cursor_config_dir = workspace / ".cursor"
    cursor_config_dir.mkdir(exist_ok=True)

    template = Path(__file__).parent / "templates" / "cursor_mcp.json"
    target = cursor_config_dir / "mcp.json"

    # Use forward slashes for JSON compatibility (valid on Windows too)
    workspace_str = str(workspace).replace("\\", "/")
    config = template.read_text(encoding="utf-8").replace(
        "${workspaceFolder}", workspace_str
    )
    target.write_text(config, encoding="utf-8")

    return {
        "client": "cursor",
        "config_path": str(target),
        "success": True,
        "message": (
            f"Cursor configuration created: {target}. "
            "Restart Cursor to apply changes."
        ),
    }


# ---------------------------------------------------------------------------
# Dispatch
# ---------------------------------------------------------------------------

_CLIENT_MAP = {
    "claude-desktop": setup_claude_desktop,
    "cline": setup_cline,
    "copilot": setup_copilot,
    "cursor": setup_cursor,
}

SUPPORTED_CLIENTS = list(_CLIENT_MAP.keys())


def setup_mcp_client(client_name: str) -> Dict[str, Any]:
    """Configure an MCP client by name.

    Parameters
    ----------
    client_name:
        One of ``claude-desktop``, ``cline``, ``copilot``, ``cursor``.

    Returns
    -------
    dict
        Status dict with ``client``, ``config_path``, and ``message``.

    Raises
    ------
    ValueError
        If *client_name* is not supported.
    """
    normalised = client_name.lower().strip()
    handler = _CLIENT_MAP.get(normalised)

    if handler is None:
        supported = ", ".join(sorted(SUPPORTED_CLIENTS))
        raise ValueError(
            f"Unsupported MCP client: '{client_name}'. "
            f"Supported clients: {supported}"
        )

    return handler()


# ---------------------------------------------------------------------------
# CLI entry-point  (Click)
# ---------------------------------------------------------------------------

def register_cli(parent_group: Any) -> None:
    """Register the ``mcp`` sub-group on *parent_group*.

    Usage::

        # In cli.py
        from supreme_max.mcp_server.setup import register_cli
        register_cli(main)

    This adds ``sm mcp setup --client <name>``.
    """
    import click

    @parent_group.group("mcp")
    def mcp_group() -> None:
        """MCP server management commands."""

    @mcp_group.command("setup")
    @click.option(
        "--client",
        required=False,
        type=click.Choice(SUPPORTED_CLIENTS, case_sensitive=False),
        help="MCP client to configure.",
    )
    @click.option(
        "--ide",
        required=False,
        type=click.Choice(["cursor"], case_sensitive=False),
        help="IDE to configure (alias for --client).",
    )
    def mcp_setup(client: Optional[str], ide: Optional[str]) -> None:
        """Auto-configure an MCP client for Supreme 2 MAX."""
        if client and ide:
            raise click.UsageError("Use either --client or --ide, not both.")
        target = client or ide
        if not target:
            raise click.UsageError("Specify --client or --ide.")

        try:
            result = setup_mcp_client(target)
            click.echo(result["message"])
        except Exception as exc:  # noqa: BLE001
            click.echo(f"Error: {exc}", err=True)
            raise SystemExit(1) from exc
