import { statSync } from "node:fs";
import { resolve } from "node:path";

const root = process.cwd();
const watchedFiles = [
  resolve(root, "scripts/caserun-native-server.ts"),
  resolve(root, "scripts/caserun-native-fixtures.ts")
];

let child: ReturnType<typeof spawnNativeServer> | null = null;
let signature = sourceSignature();
let stopping = false;
let restarting = false;

function sourceSignature(): string | null {
  try {
    return watchedFiles.map((path) => {
      const source = statSync(path);
      return `${path}:${source.mtimeMs}:${source.size}`;
    }).join("|");
  } catch (error) {
    if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
    throw error;
  }
}

function startChild(reason: string): void {
  const spawned = spawnNativeServer();
  child = spawned;
  console.log(JSON.stringify({ event: "caserun-native-reload", mode: "native-test", reason, pid: spawned.pid }));
  void spawned.exited.then((exitCode) => {
    if (!stopping && child === spawned) console.log(JSON.stringify({ event: "caserun-native-exit", exitCode }));
  });
}

function spawnNativeServer() {
  return Bun.spawn([process.execPath, "scripts/caserun-native-server.ts"], { cwd: root, stdin: "ignore", stdout: "inherit", stderr: "inherit" });
}

async function stopChild(): Promise<void> {
  const runningChild = child;
  if (!runningChild) return;
  if (runningChild.exitCode === null) runningChild.kill("SIGTERM");
  await runningChild.exited;
  if (child === runningChild) child = null;
}

startChild("initial");

async function reloadIfChanged(): Promise<void> {
  if (stopping || restarting) return;
  const nextSignature = sourceSignature();
  if (nextSignature === null || nextSignature === signature) return;
  signature = nextSignature;
  restarting = true;
  try {
    await stopChild();
    if (!stopping) startChild("source-change");
  } finally {
    restarting = false;
  }
}

const timer = setInterval(() => void reloadIfChanged(), 100);

async function shutdown(): Promise<void> {
  if (stopping) return;
  stopping = true;
  clearInterval(timer);
  await stopChild();
  process.exit(0);
}

process.on("SIGINT", () => void shutdown());
process.on("SIGTERM", () => void shutdown());
