// Headless render guard for the `aura graph` viewer (graph-viewer.js).
//
// Property protected: a free param slot whose [nodeIndex, originalPos] is a
// member of the scope's gang table renders with its public gang name appended
// ("length (gang: channel_length)"), while an identical param slot in a
// gang-free scope renders bare — the gang table wired into normalizeModel/
// cellLabel (#61) actually reaches the rendered node card, not just the JSON
// model (which the Rust-side model_carries_gangs test already pins).
//
// It loads the real viewer module and drives the exported pure `genDot`. `node`
// is REQUIRED: a skipped guard is not a guard.

import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";

const require = createRequire(import.meta.url);
const here = dirname(fileURLToPath(import.meta.url));

global.window = { AURA_MODEL: { root: { nodes: {}, edges: [] }, composites: {} } };
const { normalizeModel, genDot } = require(join(here, "..", "assets", "graph-viewer.js"));
const dotFor = (model) => genDot(normalizeModel(model).root, "root", new Set(), false).dot;

const fail = (msg, dot) => {
  console.error(msg + "\n--- dot ---\n" + dot);
  process.exit(1);
};

// --- Model A: two sibling nodes, their "length" params ganged as "channel_length" ---
const ganged = {
  root: {
    nodes: {
      "0": { prim: { name: "hi", type: "Sma", role: "node", params: [["length", "i64"]], ins: [], outs: [["value", "f64"]] } },
      "1": { prim: { name: "lo", type: "Sma", role: "node", params: [["length", "i64"]], ins: [], outs: [["value", "f64"]] } },
    },
    edges: [],
    gangs: [{ name: "channel_length", kind: "I64", members: [[0, 0, "length"], [1, 0, "length"]] }],
  },
  composites: {},
};
const dotGanged = dotFor(ganged);

const GANG_TAG = '<font color="#9399b2"> (gang: channel_length)</font>';
if (!dotGanged.includes(GANG_TAG)) fail("ganged param slot missing the gang-name annotation", dotGanged);
const occurrences = dotGanged.split(GANG_TAG).length - 1;
if (occurrences !== 2) fail(`expected the gang tag on BOTH member nodes (2 occurrences), got ${occurrences}`, dotGanged);

// --- Model B: identical shape, no gangs — must render bare, no annotation ---
const bare = {
  root: {
    nodes: {
      "0": { prim: { name: "hi", type: "Sma", role: "node", params: [["length", "i64"]], ins: [], outs: [["value", "f64"]] } },
      "1": { prim: { name: "lo", type: "Sma", role: "node", params: [["length", "i64"]], ins: [], outs: [["value", "f64"]] } },
    },
    edges: [],
  },
  composites: {},
};
const dotBare = dotFor(bare);
if (dotBare.includes("(gang:")) fail("gang-free scope must not render any gang annotation", dotBare);

console.log("OK — a ganged param slot renders its public gang name; a gang-free scope renders bare.");
process.exit(0);
