"use strict";

const assert = require("node:assert/strict");
const crypto = require("node:crypto");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const test = require("node:test");
const { GhdrAuthorizer } = require("./ghdr-authorizer");
const { GhdrControllerBroker } = require("./ghdr-controller-broker");

test("two outbound controllers poll one email-authorized job and return independent layout signatures", async () => {
  const directory = fs.mkdtempSync(path.join(os.tmpdir(), "ghdr-broker-"));
  const now = Math.floor(Date.now() / 1000);
  const nodes = [
    makeController("GH-CTRL-GZ-01", "tencent/ap-guangzhou/BS-GZ-006"),
    makeController("GH-CTRL-SG-01", "tencent/ap-singapore/ZY-SG-006"),
  ];
  const authorizer = new GhdrAuthorizer({
    privateKeyFile: path.join(directory, "authorizer.pem"),
    now: () => now,
  });
  const broker = new GhdrControllerBroker({
    controllers: nodes.map(item => item.binding),
    stateDir: path.join(directory, "jobs"),
    authorizer,
    now: () => now,
    waitMs: 2000,
  });
  const plan = {
    schema: "guanghu.ghdr-signed-layout-plan/v1",
    payload: {
      node_id: "GH-CVM-MAIN-PROD-01",
      generation: 1,
    },
    signatures: [],
  };
  const digest = crypto.createHash("sha256").update(JSON.stringify(plan.payload)).digest("hex");
  const binding = {
    payload_sha256: digest,
    resource: `GH-CVM-MAIN-PROD-01:${digest}:1`,
  };
  try {
    const pending = broker.queueAndWait({
      plan,
      binding,
      workorderId: "00000000-0000-4000-8000-000000000001",
    });
    const submitted = [];
    for (const node of nodes) {
      const poll = {
        schema: "guanghu.ghdr-controller-poll/v1",
        node_id: node.binding.node_id,
        issued_at_unix: now,
        nonce: crypto.randomBytes(24).toString("base64url"),
      };
      const body = {
        request: poll,
        request_signature_hex: crypto.sign(null, Buffer.from(JSON.stringify(poll)), node.transport.privateKey).toString("hex"),
      };
      const job = broker.poll(body).job;
      assert.ok(job);
      assert.equal(broker.poll(body).error, "ghdr_controller_auth_replayed");
      const layoutSignatureHex = crypto.sign(
        null,
        Buffer.from(JSON.stringify(plan.payload)),
        node.layout.privateKey,
      ).toString("hex");
      const result = {
        schema: "guanghu.ghdr-controller-result/v1",
        node_id: node.binding.node_id,
        job_id: job.job_id,
        layout_payload_sha256: digest,
        signature_hex: layoutSignatureHex,
        issued_at_unix: now,
        nonce: crypto.randomBytes(24).toString("base64url"),
      };
      const signature = {
        node_id: node.binding.node_id,
        failure_domain: node.binding.failure_domain,
        public_key_hex: node.binding.layout_public_key_hex,
        signature_hex: layoutSignatureHex,
      };
      assert.equal(broker.submit({
        request: result,
        request_signature_hex: crypto.sign(null, Buffer.from(JSON.stringify(result)), node.transport.privateKey).toString("hex"),
        signature,
      }).ok, true);
      submitted.push(signature);
    }
    const completed = await pending;
    assert.equal(completed.ok, true);
    assert.deepEqual(completed.signatures, submitted);
  } finally {
    fs.rmSync(directory, { recursive: true, force: true });
  }
});

function makeController(nodeId, failureDomain) {
  const layout = crypto.generateKeyPairSync("ed25519");
  const transport = crypto.generateKeyPairSync("ed25519");
  return {
    layout,
    transport,
    binding: {
      node_id: nodeId,
      failure_domain: failureDomain,
      layout_public_key_hex: rawPublic(layout.publicKey),
      transport_public_key_hex: rawPublic(transport.publicKey),
    },
  };
}

function rawPublic(key) {
  return key.export({ type: "spki", format: "der" }).subarray(-32).toString("hex");
}
