/**
 * `forge mcp install` — add the Forge Management MCP to Claude Code.
 *
 * Mints an API key if one isn't already saved, then runs `claude mcp add` — so a
 * user can just say "add forge management MCP" and the agent runs one command.
 *
 * No admin password: running the `forge` CLI already proves filesystem access to
 * the data dir, so the key is minted directly into <dataDir>/api-keys.json. The
 * running server picks it up on its next request (its key cache self-invalidates
 * on the file's mtime), and the server need not even be up. (A loopback-IP check
 * would be wrong — Forge runs behind nginx, so remote users also arrive over
 * loopback; filesystem access is the only trustworthy local signal.)
 *
 * The store is getDataDir() — FORGE_DATA_DIR, else ~/.forge/data — so set
 * FORGE_DATA_DIR to mint into a non-default instance's data dir.
 *
 * A --remote install can't reach the remote's data dir, so it uses a key you
 * already hold (FORGE_API_KEY, the saved key file, or --http --header).
 *
 *   forge mcp install                 stdio proxy, local server
 *   forge mcp install --http          register the HTTP transport instead
 *   forge mcp install --scope user    available in every project
 *   forge mcp install --remote URL    target a remote Forge (needs FORGE_API_KEY)
 *   forge mcp install --print         print the `claude mcp add` command, don't run it
 */

import { spawnSync } from 'node:child_process';
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { createApiKey } from '../lib/api-keys';

const opt = (args: string[], name: string): string | undefined => {
  const i = args.indexOf(name);
  return i >= 0 ? args[i + 1] : undefined;
};

const keyFile = (): string => join(homedir(), '.forge', 'mcp-key');

/** A key already available from env or the Forge-owned key file, if any. */
function savedKey(): string {
  const env = (process.env.FORGE_API_KEY || process.env.FORGE_API_TOKEN || '').trim();
  if (env) return env;
  try { if (existsSync(keyFile())) return readFileSync(keyFile(), 'utf-8').trim(); } catch { /* ignore */ }
  return '';
}

/** Return a usable key, minting one locally if none is saved.
 *  Local mint writes <dataDir>/api-keys.json (no password, no running server).
 *  Remote installs can't mint here, so a saved key is required. */
function ensureKey(remote: string | undefined): string {
  const existing = savedKey();
  if (existing) return existing;
  if (remote) throw new Error("remote install needs a key — set FORGE_API_KEY (mint one in the remote's Settings → API Key)");

  const { key } = createApiKey('claude-code');
  mkdirSync(join(homedir(), '.forge'), { recursive: true });
  writeFileSync(keyFile(), key, { mode: 0o600 });
  console.error(`[forge mcp install] minted a local API key → ${keyFile()}`);
  return key;
}

export async function mcpInstallCommand(args: string[]): Promise<void> {
  const name = opt(args, '--name') || 'forge';
  const scope = opt(args, '--scope');
  const remote = opt(args, '--remote');
  const useHttp = args.includes('--http');
  const printOnly = args.includes('--print');
  const port = opt(args, '--port');
  const base = (remote || process.env.MW_URL || `http://localhost:${port || '8403'}`).replace(/\/$/, '');

  // A local mint lands in getDataDir() (FORGE_DATA_DIR or ~/.forge/data), which
  // ignores --port/MW_URL. If those target a non-default instance, the key would
  // go to the wrong store and silently 401 — refuse with guidance instead.
  if (!printOnly && !remote && !savedKey() && !process.env.FORGE_DATA_DIR
      && (process.env.MW_URL || (port && port !== '8403'))) {
    throw new Error('targeting a non-default instance — set FORGE_DATA_DIR to its data dir so the key is minted into the store that server reads');
  }

  // --print is a dry run: don't mint anything, just show the command.
  const key = printOnly ? savedKey() : ensureKey(remote);

  const add = ['mcp', 'add'];
  if (scope) add.push('--scope', scope);
  if (useHttp) {
    add.push('--transport', 'http', name, `${base}/api/mcp`);
    if (key) add.push('--header', `Authorization: Bearer ${key}`);
    else if (printOnly) add.push('--header', 'Authorization: Bearer forge_sk_YOUR_KEY');
  } else {
    // The stdio proxy reads ~/.forge/mcp-key itself; only forward MW_URL when non-default.
    if (remote || process.env.MW_URL) add.push('--env', `MW_URL=${base}`);
    add.push(name, '--', 'forge', 'mcp');
  }

  const pretty = `claude ${add.map((a) => (a.includes(' ') ? JSON.stringify(a) : a)).join(' ')}`;
  if (printOnly) { console.log(pretty); return; }

  const r = spawnSync('claude', add, { stdio: 'inherit' });
  if (r.error && (r.error as NodeJS.ErrnoException).code === 'ENOENT') {
    console.error('[forge mcp install] the `claude` CLI is not on PATH. Run this yourself:\n');
    console.log(`  ${pretty}\n`);
    return;
  }
  if (r.status && r.status !== 0) process.exit(r.status);
  console.error(`\n[forge mcp install] added '${name}'. Run /mcp in a Claude Code session to confirm.`);
}
