# Copyright (C) 2026 GoodData Corporation

"""Shared helpers for MCP installation flows."""

from __future__ import annotations

import shutil
import subprocess
import sys
from collections.abc import Iterable, Sequence
from pathlib import Path

DEFAULT_HTTP_PORT = 9910
DEFAULT_HTTP_HOST = "localhost"
DEFAULT_HTTP_PATH = "/mcp"


def log(message: str) -> None:
    print(f"[aida-mcp][install-mcp] {message}")


def err(message: str) -> None:
    print(f"[aida-mcp][install-mcp][error] {message}", file=sys.stderr)


def build_http_url(*, port: int | None = None, host: str = DEFAULT_HTTP_HOST, path: str = DEFAULT_HTTP_PATH) -> str:
    normalized_path = path if path.startswith("/") else f"/{path}"
    effective_port = DEFAULT_HTTP_PORT if port is None else port
    return f"http://{host}:{effective_port}{normalized_path}"


def _dedupe_targets(raw: Iterable[str]) -> list[str]:
    ordered: list[str] = []
    seen: set[str] = set()
    for item in raw:
        if item not in seen:
            ordered.append(item)
            seen.add(item)
    return ordered


def resolve_targets(raw_targets: Sequence[str] | None) -> list[str]:
    """Resolve targets; supports `all` and `jetbrains` expansions."""

    if not raw_targets:
        return []

    expanded: list[str] = []
    for entry in raw_targets:
        if entry == "all":
            expanded.extend(["cursor", "claude", "codex", "jetbrains", "junie"])
        elif entry == "jetbrains":
            expanded.extend(["jetbrains", "junie"])
        else:
            expanded.append(entry)
    return _dedupe_targets(expanded)


def _find_in_path(command: str) -> Path | None:
    result = shutil.which(command)
    return Path(result) if result else None


def _npm_prefix_path() -> Path | None:
    try:
        completed = subprocess.run(
            ["npm", "config", "get", "prefix"],
            check=True,
            capture_output=True,
            text=True,
        )
    except (subprocess.CalledProcessError, FileNotFoundError):
        return None
    value = completed.stdout.strip()
    return Path(value) if value else None


def _newest_nvm_claude(nvm_dir: Path) -> Path | None:
    node_dir = nvm_dir / "versions" / "node"
    if not node_dir.exists():
        return None

    candidates = [path for path in node_dir.iterdir() if path.is_dir()]
    if not candidates:
        return None

    def version_key(path: Path) -> list[tuple[int, object]]:
        key: list[tuple[int, object]] = []
        for part in path.name.split("."):
            try:
                key.append((0, int(part)))
            except ValueError:
                key.append((1, part))
        return key

    candidates.sort(key=version_key)
    candidate = candidates[-1] / "bin" / "claude"
    return candidate if candidate.exists() else None


def _make_server_entry(
    *,
    command: str | None = None,
    args: Sequence[str] = (),
    url: str | None = None,
    workspace_root: str | None,
    config_root: str | None,
    extra_env: dict[str, str] | None = None,
) -> dict[str, object]:
    if url:
        entry: dict[str, object] = {"type": "http", "url": url}
    elif command:
        entry = {"command": command, "args": list(args)}
    else:
        raise ValueError("Either command or url must be provided for MCP server entry.")

    env: dict[str, str] = {}
    if extra_env:
        env.update(extra_env)
    if not url and workspace_root:
        env["AIDA_WORKSPACE_ROOT"] = workspace_root
    if config_root:
        env["AIDA_CONFIG_ROOT"] = config_root
    if env:
        entry["env"] = env
    return entry


def resolve_self_command_path(argv0: str) -> Path:
    """Resolve the currently running `aida-mcp` command path."""

    candidate = Path(argv0).expanduser()
    if not candidate.is_absolute():
        candidate = (Path.cwd() / candidate).resolve()
    if candidate.exists():
        return candidate

    which = _find_in_path("aida-mcp")
    if which:
        return which

    raise RuntimeError("Unable to resolve aida-mcp command path")
