#!/usr/bin/env node

import { spawn } from "node:child_process";
import { join } from "node:path";
import { setTimeout as delay } from "node:timers/promises";
import { sanitizeColorEnv } from "./runtime-env.mjs";

function parsePort(value, fallback) {
  const parsed = Number.parseInt(String(value), 10);
  return Number.isFinite(parsed) && parsed > 0 && parsed <= 65535 ? parsed : fallback;
}

const explicitBaseUrl = process.env.OMNIROUTE_BASE_URL || "";
const isolatedPort = parsePort(
  process.env.DASHBOARD_PORT || process.env.PORT,
  23000 + (process.pid % 1000)
);
const isolatedDataDir =
  process.env.DATA_DIR || join(process.cwd(), ".tmp", "protocol-clients-data", String(process.pid));
const port = explicitBaseUrl ? null : isolatedPort;
const baseUrl = explicitBaseUrl || `http://127.0.0.1:${isolatedPort}`;
const healthUrl = `${baseUrl}/api/monitoring/health`;
const maxWaitMs = Number(process.env.ECOSYSTEM_SERVER_WAIT_MS || 180000);
const pollMs = 2000;

async function isServerReady() {
  const timeout = AbortSignal.timeout(2000);
  try {
    const res = await fetch(healthUrl, { signal: timeout });
    return res.ok;
  } catch {
    return false;
  }
}

async function waitForServerReady() {
  const maxAttempts = Math.ceil(maxWaitMs / pollMs);
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    if (await isServerReady()) return;
    await delay(pollMs);
  }
  throw new Error(`Timed out waiting for ${healthUrl} after ${maxWaitMs}ms`);
}

async function main() {
  let serverProcess = null;
  let startedHere = false;
  const testEnv = {
    ...sanitizeColorEnv(process.env),
    DATA_DIR: isolatedDataDir,
    ...(explicitBaseUrl
      ? {}
      : {
          PORT: String(port),
          DASHBOARD_PORT: String(port),
          API_PORT: String(port),
          OMNIROUTE_BASE_URL: baseUrl,
        }),
    OMNIROUTE_E2E_BOOTSTRAP_MODE: process.env.OMNIROUTE_E2E_BOOTSTRAP_MODE || "open",
  };

  if (!(await isServerReady())) {
    serverProcess = spawn(process.execPath, ["scripts/run-next-playwright.mjs", "dev"], {
      stdio: "inherit",
      env: testEnv,
    });
    startedHere = true;
    await waitForServerReady();
  }

  const vitestProcess = spawn(
    process.execPath,
    [
      "./node_modules/vitest/vitest.mjs",
      "run",
      "--environment",
      "node",
      "tests/e2e/protocol-clients.test.ts",
    ],
    {
      stdio: "inherit",
      env: testEnv,
    }
  );

  const exitCode = await new Promise((resolve) => {
    vitestProcess.on("exit", (code, signal) => {
      if (signal) {
        resolve(1);
        return;
      }
      resolve(code ?? 1);
    });
  });

  if (startedHere && serverProcess) {
    serverProcess.kill("SIGTERM");
    await delay(1000);
    if (!serverProcess.killed) {
      serverProcess.kill("SIGKILL");
    }
  }

  process.exit(exitCode);
}

main().catch((error) => {
  console.error("[test:protocols:e2e] Failed:", error?.message || error);
  process.exit(1);
});
