import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import postgres from "postgres";

const webUrl = process.env.AWP_WEB_URL;
const controlPlaneUrl = process.env.AWP_CONTROL_PLANE_URL;
const databaseUrl = process.env.AWP_TEST_POSTGRES_URL;
const repository = process.env.AWP_PROJECT_REPOSITORY;
const playwrightModule = process.env.AWP_PLAYWRIGHT_MODULE;
if (!webUrl) throw new Error("AWP_WEB_URL is required");
if (!controlPlaneUrl) throw new Error("AWP_CONTROL_PLANE_URL is required");
if (!databaseUrl) throw new Error("AWP_TEST_POSTGRES_URL is required");
if (!repository) throw new Error("AWP_PROJECT_REPOSITORY is required");
if (!playwrightModule) throw new Error("AWP_PLAYWRIGHT_MODULE is required");

const sql = postgres(databaseUrl, { max: 1 });
const [row] = await sql<
  {
    change_set_id: string;
    project_id: string;
    review_id: string;
    reviewer_principal_id: string;
    agent_principal_id: string;
  }[]
>`
  select
    cs.id as change_set_id,
    cs.project_id,
    r.id as review_id,
    r.reviewer_principal_id,
    ar.agent_principal_id
  from change_sets cs
  join reviews r on r.change_set_id = cs.id
  join attempts a on a.id = cs.producer_attempt_id
  join agent_runs ar on ar.id = a.agent_run_id
  where cs.status = 'candidate-ready' and r.status = 'requested'
  order by cs.created_at desc
  limit 1
`;
assert.ok(row, "a pending independent Review must exist");
assert.notEqual(row.reviewer_principal_id, row.agent_principal_id);

const refused = await fetch(
  `${controlPlaneUrl}/internal/changesets/${encodeURIComponent(row.change_set_id)}/merge`,
  { method: "POST" },
);
assert.equal(refused.status, 409);
assert.equal(((await refused.json()) as { error: string }).error, "merge-refused");

const { chromium } = await import(playwrightModule);
const browser = await chromium.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.goto(`${webUrl}/projects/${encodeURIComponent(row.project_id)}`, {
    waitUntil: "networkidle",
  });
  await page.getByRole("button", { name: "Approve ChangeSet" }).click();
  await page.getByText("ready-to-merge", { exact: true }).waitFor();
  await page.getByRole("button", { name: "Merge approved ChangeSet" }).click();
  await page.getByText("merged", { exact: true }).waitFor();

  const [persisted] = await sql<
    { change_set_status: string; review_status: string; disposition: string | null }[]
  >`
    select cs.status as change_set_status, r.status as review_status, r.disposition
    from change_sets cs join reviews r on r.change_set_id = cs.id
    where cs.id = ${row.change_set_id}
  `;
  assert.deepEqual(persisted, {
    change_set_status: "merged",
    review_status: "submitted",
    disposition: "approved",
  });
  const [progression] = await sql<{ completed: number; dispatched: number }[]>`
    select
      count(*) filter (where status = 'completed')::int as completed,
      count(*) filter (where status = 'dispatched')::int as dispatched
    from tasks
    where plan_revision_id = (
      select plan_revision_id from tasks where id = (
        select task_id from change_sets where id = ${row.change_set_id}
      )
    )
  `;
  assert.equal(progression?.completed, 1);
  assert.equal(progression?.dispatched, 1);
  const committer = execFileSync("git", ["log", "-1", "--format=%cn <%ce>"], {
    cwd: repository,
    encoding: "utf8",
  }).trim();
  assert.equal(committer, "AWP Control Plane <control-plane@awp.local>");
  process.stdout.write(
    `AC-19..AC-21 PASS: pending merge refused; owner UI approval merged ${row.change_set_id}; reviewer and author identities differ; control-plane committer proven\n`,
  );
} finally {
  await browser.close();
  await sql.end({ timeout: 5 });
}
