import assert from "node:assert/strict";
import { execFileSync, spawn } from "node:child_process";
import { readFileSync } from "node:fs";
import postgres from "postgres";

const databaseUrl = process.env.AWP_TEST_POSTGRES_URL;
const pidFile = process.env.AWP_CONTROL_PLANE_PID_FILE;
const restartCommand = process.env.AWP_CONTROL_PLANE_RESTART_COMMAND;
const kubeconfig = process.env.AWP_KUBECONFIG;
const namespace = process.env.AWP_WORKSPACE_NAMESPACE ?? "awp-workspaces";
const timeoutMs = Number(process.env.AWP_GOLIVE_TIMEOUT_MS ?? "120000");
if (!databaseUrl) throw new Error("AWP_TEST_POSTGRES_URL is required");
if (!pidFile) throw new Error("AWP_CONTROL_PLANE_PID_FILE is required");
if (!restartCommand) throw new Error("AWP_CONTROL_PLANE_RESTART_COMMAND is required");
if (!kubeconfig) throw new Error("AWP_KUBECONFIG is required");

const sql = postgres(databaseUrl, { max: 1 });
try {
  const [run] = await sql<{ agent_run_id: string }[]>`
    select ar.id as agent_run_id
    from agent_runs ar
    join attempts a on a.agent_run_id = ar.id
    where ar.status = 'active' and a.status = 'running'
    order by ar.created_at desc
    limit 1
  `;
  assert.ok(run, "an in-flight AgentRun must exist when AC-26 starts");

  const pid = Number(readFileSync(pidFile, "utf8").trim());
  assert.ok(Number.isInteger(pid) && pid > 1, "control-plane PID file must contain a safe PID");
  process.kill(pid, "SIGKILL");
  const restarted = spawn(restartCommand, {
    shell: true,
    detached: true,
    stdio: "ignore",
  });
  restarted.unref();

  const deadline = Date.now() + timeoutMs;
  let terminal: { status: string; change_sets: number } | undefined;
  while (Date.now() < deadline) {
    const [state] = await sql<{ status: string; change_sets: number }[]>`
      select ar.status,
        (select count(*)::int
         from change_sets cs
         join attempts producer on producer.id = cs.producer_attempt_id
         where producer.agent_run_id = ar.id) as change_sets
      from agent_runs ar
      where ar.id = ${run.agent_run_id}
    `;
    if (state && ["completed", "failed", "cancelled"].includes(state.status)) {
      terminal = state;
      break;
    }
    await new Promise((resolve) => setTimeout(resolve, 250));
  }
  assert.ok(terminal, "restarted control plane must reconcile the AgentRun to terminal");
  assert.equal(terminal.status, "completed");
  assert.equal(terminal.change_sets, 1);

  let pods = "";
  const cleanupDeadline = Date.now() + timeoutMs;
  while (Date.now() < cleanupDeadline) {
    pods = execFileSync(
      "kubectl",
      [
        "--kubeconfig",
        kubeconfig,
        "-n",
        namespace,
        "get",
        "pods",
        "-l",
        "awp.agent-run-id=" + run.agent_run_id,
        "-o",
        "name",
      ],
      { encoding: "utf8" },
    ).trim();
    if (!pods) break;
    await new Promise((resolve) => setTimeout(resolve, 250));
  }
  assert.equal(pods, "");

  process.stdout.write(
    "AC-26 PASS: killed and restarted control plane; AgentRun " +
      run.agent_run_id +
      " completed with one ChangeSet and no orphaned pod\n",
  );
} finally {
  await sql.end({ timeout: 5 });
}
