import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { setTimeout as delay } from "node:timers/promises";
import postgres from "postgres";
import { approvedJourneyEnvironment, requiredEnvironment } from "./exact-journey.js";

const databaseUrl = requiredEnvironment(process.env, "AWP_TEST_POSTGRES_URL");
const kubeconfig = requiredEnvironment(process.env, "AWP_KUBECONFIG");
const namespace = process.env.AWP_WORKSPACE_NAMESPACE ?? "awp-workspaces";
const { factoryRunId, firstTaskId } = approvedJourneyEnvironment();
const timeoutMs = Number(process.env.AWP_GOLIVE_TIMEOUT_MS ?? "120000");

function resources(kind: "pods" | "pvc", workspaceId: string): string {
  return execFileSync(
    "kubectl",
    [
      "--kubeconfig",
      kubeconfig,
      "-n",
      namespace,
      "get",
      kind,
      "-l",
      `awp.dev/workspace-id=${workspaceId}`,
      "-o",
      "name",
    ],
    { encoding: "utf8" },
  ).trim();
}

const sql = postgres(databaseUrl, { max: 1 });
try {
  const changeSets = await sql<{ id: string; producer_attempt_id: string }[]>`
    select cs.id, cs.producer_attempt_id
    from change_sets cs
    join attempts a on a.id = cs.producer_attempt_id
    join agent_runs ar on ar.id = a.agent_run_id
    where ar.factory_run_id = ${factoryRunId}
      and ar.task_id = ${firstTaskId}
      and cs.task_id = ${firstTaskId}
      and cs.status = 'merged'
    order by cs.created_at, cs.id
  `;
  assert.equal(
    changeSets.length,
    1,
    "the exact first Task must have exactly one trusted merged ChangeSet at AC-14",
  );
  const changeSet = changeSets[0]!;

  const reviews = await sql<{ reviewer_agent_run_id: string | null }[]>`
    select reviewer_agent_run_id
    from reviews
    where change_set_id = ${changeSet.id}
      and reviewer_agent_run_id is not null
    order by created_at, id
  `;
  assert.equal(
    reviews.length,
    1,
    "canonical ChangeSet must retain one independent reviewer AgentRun",
  );

  type WorkspaceRow = {
    id: string;
    checkpoint_digest: string | null;
    checkpoint_collected_at: Date | null;
    cleaned_at: Date | null;
  };
  let workspaces: WorkspaceRow[] = [];
  const cleanupDeadline = Date.now() + timeoutMs;
  while (Date.now() < cleanupDeadline) {
    workspaces = await sql<WorkspaceRow[]>`
      with relevant_agent_runs as (
        select a.agent_run_id from attempts a where a.id = ${changeSet.producer_attempt_id}
        union
        select r.reviewer_agent_run_id from reviews r where r.change_set_id = ${changeSet.id} and r.reviewer_agent_run_id is not null
      )
      select distinct w.id, w.checkpoint_digest, w.checkpoint_collected_at, w.cleaned_at
      from relevant_agent_runs rr
      join attempts a on a.agent_run_id = rr.agent_run_id
      join workspaces w on w.id = a.workspace_id
      order by w.id
    `;
    if (workspaces.length === 2 && workspaces.every((workspace) => workspace.cleaned_at !== null)) {
      break;
    }
    await delay(100);
  }

  assert.equal(
    workspaces.length,
    2,
    "merged ChangeSet must retain producer + independent reviewer Workspace records",
  );
  for (const workspace of workspaces) {
    assert.match(workspace.checkpoint_digest ?? "", /^[0-9a-f]{40,64}$/i);
    assert.ok(
      workspace.checkpoint_collected_at,
      "terminal cleanup requires a collected content checkpoint",
    );
    assert.ok(workspace.cleaned_at, "terminal cleanup must be durably recorded");
    assert.ok(
      workspace.checkpoint_collected_at <= workspace.cleaned_at,
      "checkpoint collection must precede or coincide with destructive cleanup",
    );
    assert.equal(
      resources("pods", workspace.id),
      "",
      `Workspace ${workspace.id} pod must be gone after trusted merge`,
    );
    assert.equal(
      resources("pvc", workspace.id),
      "",
      `Workspace ${workspace.id} PVC must be gone after exact-digest cleanup`,
    );
  }

  process.stdout.write(
    `AC-14 PASS: exact FactoryRun ${factoryRunId} first-Task ChangeSet ${changeSet.id} retained collected producer + reviewer checkpoints before compute/PVC cleanup\n`,
  );
} finally {
  await sql.end({ timeout: 5 });
}
