// STATIC GUARD: no `.sort(` introduced in shared/turn.js outside the ONE
// allowed function (sortParticipantsByInitiative, used by startEncounter to
// freeze the list once). Slot-not-sort design (docs/INITIATIVE_ORDERING.md):
// mutations = insert/move, never wholesale re-sort. A stray `.sort(` after
// start destroys drag tie-break order.
//
// This test errs the moment someone adds `.sort(` anywhere but the allowlist.
// Maintenance: add a function to ALLOWED only if it runs at startEncounter
// (one-time freeze), NOT in add/update/reorder/nextTurn paths.

'use strict';

const fs = require('fs');
const path = require('path');

const SRC = fs.readFileSync(path.join(__dirname, '..', 'turn.js'), 'utf8');

// Functions permitted to call .sort(. Listed so intent is explicit + reviewed.
const ALLOWED_SORT_FUNCTIONS = new Set([
  'sortParticipantsByInitiative',
]);

// Collect top-level function bodies by brace counting.
// Matches `function NAME(` decls AND `const NAME = ... =>` arrow fns.
// Returns [{ name, body }].
function collectBody(src, fromIdx) {
  let i = fromIdx;
  while (i < src.length && src[i] !== '{') i++;
  let depth = 0;
  const start = i;
  for (; i < src.length; i++) {
    if (src[i] === '{') depth++;
    else if (src[i] === '}') { depth--; if (depth === 0) break; }
  }
  return src.slice(start, i + 1);
}

function extractFunctions(src) {
  const out = [];
  const fnRe = /\bfunction\s+([A-Za-z0-9_$]+)\s*\(/g;
  const arrowRe = /(?:const|let|var)\s+([A-Za-z0-9_$]+)\s*=\s*[^=;]*?=>/g;
  let m;
  while ((m = fnRe.exec(src)) !== null) {
    out.push({ name: m[1], body: collectBody(src, m.index + m[0].length) });
  }
  while ((m = arrowRe.exec(src)) !== null) {
    out.push({ name: m[1], body: collectBody(src, m.index + m[0].length) });
  }
  return out;
}

describe('STATIC: no .sort( outside allowlist (slot-not-sort design)', () => {
  test('every .sort( call lives in an allowed function', () => {
    const fns = extractFunctions(SRC);
    const offenders = [];
    for (const { name, body } of fns) {
      if (!ALLOWED_SORT_FUNCTIONS.has(name) && /\.sort\(/.test(body)) {
        offenders.push(name);
      }
    }
    expect(offenders).toEqual([]);
  });

  test('allowed sort function is declared (no silent allowlist drift)', () => {
    const declared = new Set(extractFunctions(SRC).map(f => f.name));
    for (const name of ALLOWED_SORT_FUNCTIONS) {
      expect(declared.has(name)).toBe(true);
    }
  });
});
