#!/usr/bin/env node
/**
 * Dev launcher — finds free ports for both Vite (viewer) and the CLI API server,
 * so multiple `pnpm dev` sessions can run simultaneously without conflicts.
 *
 * Usage:
 *   node scripts/dev.mjs            # dashboard-first dev (default)
 *   node scripts/dev.mjs -d         # explicit dashboard mode (same as default)
 *   node scripts/dev.mjs --menu     # interactive CLI menu mode
 */
import { watch } from "node:fs";
import { findFreePort, spawnPnpm, spawnTsx, viewerLogPath } from "./dev-utils.mjs";

const VITE_PREFERRED = 5173;
const API_PREFERRED = 13456;

const forceDashboard = process.argv.includes("-d");
const menuMode = process.argv.includes("--menu");
const dashboardMode = forceDashboard || !menuMode;

// Find two non-colliding free ports
const apiPort = await findFreePort(API_PREFERRED);
const vitePort = await findFreePort(VITE_PREFERRED);

console.log();
console.log(
  `[vibe-replay] API port:    ${apiPort}${apiPort !== API_PREFERRED ? ` (${API_PREFERRED} was busy)` : ""}`,
);
console.log(
  `[vibe-replay] Viewer port: ${vitePort}${vitePort !== VITE_PREFERRED ? ` (${VITE_PREFERRED} was busy)` : ""}`,
);
console.log(`[vibe-replay] Viewer:      http://localhost:${vitePort}  (HMR enabled)`);
console.log(`[vibe-replay] CLI watch:   auto-restarts on packages/cli/src changes`);
console.log(
  `[vibe-replay] Mode:        ${dashboardMode ? "dashboard-first (auto-open dashboard)" : "interactive menu"}`,
);
console.log();

// Start Vite dev server (backgrounded) — port + strictPort via env vars in vite.config.ts
const cloudApiUrl = process.env.VIBE_REPLAY_API_URL || "http://localhost:8787";
const vite = spawnPnpm(["--filter", "@vibe-replay/viewer", "dev"], {
  stdio: ["ignore", "pipe", "pipe"],
  env: { ...process.env, VITE_PORT: String(vitePort), VITE_API_PORT: String(apiPort), VITE_CLOUD_API_URL: cloudApiUrl },
});

// Pipe Vite output to a log file
const { createWriteStream } = await import("node:fs");
const logPath = viewerLogPath(vitePort);
const logStream = createWriteStream(logPath);
vite.stdout.pipe(logStream);
vite.stderr.pipe(logStream);
console.log(`[vibe-replay] Viewer logs: ${logPath}`);

// ── CLI with custom file watcher ──────────────────────────────────────
// We avoid `tsx watch` because it intercepts stdin (Enter = restart),
// which conflicts with interactive prompts (inquirer select menus).
// Instead, we use plain `tsx` + Node's fs.watch for restarts.

const cliEnv = {
  ...process.env,
  VIBE_REPLAY_DEV_MENU: "1",
  VIBE_REPLAY_API_URL: cloudApiUrl,
  VIBE_API_PORT: String(apiPort),
  VIBE_VIEWER_PORT: String(vitePort),
};

const cliScript = "packages/cli/src/index.ts";
const cliExtraArgs = dashboardMode ? ["-d"] : [];

let cli;
let restarting = false;
let shuttingDown = false;
let hasOpenedBrowser = false;

function startCli() {
  // Run tsx via `node --import tsx` (see spawnTsx) instead of the
  // node_modules/.bin/tsx shim — the shim is a .cmd on Windows that spawn()
  // can't exec directly, and spawning Node directly keeps Ctrl+C reliable.
  const cliEnvForRun = {
    ...cliEnv,
    // Open browser only once per dev launcher process to avoid tab spam on restarts.
    VIBE_REPLAY_NO_AUTO_OPEN: hasOpenedBrowser ? "1" : "0",
  };
  cli = spawnTsx([cliScript, ...cliExtraArgs], {
    stdio: "inherit",
    env: cliEnvForRun,
  });
  hasOpenedBrowser = true;

  cli.on("exit", (code, signal) => {
    if (restarting) return; // will be respawned by the watcher
    cleanup();
    process.exit(code ?? (signal === "SIGINT" ? 130 : 0));
  });
}

function killCli() {
  try { cli.kill("SIGTERM"); } catch {}
}

function restartCli() {
  if (restarting || shuttingDown) return;
  restarting = true;
  console.log("\n[dev] change detected — restarting CLI...\n");
  cli.on("exit", () => {
    restarting = false;
    startCli();
  });
  killCli();
}

startCli();

// Watch CLI source + shared types for changes
// Note: fs.watch({ recursive: true }) works on macOS/Windows natively.
// On Linux it requires Node 22+; older Node only watches the top-level dir.
let debounce;
for (const dir of ["packages/cli/src", "packages/types/src"]) {
  watch(dir, { recursive: true }, (_event, filename) => {
    if (!filename?.endsWith(".ts")) return;
    clearTimeout(debounce);
    debounce = setTimeout(restartCli, 200);
  });
}

// Cleanup on exit
function cleanup() {
  shuttingDown = true;
  try { vite.kill("SIGTERM"); } catch {}
}

process.on("SIGINT", () => {
  cleanup();
  killCli();
  // Give children a moment to exit, then force quit
  setTimeout(() => process.exit(130), 500);
});

process.on("SIGTERM", () => {
  cleanup();
  killCli();
  setTimeout(() => process.exit(143), 500);
});
