const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const { spawn } = require("node:child_process");

const repoRoot = path.resolve(__dirname, "..");
const baseUrl = process.env.SELENIUM_BASE_URL || "http://127.0.0.1:5095";
const healthUrl = new URL("/api/health", baseUrl).toString();
const smokeScript = process.argv[2] || "tests/e2e/smoke.js";
const tempDbPath = path.join(os.tmpdir(), `rpgroller-selenium-${Date.now()}-${Math.random().toString(16).slice(2)}.db`);

function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function waitForHealthCheck() {
  for (let attempt = 0; attempt < 60; attempt += 1) {
    try {
      const response = await fetch(healthUrl);
      if (response.ok) {
        return;
      }
    } catch {
    }

    await delay(500);
  }

  throw new Error(`Application failed to start on ${baseUrl}.`);
}

function spawnProcess(command, args, options) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, options);
    child.once("error", reject);
    resolve(child);
  });
}

async function run() {
  const app = await spawnProcess(
    "dotnet",
    ["run", "--project", "RpgRoller/RpgRoller.csproj", "--verbosity", "minimal", "--urls", baseUrl],
    {
      cwd: repoRoot,
      stdio: "inherit",
      env: {
        ...process.env,
        ConnectionStrings__RpgRoller: `Data Source=${tempDbPath}`
      }
    }
  );

  try {
    await waitForHealthCheck();

    const smoke = await spawnProcess("node", [smokeScript], {
      cwd: repoRoot,
      stdio: "inherit",
      env: {
        ...process.env,
        SELENIUM_BASE_URL: baseUrl
      }
    });

    const exitCode = await new Promise((resolve, reject) => {
      smoke.once("error", reject);
      smoke.once("exit", resolve);
    });

    if (exitCode !== 0) {
      throw new Error(`Selenium smoke exited with code ${exitCode}.`);
    }
  } finally {
    app.kill("SIGTERM");
    await delay(500);
    if (!app.killed) {
      app.kill("SIGKILL");
    }

    if (fs.existsSync(tempDbPath)) {
      fs.rmSync(tempDbPath, { force: true });
    }
  }
}

run().catch((error) => {
  console.error(error.stack || error);
  process.exitCode = 1;
});
