import assert from "node:assert/strict";
import test from "node:test";

import { attachCiPlanIdentity } from "./src/ci-plan-identity.mjs";
import { compareReviewedPlan, decodeReviewedPlan } from "./ci/verify-reviewed-plan.mjs";

const sourceCommit = "a".repeat(40);
const baseCommit = "b".repeat(40);

test("plan identity is stable across service and scope ordering", () => {
  const left = identifiedPlan({
    services: [registryService("worker"), registryService("api")],
    buildServices: ["worker", "api"]
  });
  const right = identifiedPlan({
    services: [registryService("api"), registryService("worker")],
    buildServices: ["api", "worker"]
  });
  assert.equal(left.planIdentity.value, right.planIdentity.value);
});

test("registry probe result and build scope change plan identity", () => {
  const reused = identifiedPlan({ services: [registryService("api", "present")] });
  const missing = identifiedPlan({
    services: [registryService("api", "missing")],
    buildServices: ["api"],
    rolloutServices: ["api"],
    affectedServices: ["api"]
  });
  assert.notEqual(reused.planIdentity.value, missing.planIdentity.value);
});

test("reviewed plan admission rejects scope additions before build", () => {
  const expectedPlan = identifiedPlan({ rolloutServices: ["api"], affectedServices: ["api"] });
  const actualPlan = identifiedPlan({
    buildServices: ["worker"],
    rolloutServices: ["api", "worker"],
    affectedServices: ["api", "worker"]
  });
  const result = compareReviewedPlan(envelope(expectedPlan), actualPlan);
  assert.equal(result.ok, false);
  assert.equal(result.scopeExpanded, true);
  assert.deepEqual(result.additions.buildServices, ["worker"]);
  assert.deepEqual(result.additions.rolloutServices, ["worker"]);
});

test("reviewed plan envelope round trips without a second authority", () => {
  const plan = identifiedPlan({ rolloutServices: ["api"], affectedServices: ["api"] });
  const expected = envelope(plan);
  const encoded = `unidesk-release-plan-v1.${Buffer.from(JSON.stringify(expected)).toString("base64url")}`;
  assert.deepEqual(decodeReviewedPlan(encoded), expected);
  assert.equal(compareReviewedPlan(expected, plan).ok, true);
});

function identifiedPlan(overrides = {}) {
  return attachCiPlanIdentity({
    sourceCommitId: sourceCommit,
    baseCommitId: baseCommit,
    compatibility: { lane: "v03" },
    inputs: { verifyReuseRegistry: true },
    artifactCatalog: {
      authority: "gitops-branch",
      catalogSourceCommitId: baseCommit,
      catalogSha256: "sha256:catalog",
      gitopsCommitId: "c".repeat(40)
    },
    services: [],
    buildServices: [],
    rolloutServices: [],
    affectedServices: [],
    ...overrides
  }, { releaseTarget: "NC01", releaseConsumer: "hwlab-nc01-v03" });
}

function registryService(serviceId, status = "present") {
  return { serviceId, reuseRegistry: { status, image: `registry/${serviceId}:env`, digest: `sha256:${serviceId}` } };
}

function envelope(plan) {
  return {
    version: "v1",
    planIdentity: plan.planIdentity.value,
    target: "NC01",
    consumer: "hwlab-nc01-v03",
    sourceCommit,
    baseCommit,
    buildServices: plan.buildServices,
    rolloutServices: plan.rolloutServices,
    affectedServices: plan.affectedServices
  };
}
