#!/usr/bin/env node

// index.ts
import { Command } from "commander";
import { spawn } from "child_process";
import fs from "fs";
import { promises as fsp } from "fs";
import os from "os";
import path from "path";
import process from "process";
import { fileURLToPath } from "url";
import { createRequire } from "module";
import { green, yellow } from "colorette";
var program = new Command();
var DEFAULT_PORT = 4100;
var HOME_DIR = path.join(os.homedir(), ".cc-gw");
var PID_FILE = path.join(HOME_DIR, "cc-gw.pid");
var LOG_DIR = path.join(HOME_DIR, "logs");
var LOG_FILE = path.join(LOG_DIR, "cc-gw.log");
var CONFIG_FILE = path.join(HOME_DIR, "config.json");
var require2 = createRequire(import.meta.url);
function resolvePackageVersion() {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const candidates = [
    path.resolve(__dirname, "../../package.json"),
    path.resolve(__dirname, "../package.json"),
    path.resolve(__dirname, "../../../package.json")
  ];
  for (const candidate of candidates) {
    try {
      const pkg = require2(candidate);
      if (pkg && typeof pkg.version === "string") {
        return pkg.version;
      }
    } catch {
    }
  }
  return "0.0.0";
}
var CLI_VERSION = resolvePackageVersion();
function resolveProjectRoot() {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const candidates = [
    path.resolve(__dirname, "../../.."),
    path.resolve(__dirname, "../.."),
    process.cwd()
  ];
  for (const candidate of candidates) {
    if (fs.existsSync(path.join(candidate, "package.json"))) {
      return candidate;
    }
  }
  return candidates[0];
}
function resolveExecutableName() {
  return process.platform === "win32" ? "cc-gw-server.exe" : "cc-gw-server";
}
function resolveNativePackageBinary() {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const executable = resolveExecutableName();
  const packageMap = {
    "darwin-arm64": "@chenpu17/cc-gw-darwin-arm64",
    "linux-x64": "@chenpu17/cc-gw-linux-x64",
    "linux-arm64": "@chenpu17/cc-gw-linux-arm64",
    "win32-x64": "@chenpu17/cc-gw-win32-x64"
  };
  const packageName = packageMap[`${process.platform}-${process.arch}`];
  if (!packageName) {
    return null;
  }
  try {
    const packageJsonPath = require2.resolve(`${packageName}/package.json`, {
      paths: [__dirname]
    });
    const packageDir = path.dirname(packageJsonPath);
    const candidate = path.join(packageDir, "bin", executable);
    if (fs.existsSync(candidate)) {
      return candidate;
    }
  } catch {
  }
  return null;
}
function resolveBundledBinary() {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const executable = resolveExecutableName();
  const targetDir = `${process.platform}-${process.arch}`;
  const candidates = [
    path.resolve(__dirname, `../../../bin/${targetDir}/${executable}`),
    path.resolve(__dirname, `../../../../bin/${targetDir}/${executable}`)
  ];
  for (const candidate of candidates) {
    if (fs.existsSync(candidate)) {
      return candidate;
    }
  }
  return null;
}
function resolveServerCommand() {
  const envOverride = process.env.CC_GW_SERVER_BIN;
  if (envOverride && fs.existsSync(envOverride)) {
    return { command: envOverride, args: [] };
  }
  const packaged = resolveNativePackageBinary();
  if (packaged) {
    return { command: packaged, args: [] };
  }
  const bundled = resolveBundledBinary();
  if (bundled) {
    return { command: bundled, args: [] };
  }
  const projectRoot = resolveProjectRoot();
  const executable = resolveExecutableName();
  const candidates = [
    path.join(projectRoot, "target", "release", executable),
    path.join(projectRoot, "target", "debug", executable)
  ];
  for (const candidate of candidates) {
    if (fs.existsSync(candidate)) {
      return { command: candidate, args: [] };
    }
  }
  return {
    command: "cargo",
    args: ["run", "-p", "cc-gw-server", "--"],
    cwd: projectRoot
  };
}
function resolveWebDist() {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const hasIndex = (dir) => fs.existsSync(path.join(dir, "index.html"));
  const candidates = [
    path.resolve(__dirname, "../web/dist"),
    path.resolve(__dirname, "../../web/dist"),
    path.resolve(__dirname, "../../../src/web/dist"),
    path.resolve(process.cwd(), "src/web/dist")
  ];
  for (const candidate of candidates) {
    if (hasIndex(candidate)) {
      return candidate;
    }
  }
  return null;
}
async function ensureHomeDir() {
  await fsp.mkdir(LOG_DIR, { recursive: true });
}
async function ensureConfigTemplate(port) {
  try {
    await fsp.access(CONFIG_FILE);
    return false;
  } catch {
    const selectedPort = port ? Number.parseInt(port, 10) || DEFAULT_PORT : DEFAULT_PORT;
    const baseDefaults = {
      completion: null,
      reasoning: null,
      background: null,
      longContextThreshold: 6e4
    };
    const template = {
      http: {
        enabled: true,
        port: selectedPort,
        host: "127.0.0.1"
      },
      https: {
        enabled: false,
        port: 4443,
        host: "127.0.0.1",
        keyPath: "",
        certPath: "",
        caPath: ""
      },
      host: "127.0.0.1",
      port: selectedPort,
      providers: [],
      defaults: { ...baseDefaults },
      storeRequestPayloads: true,
      storeResponsePayloads: true,
      enableRoutingFallback: false,
      endpointRouting: {
        anthropic: {
          defaults: { ...baseDefaults },
          modelRoutes: {}
        },
        openai: {
          defaults: { ...baseDefaults },
          modelRoutes: {}
        }
      },
      logRetentionDays: 30,
      modelRoutes: {},
      logLevel: "info",
      bodyLimit: 10 * 1024 * 1024,
      webAuth: {
        enabled: false,
        username: "",
        passwordHash: "",
        passwordSalt: ""
      }
    };
    await fsp.mkdir(path.dirname(CONFIG_FILE), { recursive: true });
    await fsp.writeFile(CONFIG_FILE, JSON.stringify(template, null, 2), "utf-8");
    return true;
  }
}
async function readConfiguredPort() {
  try {
    const raw = await fsp.readFile(CONFIG_FILE, "utf-8");
    const parsed = JSON.parse(raw);
    if (parsed && typeof parsed.port === "number" && Number.isFinite(parsed.port)) {
      return parsed.port;
    }
  } catch {
  }
  return null;
}
async function readPid() {
  try {
    const raw = await fsp.readFile(PID_FILE, "utf-8");
    const pid = Number.parseInt(raw.trim(), 10);
    return Number.isNaN(pid) ? null : pid;
  } catch {
    return null;
  }
}
async function writePid(pid) {
  if (pid == null) return;
  await fsp.writeFile(PID_FILE, String(pid), "utf-8");
}
async function removePid() {
  try {
    await fsp.unlink(PID_FILE);
  } catch {
  }
}
function isProcessAlive(pid) {
  try {
    process.kill(pid, 0);
    return true;
  } catch {
    return false;
  }
}
async function isServiceRunning() {
  const pid = await readPid();
  if (!pid) return { running: false };
  if (isProcessAlive(pid)) {
    return { running: true, pid };
  }
  await removePid();
  return { running: false };
}
async function handleStart(options) {
  await ensureHomeDir();
  const { running, pid } = await isServiceRunning();
  if (running && pid) {
    console.log(yellow(`cc-gw \u5DF2\u5728\u8FD0\u884C (pid: ${pid})`));
    return;
  }
  const configCreated = await ensureConfigTemplate(options.port);
  const server = resolveServerCommand();
  const env = { ...process.env };
  if (options.port) {
    env.PORT = options.port;
  }
  if (!env.CC_GW_UI_ROOT) {
    const uiRoot = resolveWebDist();
    if (uiRoot) {
      env.CC_GW_UI_ROOT = uiRoot;
    }
  }
  const daemonMode = options.foreground ? false : options.daemon !== false;
  const spawnOptions = {
    cwd: server.cwd,
    env,
    detached: daemonMode,
    stdio: daemonMode ? [
      "ignore",
      fs.openSync(LOG_FILE, "a"),
      fs.openSync(LOG_FILE, "a")
    ] : "inherit"
  };
  const child = spawn(server.command, server.args, spawnOptions);
  child.on("error", (err) => {
    console.error("\u542F\u52A8 cc-gw \u5931\u8D25:", err);
  });
  await writePid(child.pid);
  if (daemonMode) {
    child.unref();
    console.log(green(`cc-gw \u5DF2\u4EE5\u5B88\u62A4\u8FDB\u7A0B\u65B9\u5F0F\u542F\u52A8 (pid: ${child.pid})`));
  }
  let effectivePort;
  if (options.port) {
    const parsed = Number.parseInt(options.port, 10);
    effectivePort = Number.isFinite(parsed) ? parsed : DEFAULT_PORT;
  } else {
    const configured = await readConfiguredPort();
    effectivePort = configured ?? DEFAULT_PORT;
  }
  if (configCreated) {
    console.log(green(`\u5DF2\u5728 ${CONFIG_FILE} \u751F\u6210\u9ED8\u8BA4\u914D\u7F6E`));
    console.log(yellow("\u9996\u6B21\u542F\u52A8\u540E\uFF0C\u8BF7\u8BBF\u95EE Web UI \u7EE7\u7EED\u914D\u7F6E provider\u3002"));
  }
  console.log(green(`\u670D\u52A1\u5730\u5740: http://127.0.0.1:${effectivePort}/ui`));
  if (!daemonMode) {
    const forwardSignal = (signal) => {
      if (!child.killed) {
        try {
          child.kill(signal);
        } catch {
        }
      }
    };
    process.on("SIGINT", forwardSignal);
    process.on("SIGTERM", forwardSignal);
    await new Promise((resolve) => {
      child.on("exit", () => resolve());
      child.on("close", () => resolve());
    });
    process.off("SIGINT", forwardSignal);
    process.off("SIGTERM", forwardSignal);
    await removePid();
    if (child.exitCode && child.exitCode !== 0) {
      process.exitCode = child.exitCode;
    }
  }
}
async function handleStop() {
  const pid = await readPid();
  if (!pid) {
    console.log(yellow("cc-gw \u672A\u5728\u8FD0\u884C\u3002"));
    return;
  }
  if (!isProcessAlive(pid)) {
    console.log(yellow("\u68C0\u6D4B\u5230\u9648\u65E7\u7684 PID \u6587\u4EF6\uFF0C\u5DF2\u6E05\u7406\u3002"));
    await removePid();
    return;
  }
  try {
    process.kill(pid, "SIGTERM");
    console.log(green(`\u5DF2\u5411\u8FDB\u7A0B ${pid} \u53D1\u9001 SIGTERM`));
  } catch (err) {
    console.error(`\u505C\u6B62 cc-gw \u5931\u8D25: ${err.message}`);
  } finally {
    await removePid();
  }
}
async function handleStatus() {
  const { running, pid } = await isServiceRunning();
  if (running && pid) {
    console.log(green(`cc-gw \u6B63\u5728\u8FD0\u884C (pid: ${pid})`));
  } else {
    console.log(yellow("cc-gw \u672A\u5728\u8FD0\u884C\u3002"));
  }
  console.log(`PID \u6587\u4EF6: ${PID_FILE}`);
  console.log(`\u65E5\u5FD7\u76EE\u5F55: ${LOG_DIR}`);
  console.log(`\u914D\u7F6E\u6587\u4EF6: ${CONFIG_FILE}`);
}
program.name("cc-gw").description("Rust-backed Claude Code Gateway CLI").version(CLI_VERSION);
program.command("version").description("\u663E\u793A\u5F53\u524D cc-gw CLI \u7248\u672C").action(() => {
  console.log(`cc-gw CLI \u7248\u672C: ${CLI_VERSION}`);
});
program.command("start").description("\u542F\u52A8 cc-gw \u670D\u52A1").option("--daemon", "\u4EE5\u5B88\u62A4\u8FDB\u7A0B\u65B9\u5F0F\u8FD0\u884C\uFF08\u9ED8\u8BA4\uFF09").option("--foreground", "\u4EE5\u524D\u53F0\u6A21\u5F0F\u8FD0\u884C\u5E76\u4FDD\u6301\u63A7\u5236\u53F0\u8F93\u51FA").option("--port <port>", "\u6307\u5B9A\u670D\u52A1\u76D1\u542C\u7AEF\u53E3").action(async (options) => {
  try {
    await handleStart(options);
  } catch (err) {
    console.error(err.message);
    process.exitCode = 1;
  }
});
program.command("stop").description("\u505C\u6B62 cc-gw \u670D\u52A1").action(async () => {
  await handleStop();
});
program.command("restart").description("\u91CD\u542F cc-gw \u670D\u52A1").option("--daemon", "\u4EE5\u5B88\u62A4\u8FDB\u7A0B\u65B9\u5F0F\u8FD0\u884C").option("--port <port>", "\u6307\u5B9A\u670D\u52A1\u76D1\u542C\u7AEF\u53E3").action(async (options) => {
  await handleStop();
  await handleStart(options);
});
program.command("status").description("\u67E5\u770B cc-gw \u8FD0\u884C\u72B6\u6001").action(async () => {
  await handleStatus();
});
program.parseAsync(process.argv);
