import { spawn, type Subprocess } from "bun";
import { unlinkSync, existsSync } from "fs";

const BUN_SOCKET = "/tmp/bun.sock";
const API_SOCKET = "/tmp/api.sock";
const PORT = process.env.PORT || "8080";
const LOG_JSON = process.env.LOG_JSON || "true";

function tryUnlink(path: string) {
  try {
    unlinkSync(path);
  } catch (e) {
    // ENOENT is expected (socket doesn't exist yet), other errors are unexpected
    if (e instanceof Error && "code" in e && e.code !== "ENOENT") {
      console.error(`Failed to cleanup ${path}: ${e.message}`);
    }
  }
}

function cleanup() {
  tryUnlink(BUN_SOCKET);
  tryUnlink(API_SOCKET);
}

// Cleanup on signals
process.on("SIGTERM", () => {
  cleanup();
  process.exit(0);
});
process.on("SIGINT", () => {
  cleanup();
  process.exit(0);
});

// Start Bun SSR
console.log("Starting Bun SSR...");
const bunProc = spawn({
  cmd: ["bun", "--preload", "/app/web/console-logger.js", "index.js"],
  cwd: "/app/web/build",
  env: {
    ...process.env,
    SOCKET_PATH: BUN_SOCKET,
    LOG_JSON,
    UPSTREAM_URL: API_SOCKET,
  },
  stdout: "inherit",
  stderr: "inherit",
});

// Wait for Bun socket (5s timeout)
const startTime = Date.now();
while (!existsSync(BUN_SOCKET)) {
  if (Date.now() - startTime > 5000) {
    console.error("ERROR: Bun failed to create socket within 5s");
    bunProc.kill();
    cleanup();
    process.exit(1);
  }
  await Bun.sleep(100);
}

// Start Rust server
console.log("Starting Rust API...");
const rustProc = spawn({
  cmd: [
    "/app/xevion",
    "--listen",
    `[::]:${PORT}`,
    "--listen",
    API_SOCKET,
    "--downstream",
    BUN_SOCKET,
  ],
  stdout: "inherit",
  stderr: "inherit",
});

// Monitor both processes - exit if either dies
async function monitor(name: string, proc: Subprocess) {
  const exitCode = await proc.exited;
  console.error(`${name} exited with code ${exitCode}`);
  return { name, exitCode };
}

const result = await Promise.race([
  monitor("Bun", bunProc),
  monitor("Rust", rustProc),
]);

// Kill the other process
console.error(`${result.name} died, shutting down...`);
bunProc.kill();
rustProc.kill();
cleanup();
process.exit(result.exitCode || 1);
