"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const P = require("../static/preload.js");

// Tiny fixture: 3-altitude cyclic ring, 2 pool members each. base_file = "<id>.b";
// morph file = "<from>>-<to>" when it exists.
const scales = [
  { id: "a", pool: [{ clip_id: "a1" }, { clip_id: "a2" }] },
  { id: "b", pool: [{ clip_id: "b1" }, { clip_id: "b2" }] },
  { id: "c", pool: [{ clip_id: "c1" }, { clip_id: "c2" }] },
];
const baseOf = (id) => id + ".b";
const morphFile = (a, b) => `${a}>-${b}`;        // pretend every pair has a morph
const cached = new Set();
const deps = () => ({ baseOf, morphFile, isCached: (f) => cached.has(f) });

test("phase1Files = first member of each altitude + connecting morphs (both dirs, cyclic)", () => {
  const files = P.phase1Files(scales, deps());
  // bases: a1.b b1.b c1.b
  for (const b of ["a1.b", "b1.b", "c1.b"]) assert.ok(files.includes(b), `missing base ${b}`);
  assert.ok(!files.includes("a2.b"), "second members must NOT be in phase 1");
  // connecting morphs between adjacent first-members, both directions, cyclic (a-b, b-c, c-a)
  for (const m of ["a1>-b1", "b1>-a1", "b1>-c1", "c1>-b1", "c1>-a1", "a1>-c1"])
    assert.ok(files.includes(m), `missing morph ${m}`);
  assert.equal(files.length, 3 + 6);
});

test("eligibleDestinations requires base + BOTH morphs cached", () => {
  cached.clear();
  // from a1, candidate b1: nothing cached → not eligible
  assert.deepEqual(P.eligibleDestinations(["b1", "b2"], "a1", deps()), []);
  cached.add("b1.b"); cached.add("a1>-b1");          // base + forward only
  assert.deepEqual(P.eligibleDestinations(["b1", "b2"], "a1", deps()), [], "reverse morph still missing");
  cached.add("b1>-a1");                               // now reverse too
  assert.deepEqual(P.eligibleDestinations(["b1", "b2"], "a1", deps()), ["b1"]);
  // b2 fully cached too → both eligible
  cached.add("b2.b"); cached.add("a1>-b2"); cached.add("b2>-a1");
  assert.deepEqual(P.eligibleDestinations(["b1", "b2"], "a1", deps()).sort(), ["b1", "b2"]);
});

test("eligibleMembers requires only the base cached", () => {
  cached.clear();
  assert.deepEqual(P.eligibleMembers(["a1", "a2"], deps()), []);
  cached.add("a1.b");
  assert.deepEqual(P.eligibleMembers(["a1", "a2"], deps()), ["a1"]);
});

test("pick returns null on empty, a member otherwise", () => {
  assert.equal(P.pick([], Math.random), null);
  assert.equal(P.pick(["x"], () => 0), "x");
  assert.equal(P.pick(["x", "y", "z"], () => 0.99), "z");
});
