#!/usr/bin/env node

/**
 * godot-mcp-setup — Setup and management CLI for Godot MCP Pro
 *
 * Commands:
 *   install        Install dependencies and build the server
 *   check-update   Check if a newer version is available on GitHub
 *   configure      Auto-detect AI client and generate MCP config
 *   doctor         Diagnose environment (Node.js, npm, build status)
 */

import { execSync } from "child_process";
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
import { resolve, dirname, join } from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Server root is one level up from build/
const SERVER_DIR = resolve(__dirname, "..");
const PACKAGE_JSON = join(SERVER_DIR, "package.json");
const BUILD_INDEX = join(SERVER_DIR, "build", "index.js");
const GITHUB_REPO = "youichi-uda/godot-mcp-pro";

// ─── Utilities ────────────────────────────────────────────────

function getVersion(): string {
  try {
    const pkg = JSON.parse(readFileSync(PACKAGE_JSON, "utf-8"));
    return pkg.version || "unknown";
  } catch {
    return "unknown";
  }
}

function run(cmd: string, cwd?: string): string {
  try {
    return execSync(cmd, {
      cwd: cwd || SERVER_DIR,
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"],
    }).trim();
  } catch (err: any) {
    return err.stderr?.trim() || err.message || "command failed";
  }
}

function check(label: string, ok: boolean, detail?: string): void {
  const icon = ok ? "✓" : "✗";
  const line = detail ? `${label}: ${detail}` : label;
  console.log(`  ${icon} ${line}`);
}

/** Compare semver strings. Returns >0 if a > b, <0 if a < b, 0 if equal. */
function compareSemver(a: string, b: string): number {
  const pa = a.split(".").map(Number);
  const pb = b.split(".").map(Number);
  for (let i = 0; i < 3; i++) {
    const diff = (pa[i] || 0) - (pb[i] || 0);
    if (diff !== 0) return diff;
  }
  return 0;
}

// ─── Commands ─────────────────────────────────────────────────

async function cmdInstall(): Promise<void> {
  console.log("Installing Godot MCP Pro server...\n");

  console.log("[1/2] Installing dependencies...");
  try {
    execSync("npm install", { cwd: SERVER_DIR, stdio: "inherit" });
  } catch {
    console.error("\nFailed to install dependencies. Make sure npm is available.");
    process.exit(1);
  }

  console.log("\n[2/2] Building server...");
  try {
    execSync("npm run build", { cwd: SERVER_DIR, stdio: "inherit" });
  } catch {
    console.error("\nBuild failed. Check for TypeScript errors above.");
    process.exit(1);
  }

  console.log(`\nDone! Server built at: ${BUILD_INDEX}`);
  console.log(`Version: ${getVersion()}`);
  console.log("\nNext step: Run 'node build/setup.js configure' to set up your AI client.");
}

async function cmdCheckUpdate(): Promise<void> {
  const current = getVersion();
  console.log(`Current version: ${current}\n`);
  console.log(`Checking GitHub releases for ${GITHUB_REPO}...`);

  try {
    const res = await fetch(
      `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`,
      { headers: { "User-Agent": "godot-mcp-pro-setup" } }
    );

    if (!res.ok) {
      if (res.status === 404) {
        console.log("No releases found on GitHub.");
        return;
      }
      console.error(`GitHub API error: ${res.status} ${res.statusText}`);
      return;
    }

    const data = (await res.json()) as { tag_name: string; html_url: string };
    const latest = data.tag_name.replace(/^v/, "");

    if (compareSemver(latest, current) > 0) {
      console.log(`\nUpdate available: v${latest} (current: v${current})`);
      console.log(`Download: ${data.html_url}`);
      console.log(
        "\nTo update: download the new version, replace server/src/, and run 'node build/setup.js install'"
      );
    } else {
      console.log(`\nUp to date! (${current})`);
    }
  } catch (err) {
    console.error(`Failed to check for updates: ${(err as Error).message}`);
  }
}

interface McpConfig {
  mcpServers: Record<string, {
    command: string;
    args: string[];
    env?: Record<string, string>;
  }>;
}

interface ClientInfo {
  name: string;
  configPath: string;
  configKey: string;
}

async function cmdConfigure(): Promise<void> {
  const serverPath = resolve(BUILD_INDEX).replace(/\\/g, "/");

  if (!existsSync(BUILD_INDEX)) {
    console.error(
      "Server not built yet. Run 'node build/setup.js install' first."
    );
    process.exit(1);
  }

  console.log("Detecting AI clients...\n");

  // Detect available clients by checking config file locations
  const home = process.env.HOME || process.env.USERPROFILE || "";
  const cwd = process.cwd();

  const candidates: ClientInfo[] = [
    {
      name: "Claude Code (project)",
      configPath: join(cwd, ".mcp.json"),
      configKey: "godot-mcp-pro",
    },
    {
      name: "Cursor (project)",
      configPath: join(cwd, ".cursor", "mcp.json"),
      configKey: "godot-mcp-pro",
    },
    {
      name: "Windsurf (project)",
      configPath: join(cwd, ".windsurf", "mcp.json"),
      configKey: "godot-mcp-pro",
    },
    {
      name: "Claude Desktop",
      configPath: join(
        home,
        process.platform === "win32"
          ? "AppData/Roaming/Claude/claude_desktop_config.json"
          : process.platform === "darwin"
            ? "Library/Application Support/Claude/claude_desktop_config.json"
            : ".config/claude/claude_desktop_config.json"
      ),
      configKey: "godot-mcp-pro",
    },
  ];

  // Find existing configs
  const existing = candidates.filter((c) => existsSync(c.configPath));
  const missing = candidates.filter((c) => !existsSync(c.configPath));

  if (existing.length > 0) {
    console.log("Found existing configs:");
    for (const c of existing) {
      console.log(`  ✓ ${c.name}: ${c.configPath}`);
    }
  }

  // Default: create .mcp.json in cwd (Claude Code)
  const target = candidates[0]; // Claude Code project-level
  // No GODOT_MCP_PORT env: lets the server auto-scan 6505-6509 so multiple
  // Claude Code sessions can each grab a free port. Pinning a single port
  // here would force every session to collide on 6505.
  const entry = {
    command: "node",
    args: [serverPath],
  };

  let config: McpConfig;
  if (existsSync(target.configPath)) {
    try {
      config = JSON.parse(readFileSync(target.configPath, "utf-8"));
      if (!config.mcpServers) config.mcpServers = {};
    } catch {
      config = { mcpServers: {} };
    }
  } else {
    config = { mcpServers: {} };
  }

  if (config.mcpServers[target.configKey]) {
    console.log(
      `\n${target.name} already configured in ${target.configPath}`
    );
    console.log("Updating server path...");
  }

  config.mcpServers[target.configKey] = entry;

  const dir = dirname(target.configPath);
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });

  writeFileSync(target.configPath, JSON.stringify(config, null, 2) + "\n");
  console.log(`\nWrote config to: ${target.configPath}`);
  console.log(`Server path: ${serverPath}`);
  console.log("\nYou're all set! Start your AI assistant to begin using Godot MCP Pro.");
}

function cmdDoctor(): void {
  console.log("Godot MCP Pro — Environment Check\n");

  // Node.js
  const nodeVer = run("node --version");
  const nodeOk = nodeVer.startsWith("v") && parseInt(nodeVer.slice(1)) >= 18;
  check("Node.js", nodeOk, nodeVer);

  // npm
  const npmVer = run("npm --version");
  const npmOk = !npmVer.includes("not found") && !npmVer.includes("failed");
  check("npm", npmOk, npmVer);

  // Dependencies installed
  const nodeModules = existsSync(join(SERVER_DIR, "node_modules"));
  check("Dependencies installed", nodeModules);

  // Server built
  const built = existsSync(BUILD_INDEX);
  check("Server built", built, built ? BUILD_INDEX : "run 'node build/setup.js install'");

  // Version
  console.log(`\n  Version: ${getVersion()}`);

  // Overall
  const allOk = nodeOk && npmOk && nodeModules && built;
  console.log(allOk ? "\nAll good!" : "\nSome issues found. Fix them above.");
  if (!allOk) process.exit(1);
}

// ─── Main ─────────────────────────────────────────────────────

function showHelp(): void {
  console.log(`godot-mcp-setup — Setup and management for Godot MCP Pro

Usage: node build/setup.js <command>

Commands:
  install        Install dependencies and build the server
  check-update   Check if a newer version is available on GitHub
  configure      Auto-detect AI client and generate .mcp.json config
  doctor         Check Node.js, npm, and build status

Options:
  --help         Show this help
  --version      Show current version

Examples:
  node build/setup.js install
  node build/setup.js doctor
  node build/setup.js configure
  node build/setup.js check-update`);
}

async function main() {
  const args = process.argv.slice(2);
  const cmd = args[0];

  if (!cmd || cmd === "--help" || cmd === "-h") {
    showHelp();
    process.exit(0);
  }

  if (cmd === "--version" || cmd === "-v") {
    console.log(getVersion());
    process.exit(0);
  }

  switch (cmd) {
    case "install":
      await cmdInstall();
      break;
    case "check-update":
      await cmdCheckUpdate();
      break;
    case "configure":
      await cmdConfigure();
      break;
    case "doctor":
      cmdDoctor();
      break;
    default:
      console.error(`Unknown command: ${cmd}`);
      showHelp();
      process.exit(1);
  }
}

main().catch((err) => {
  console.error("Fatal:", err.message);
  process.exit(1);
});
