#!/usr/bin/env node

import { spawn, execSync } from "child_process";
import { existsSync, readFileSync, writeFileSync, unlinkSync, openSync, appendFileSync } from "fs";
import { resolve } from "path";
import { platform } from "os";
import { createServer } from "net";
import { PROJECT_ROOT, DATA_DIR, PID_FILE, LOG_FILE, PORT, DASHBOARD_URL } from "./paths.js";

// ─── Helpers ────────────────────────────────────────────────────────────

const IS_WIN = platform() === "win32";
const GREEN = "\x1b[32m";
const YELLOW = "\x1b[33m";
const RED = "\x1b[31m";
const CYAN = "\x1b[36m";
const DIM = "\x1b[2m";
const BOLD = "\x1b[1m";
const RESET = "\x1b[0m";

function log(msg) { console.log(`${DIM}[arouter]${RESET} ${msg}`); }
function success(msg) { console.log(`${GREEN}✔${RESET} ${msg}`); }
function warn(msg) { console.log(`${YELLOW}⚠${RESET} ${msg}`); }
function error(msg) { console.error(`${RED}✖${RESET} ${msg}`); }

/** Cross-platform port check using Node.js net module */
function isPortInUse(port) {
  return new Promise((resolve) => {
    const server = createServer();
    server.once("error", (err) => {
      if (err.code === "EADDRINUSE") resolve(true);
      else resolve(false);
    });
    server.once("listening", () => {
      server.close(() => resolve(false));
    });
    server.listen(port, "0.0.0.0");
  });
}

function getRunningPid() {
  if (!existsSync(PID_FILE)) return null;
  const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10);
  if (isNaN(pid)) return null;
  try {
    process.kill(pid, 0); // check if alive (works on all platforms)
    return pid;
  } catch {
    // stale PID file
    try { unlinkSync(PID_FILE); } catch { /* ignore */ }
    return null;
  }
}

function waitForServer(timeoutMs = 60000) {
  return new Promise((resolve, reject) => {
    const start = Date.now();
    const check = () => {
      if (Date.now() - start > timeoutMs) {
        return reject(new Error("Server startup timed out"));
      }
      import("http").then(({ default: http }) => {
        const req = http.get(`http://localhost:${PORT}`, (res) => {
          res.resume();
          resolve();
        });
        req.on("error", () => setTimeout(check, 500));
        req.setTimeout(2000, () => { req.destroy(); setTimeout(check, 500); });
      });
    };
    check();
  });
}

/** Cross-platform: get npm executable name */
function npmCommand() {
  return IS_WIN ? "npm.cmd" : "npm";
}

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

async function buildIfNeeded() {
  const standaloneServer = resolve(PROJECT_ROOT, ".next/standalone/server.js");
  if (existsSync(standaloneServer)) return;

  log("First run — building production bundle...");
  log(`${DIM}This will take a minute, please wait...${RESET}`);

  try {
    execSync(`${npmCommand()} run build`, {
      cwd: PROJECT_ROOT,
      stdio: "inherit",
      env: { ...process.env, NODE_ENV: "production" },
      shell: true,
    });
    success("Build complete!");
  } catch (e) {
    error("Build failed. Check the output above.");
    process.exit(1);
  }
}

async function startServer() {
  const pid = getRunningPid();
  if (pid) {
    success(`aRouter is already running ${DIM}(PID: ${pid})${RESET}`);
    console.log();
    console.log(`  ${BOLD}Dashboard${RESET}  ${CYAN}${DASHBOARD_URL}${RESET}`);
    console.log();
    return;
  }

  const portBusy = await isPortInUse(PORT);
  if (portBusy) {
    error(`Port ${PORT} is already in use by another process.`);
    log(`Stop the other process or change the port.`);
    process.exit(1);
  }

  await buildIfNeeded();

  log("Starting aRouter...");

  const logFd = openSync(LOG_FILE, "a");

  const runner = resolve(process.argv[1] || __filename);
  const child = spawn(process.execPath, [runner, "watch"], {
    cwd: PROJECT_ROOT,
    detached: !IS_WIN, 
    stdio: ["ignore", logFd, logFd],
    env: { ...process.env, AROUTER_DAEMON: "1" },
    ...(IS_WIN ? { windowsHide: true } : {}),
  });

  writeFileSync(PID_FILE, String(child.pid));
  child.unref();

  // Wait for server to be ready
  try {
    process.stdout.write(`  Waiting for server `);
    await waitForServer(60000);
    process.stdout.write(`${GREEN}ready!${RESET}\n`);
  } catch {
    process.stdout.write(`${YELLOW}(timeout — check logs)${RESET}\n`);
  }

  console.log();
  console.log(`  ${GREEN}${BOLD}aRouter is running!${RESET}`);
  console.log();
  console.log(`  ${BOLD}Dashboard${RESET}  ${CYAN}${DASHBOARD_URL}${RESET}`);
  console.log(`  ${BOLD}API${RESET}        ${CYAN}${DASHBOARD_URL}/v1${RESET}`);
  console.log(`  ${BOLD}PID${RESET}        ${DIM}${child.pid}${RESET}`);
  console.log(`  ${BOLD}Logs${RESET}       ${DIM}${LOG_FILE}${RESET}`);
  console.log();
  console.log(`  ${DIM}Run ${RESET}arouter stop${DIM} to stop the server.${RESET}`);
  console.log();
}

function stopServer() {
  const pid = getRunningPid();
  if (!pid) {
    warn("aRouter is not running.");
    return;
  }

  log(`Stopping aRouter (PID: ${pid})...`);
  try {
    if (IS_WIN) {
      execSync(`taskkill /PID ${pid} /T /F`, { stdio: "ignore", shell: true });
    } else {
      process.kill(pid, "SIGTERM");
      setTimeout(() => {
        try {
          process.kill(pid, 0);
          process.kill(pid, "SIGKILL");
        } catch { /* already dead */ }
      }, 3000);
    }
  } catch (e) {
    warn(`Could not kill process: ${e.message}`);
  }

  try { unlinkSync(PID_FILE); } catch { /* ignore */ }
  success("aRouter stopped.");
}

async function watchServer() {
  const serverScript = resolve(PROJECT_ROOT, ".next/standalone/server.js");
  const isDaemon = process.env.AROUTER_DAEMON === "1";
  
  if (!isDaemon) {
    log("Running aRouter watchdog in foreground.");
  }
  
  let currentChild = null;
  let isStopping = false;

  const shutdown = () => {
    isStopping = true;
    if (currentChild) {
      try { process.kill(currentChild.pid, "SIGTERM"); } catch (e) { /* ignore */ }
    }
    process.exit(0);
  };

  process.on("SIGINT", shutdown);
  process.on("SIGTERM", shutdown);

  function spawnChild() {
    if (isStopping) return;
    
    currentChild = spawn(process.execPath, [serverScript], {
      cwd: PROJECT_ROOT,
      stdio: isDaemon ? "inherit" : ["ignore", "inherit", "inherit"],
      env: {
        ...process.env,
        NODE_ENV: "production",
        PORT: String(PORT),
        HOSTNAME: "0.0.0.0",
      },
      ...(IS_WIN ? { windowsHide: true } : {}),
    });

    currentChild.on("exit", (code, signal) => {
      currentChild = null;
      if (isStopping) return;
      
      const msg = `\n[WATCHDOG] Server crashed (code=${code}, signal=${signal}). Restarting...\n`;
      if (isDaemon && existsSync(LOG_FILE)) {
        appendFileSync(LOG_FILE, msg);
      } else {
        warn(msg);
      }

      setTimeout(spawnChild, 2000);
    });
  }

  spawnChild();
}

function showStatus() {
  const pid = getRunningPid();
  if (pid) {
    success(`aRouter is running ${DIM}(PID: ${pid})${RESET}`);
    console.log(`  ${BOLD}Dashboard${RESET}  ${CYAN}${DASHBOARD_URL}${RESET}`);
  } else {
    warn("aRouter is not running.");
  }
}

function showLogs() {
  if (!existsSync(LOG_FILE)) {
    warn("No log file found. Start the server first.");
    return;
  }

  try {
    const content = readFileSync(LOG_FILE, "utf-8");
    const lines = content.split("\n").slice(-50);
    console.log(lines.join("\n"));
  } catch (e) {
    error(`Could not read logs: ${e.message}`);
  }
}

async function restartServer() {
  stopServer();
  await new Promise(r => setTimeout(r, 1500));
  await startServer();
}

function showHelp() {
  console.log();
  console.log(`  ${BOLD}aRouter${RESET} — AI Model Router & MITM Proxy`);
  console.log();
  console.log(`  ${BOLD}Usage:${RESET}`);
  console.log(`    ${CYAN}arouter${RESET}              Start the server (background)`);
  console.log(`    ${CYAN}arouter start${RESET}         Same as above`);
  console.log(`    ${CYAN}arouter stop${RESET}          Stop the server`);
  console.log(`    ${CYAN}arouter restart${RESET}       Restart the server`);
  console.log(`    ${CYAN}arouter status${RESET}        Show server status`);
  console.log(`    ${CYAN}arouter logs${RESET}          Show recent logs`);
  console.log(`    ${CYAN}arouter watch${RESET}         Watchdog mode (auto-restart)`);
  console.log(`    ${CYAN}arouter dev${RESET}           Run in foreground (dev mode)`);
  console.log(`    ${CYAN}arouter build${RESET}         Force rebuild`);
  console.log(`    ${CYAN}arouter help${RESET}          Show this help`);
  console.log();
  console.log(`  ${BOLD}Data:${RESET}  ${DIM}${DATA_DIR}${RESET}`);
  console.log(`  ${BOLD}Port:${RESET}  ${DIM}${PORT}${RESET}`);
  console.log();
}

function devMode() {
  log("Starting in dev mode (foreground)...");
  const child = spawn(npmCommand(), ["run", "dev"], {
    cwd: PROJECT_ROOT,
    stdio: "inherit",
    env: { ...process.env },
    shell: IS_WIN,
  });
  child.on("exit", (code) => process.exit(code || 0));
}

async function forceBuild() {
  log("Force rebuilding...");
  const nextDir = resolve(PROJECT_ROOT, ".next");
  try {
    if (IS_WIN) {
      execSync(`rmdir /s /q "${nextDir}"`, { cwd: PROJECT_ROOT, shell: true, stdio: "ignore" });
    } else {
      execSync(`rm -rf "${nextDir}"`, { cwd: PROJECT_ROOT, stdio: "ignore" });
    }
  } catch { /* ignore */ }
  await buildIfNeeded();
}

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

const command = process.argv[2] || "start";

switch (command) {
  case "start":   await startServer(); break;
  case "stop":    stopServer(); break;
  case "restart": await restartServer(); break;
  case "status":  showStatus(); break;
  case "logs":    showLogs(); break;
  case "watch":   await watchServer(); break;
  case "dev":     devMode(); break;
  case "build":   await forceBuild(); break;
  case "help":
  case "--help":
  case "-h":      showHelp(); break;
  default:
    error(`Unknown command: ${command}`);
    showHelp();
    process.exit(1);
}
