"""Utilities for launching g-gremlin Dynamics MCP in stdio mode."""

from __future__ import annotations

from functools import lru_cache
import os
import shutil
import subprocess
import sys
from pathlib import Path

from packaging.version import InvalidVersion, Version

from g_gremlin_dynamics_mcp import MIN_GREMLIN_VERSION


@lru_cache(maxsize=1)
def _find_gremlin() -> str:
    """Locate g-gremlin, preferring the current Python environment."""
    exe_dir = Path(sys.executable).parent
    for candidate in ("g-gremlin", "g-gremlin.exe"):
        full = exe_dir / candidate
        if full.is_file():
            return str(full)

    path = shutil.which("g-gremlin")
    if path:
        return path

    raise RuntimeError("g-gremlin not found. Install with: pipx install g-gremlin")


def clear_gremlin_path_cache() -> None:
    """Clear cached g-gremlin executable lookup (primarily for tests)."""
    _find_gremlin.cache_clear()


def check_gremlin_version() -> str:
    """Ensure g-gremlin meets the minimum required version."""
    gremlin = _find_gremlin()
    try:
        result = subprocess.run(
            [gremlin, "--version"],
            check=False,
            capture_output=True,
            text=True,
            timeout=10,
        )
    except Exception as exc:
        raise RuntimeError(f"Failed to run g-gremlin --version: {exc}") from exc

    if result.returncode != 0:
        stderr = (result.stderr or "").strip()
        raise RuntimeError(f"g-gremlin --version failed (exit {result.returncode}): {stderr}")

    version_str = (result.stdout or "").strip()
    parts = version_str.split()
    version_str = parts[-1] if parts else version_str

    try:
        detected = Version(version_str)
        required = Version(MIN_GREMLIN_VERSION)
    except InvalidVersion as exc:
        raise RuntimeError(f"Could not parse g-gremlin version '{version_str}': {exc}") from exc

    if detected < required:
        raise RuntimeError(
            f"g-gremlin {detected} found, but >={MIN_GREMLIN_VERSION} required. Run: pipx upgrade g-gremlin"
        )

    return str(detected)


def build_command(*, enable_writes: bool = False, profile: str | None = None) -> list[str]:
    """Build the g-gremlin Dynamics MCP serve command."""
    cmd = ["mcp", "serve-dynamics"]
    if profile:
        cmd.extend(["--profile", str(profile)])
    if enable_writes:
        cmd.append("--enable-writes")
    return cmd


def launch_gremlin_mcp(*, enable_writes: bool = False, profile: str | None = None) -> int:
    """
    Launch g-gremlin mcp serve-dynamics.

    On Unix-like platforms this replaces the current process via ``os.execv``.
    On Windows this runs as a child process with inherited stdio and returns
    the child exit code.
    """
    gremlin = _find_gremlin()
    args = [gremlin, *build_command(enable_writes=enable_writes, profile=profile)]

    if os.name == "nt":
        completed = subprocess.run(args, check=False)
        return int(completed.returncode)

    os.execv(gremlin, args)
    return 0
