#!/usr/bin/env node
'use strict';
/**
 * Generates 120 dual-verify + scenario test files under tests/suites/extended/
 * Run: node tests/helpers/generate-extended-120.js
 */
const fs = require('node:fs');
const path = require('node:path');

process.env.SC_TEST = '1';
const dumpPath = path.join(__dirname, 'engine-dump.js');
if (!fs.existsSync(dumpPath)) {
  console.error('Run gen-dump.js first');
  process.exit(1);
}
const t = require(dumpPath).__test;

const SKIP = new Set([
  'SkyDB', 'RelayHealth', 'RelayPool', 'FormulaVM', 'AdaptiveLayer', 'Bandit',
  'LayerStack', 'SelfModel', 'ScenarioDetector', 'Clock', 'Stream', 'Channel',
  'RoamResolver', 'SIOS', 'ADIS', 'ADO', 'MIND', 'ALERTS', 'XI_OPS', 'XI_SKIP',
  'XI_LIMITS', 'LATTICE_WIRED_DOORS', 'BFT_SYNC', 'COVERT_MESH', 'HYBRID_LATTICE',
  'NODE_AGENT', 'LATTICE_WAVE', 'G', 'N', 'RUN_SALT', 'zlib', 'KV_MERGE', 'POLICY_KIT',
  'buildWebSdk', 'xiDagEval', 'xiClientEval',
]);

const fnExports = Object.keys(t).filter((k) => typeof t[k] === 'function' && !SKIP.has(k)).sort();

/** מיפוי קריאות בטוחות לפונקציות נפוצות */
const FIXTURES = {
  xiSolve: [{ op: 'formula', args: { expr: '1+1' } }],
  xiQuorum: [[{ author: 'A', digest: 'd', result: 1 }, { author: 'B', digest: 'd', result: 1 }], 2],
  xiJobId: [{ op: 'formula', args: { expr: '1' } }],
  xiDigest: [1],
  bftThreshold: [1],
  bftPropose: ['abcd1234', { proposer: 'P', seq: 1 }],
  sha256: [Buffer.from('x')],
  hex: [Buffer.from('ab')],
  b32: [Buffer.from('x')],
  big: ['01'],
  encrypt: null,
  decrypt: null,
  lwwPickRecord: [{ v: 1, hlc: 1, digest: 'a' }, { v: 2, hlc: 2, digest: 'b' }],
  relayOutageUxPlan: [1, { minRelays: 2 }],
  mindGovernor: [{}],
  apiAuthCheck: [{ headers: {} }, {}],
  dagIsPure: [{ nodes: { a: { op: 'formula', args: { expr: '1' } } } }],
  latencyRace: [[{ ms: 0, value: 'a' }]],
  swarmPickPath: [[{ id: 'p1', score: 1 }], { seed: 's' }],
  offlineCommitPlan: null, // inline in wrapDualExport
};

const SCENARIOS = [
  { file: 'scenario-crypto-aead-dual.test.js', body: `
const crypto = require('node:crypto');
const { loadBoth, callSafe, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const plain = crypto.randomBytes(64);
const aad = 'sc:dual:aead';
const c1 = callSafe(d.encrypt, [plain, aad]);
const c2 = callSafe(s.encrypt, [plain, aad]);
ok('dual encrypt both ok', c1.ok && c2.ok);
const o1 = callSafe(d.decrypt, [c1.v, aad]);
const o2 = callSafe(s.decrypt, [c2.v, aad]);
ok('dual decrypt roundtrip dump', o1.ok && o1.v.equals(plain));
ok('dual decrypt roundtrip sdk', o2.ok && o2.v.equals(plain));
summary('SCENARIO-CRYPTO-AEAD-DUAL');` },
  { file: 'scenario-xi-quorum-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const prog = { op: 'formula', args: { expr: '2+2' } };
const r1 = d.xiSolve(prog);
const r2 = s.xiSolve(prog);
ok('dual xiSolve same result', r1.result === r2.result);
const agree = [{ author: 'A', digest: r1.digest, result: r1.result }, { author: 'B', digest: r1.digest, result: r1.result }];
ok('dual quorum dump', d.xiQuorum(agree, 2).ok);
ok('dual quorum sdk', s.xiQuorum(agree, 2).ok);
summary('SCENARIO-XI-QUORUM-DUAL');` },
  { file: 'scenario-bft-roundtrip-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const dig = d.hex(d.sha256(Buffer.from('dual-bft'))).slice(0, 32);
const p1 = d.bftPropose(dig, { proposer: 'P', seq: 1 });
const p2 = s.bftPropose(dig, { proposer: 'P', seq: 1 });
ok('dual bft propose', p1.ok && p2.ok);
const v1 = d.bftVote(p1, 'A', 'prepare');
const v2 = s.bftVote(p2, 'A', 'prepare');
ok('dual bft vote', v1 && v2);
summary('SCENARIO-BFT-DUAL');` },
  { file: 'scenario-skydb-shard-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const a = d.skydbShardOf('user:1', ['p1','p2'], 8);
const b = s.skydbShardOf('user:1', ['p1','p2'], 8);
ok('dual shard deterministic', a.shard === b.shard && a.owner === b.owner);
summary('SCENARIO-SKYDB-SHARD-DUAL');` },
  { file: 'scenario-roam-hash-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const rec = d.roamRecord('demo', { url: 'https://x' }, 1, null, 1000);
const rec2 = s.roamRecord('demo', { url: 'https://x' }, 1, null, 1000);
ok('dual roamRecord', JSON.stringify(rec) === JSON.stringify(rec2));
ok('dual roamHash', d.roamHash(rec) === s.roamHash(rec2));
summary('SCENARIO-ROAM-DUAL');` },
  { file: 'scenario-receipt-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const b1 = d.makeReceiptBody('dag', { n: 1 }, 42);
const b2 = s.makeReceiptBody('dag', { n: 1 }, 42);
ok('dual receipt id shape', b1.id && b2.id && b1.id.length === b2.id.length);
ok('dual verify receipt', d.verifyReceiptBody(b1, 42) && s.verifyReceiptBody(b2, 42));
summary('SCENARIO-RECEIPT-DUAL');` },
  { file: 'scenario-hybrid-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const w1 = d.hybridWrap(Buffer.from('dual'), { ecdhSeed: 'a', kemSeed: 'b', encSeed: 'c' });
const w2 = s.hybridWrap(Buffer.from('dual'), { ecdhSeed: 'a', kemSeed: 'b', encSeed: 'c' });
ok('dual hybrid wrap', w1.ok && w2.ok);
ok('dual no kemSk leak', !w1.pkg.kemSk && !w2.pkg.kemSk);
summary('SCENARIO-HYBRID-DUAL');` },
  { file: 'scenario-zk-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const sec = 'DUAL-ZK-SECRET';
const p1 = d.zkAuthProve(sec, { nonce: 'n', context: 'c' });
const p2 = s.zkAuthProve(sec, { nonce: 'n', context: 'c' });
ok('dual zk prove', p1.transcript && p2.transcript);
ok('dual zk verify', d.zkAuthVerify(p1.transcript, { checkSecret: sec }).ok);
summary('SCENARIO-ZK-DUAL');` },
  { file: 'scenario-lww-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const a = { v: 1, hlc: 5, digest: 'a' };
const b = { v: 2, hlc: 10, digest: 'b' };
ok('dual lww same winner', d.lwwPickRecord(a, b).winner.v === s.lwwPickRecord(a, b).winner.v);
summary('SCENARIO-LWW-DUAL');` },
  { file: 'scenario-merkle-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const leaves = ['a','b','c'].map((x) => d.sha256(Buffer.from(x)));
const t1 = d.merkleTreeFromLeaves(leaves);
const t2 = s.merkleTreeFromLeaves(leaves);
ok('dual merkle root', String(t1.root) === String(t2.root));
summary('SCENARIO-MERKLE-DUAL');` },
  { file: 'scenario-offline-queue-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const key = Buffer.from('offline-hmac-demo');
const c1 = d.offlineCommitPlan({ k: 1 }, [], { hmacKey: key });
const c2 = s.offlineCommitPlan({ k: 1 }, [], { hmacKey: key });
ok('dual offline queue depth', c1.queue.length === c2.queue.length);
summary('SCENARIO-OFFLINE-DUAL');` },
  { file: 'scenario-capsule-attest-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const emb = { b64: Buffer.from('capsule').toString('base64') };
const a1 = d.capsuleAttest(emb, { syncId: 'SC-DUAL' });
const a2 = s.capsuleAttest(emb, { syncId: 'SC-DUAL' });
ok('dual capsule attest digest', a1.digest === a2.digest);
summary('SCENARIO-CAPSULE-ATTEST-DUAL');` },
  { file: 'scenario-mind-cost-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const c1 = d.mindEstimateCost({ model: 'small', tokens: 100 });
const c2 = s.mindEstimateCost({ model: 'small', tokens: 100 });
ok('dual mind cost', c1.usd === c2.usd);
summary('SCENARIO-MIND-COST-DUAL');` },
  { file: 'scenario-publish-plan-dual.test.js', body: `
const path = require('node:path');
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const root = path.join(__dirname, '..', '..', '..');
const p1 = d.inferProjectPublishPlan(root);
const p2 = s.inferProjectPublishPlan(root);
ok('dual publish plan ready flag', p1.ready === p2.ready);
summary('SCENARIO-PUBLISH-PLAN-DUAL');` },
  { file: 'scenario-api-auth-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
process.env.SC_API_TOKEN = 'dual-token';
const req = { headers: { authorization: 'Bearer dual-token' } };
ok('dual api auth dump', d.apiAuthCheck(req, { required: true }).ok);
ok('dual api auth sdk', s.apiAuthCheck(req, { required: true }).ok);
delete process.env.SC_API_TOKEN;
summary('SCENARIO-API-AUTH-DUAL');` },
  { file: 'scenario-consensus-branches-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const ev = d.xiSolve({ op: 'formula', args: { expr: '3+3' } });
const tr = d.branchTrace(ev);
const c1 = d.consensusBranches([{ ...tr, author: 'p1' }, { ...tr, author: 'p2' }], 2);
const c2 = s.consensusBranches([{ ...tr, author: 'p1' }, { ...tr, author: 'p2' }], 2);
ok('dual consensus', c1.ok === c2.ok && c1.fork === c2.fork);
summary('SCENARIO-CONSENSUS-DUAL');` },
  { file: 'scenario-space-quorum-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const g1 = d.spaceCatalogQuorumGate({ votes: 3, need: 2 });
const g2 = s.spaceCatalogQuorumGate({ votes: 3, need: 2 });
ok('dual space quorum', g1.ok === g2.ok);
summary('SCENARIO-SPACE-QUORUM-DUAL');` },
  { file: 'scenario-economy-settle-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const g1 = d.economySettleGate({ balance: 10, due: 3 });
const g2 = s.economySettleGate({ balance: 10, due: 3 });
ok('dual economy settle', g1.ok === g2.ok);
summary('SCENARIO-ECONOMY-DUAL');` },
  { file: 'scenario-assign-submind-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const now = Date.now();
const jobs = [{ id: 'j1' }];
const workers = { 'v:1': { ts: now, visitor: true, sla: 60 } };
const a1 = d.assignSubMindWorkers(jobs, workers, { now });
const a2 = s.assignSubMindWorkers(jobs, workers, { now });
ok('dual submind n', a1.n === a2.n);
summary('SCENARIO-SUBMIND-DUAL');` },
  { file: 'scenario-lattice-doors-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const c1 = d.latticeWaveDoorsCatalog();
const c2 = s.latticeWaveDoorsCatalog();
ok('dual doors catalog length', c1.length === c2.length);
summary('SCENARIO-LATTICE-DOORS-DUAL');` },
  { file: 'scenario-p0-relay-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const r1 = d.relayOutageUxPlan(1, { minRelays: 2 });
const r2 = s.relayOutageUxPlan(1, { minRelays: 2 });
ok('dual relay outage', r1.relaysUp === r2.relaysUp && r1.ok === r2.ok);
summary('SCENARIO-P0-RELAY-DUAL');` },
  { file: 'scenario-blindsum-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const r1 = d.xiSolve({ op: 'blindSum', args: { values: [1,2,3], key: 'k' } });
const r2 = s.xiSolve({ op: 'blindSum', args: { values: [1,2,3], key: 'k' } });
ok('dual blindSum scheme', r1.result?.scheme === r2.result?.scheme);
summary('SCENARIO-BLINDSUM-DUAL');` },
  { file: 'scenario-dag-id-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const dag = { nodes: { a: { op: 'formula', args: { expr: '9' } } }, output: 'a' };
ok('dual xiDagId', d.xiDagId(dag) === s.xiDagId(dag));
ok('dual xiDagPlan len', d.xiDagPlan(dag).length === s.xiDagPlan(dag).length);
summary('SCENARIO-DAG-ID-DUAL');` },
  { file: 'scenario-state-digest-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const st = { a: 1, b: 2 };
ok('dual stateDigest', d.stateDigest(st) === s.stateDigest(st));
summary('SCENARIO-STATE-DIGEST-DUAL');` },
  { file: 'scenario-hlc-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const h1 = d.hlcNow();
const h2 = s.hlcNow();
ok('dual hlcNow numbers', typeof h1 === 'number' && typeof h2 === 'number');
const o1 = d.hlcObserve(h1, h2);
const o2 = s.hlcObserve(h1, h2);
ok('dual hlcObserve', JSON.stringify(o1) === JSON.stringify(o2));
summary('SCENARIO-HLC-DUAL');` },
  { file: 'scenario-schnorr-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const ev1 = d.signEvent(1, [['d', 'dual']], 'body');
const ev2 = s.signEvent(1, [['d', 'dual']], 'body');
ok('dual sign verify dump', d.verifyEvent(ev1));
ok('dual sign verify sdk', s.verifyEvent(ev2));
summary('SCENARIO-SCHNORR-DUAL');` },
  { file: 'scenario-covert-wrap-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const w1 = d.covertHttpWrap(Buffer.from('{}'), { seed: 'cov' });
const w2 = s.covertHttpWrap(Buffer.from('{}'), { seed: 'cov' });
ok('dual covert wrap', w1.ok && w2.ok);
summary('SCENARIO-COVERT-DUAL');` },
  { file: 'scenario-zero-trust-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const z1 = d.zeroTrustBundle({ syncId: 'SC', pubkey: 'pk' });
const z2 = s.zeroTrustBundle({ syncId: 'SC', pubkey: 'pk' });
ok('dual zero trust', z1.ok === z2.ok);
summary('SCENARIO-ZERO-TRUST-DUAL');` },
  { file: 'scenario-prefetch-gate-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const g1 = d.budgetPrefetchGate({ spentUsd: 0.1, capUsd: 1, calls: 1, capCalls: 5 });
const g2 = s.budgetPrefetchGate({ spentUsd: 0.1, capUsd: 1, calls: 1, capCalls: 5 });
ok('dual prefetch gate', g1.ok === g2.ok);
summary('SCENARIO-PREFETCH-DUAL');` },
  { file: 'scenario-snapshot-fresh-dual.test.js', body: `
const { loadBoth, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const g1 = d.snapshotFreshnessGate({ ageMs: 1000, maxMs: 5000 });
const g2 = s.snapshotFreshnessGate({ ageMs: 1000, maxMs: 5000 });
ok('dual snapshot fresh', g1.ok === g2.ok);
summary('SCENARIO-SNAPSHOT-DUAL');` },
];

const outDir = path.join(__dirname, '..', 'suites', 'extended');
fs.mkdirSync(outDir, { recursive: true });

// נקה קבצים ישנים שנוצרו ע"י גנרטור
for (const f of fs.readdirSync(outDir)) {
  if (f.endsWith('.test.js')) fs.unlinkSync(path.join(outDir, f));
}

let n = 0;

function wrapDualExport(name, idx) {
  const fix = FIXTURES[name];
  let callBlock;
  if (name === 'offlineCommitPlan') {
    callBlock = `const key = Buffer.from('offline-hmac-demo');
const r1 = callSafe(d[name], [{ set: 'x' }, [], { hmacKey: key }]);
const r2 = callSafe(s[name], [{ set: 'x' }, [], { hmacKey: key }]);`;
  } else if (name === 'encrypt') {
    callBlock = `const r1 = callSafe(d[name], [Buffer.from('x'), 'sc:dual:aad']);
const r2 = callSafe(s[name], [Buffer.from('x'), 'sc:dual:aad']);`;
  } else if (name === 'decrypt') {
    callBlock = `const ct = d.encrypt(Buffer.from('x'), 'sc:dual:aad');
const r1 = callSafe(d[name], [ct, 'sc:dual:aad']);
const r2 = callSafe(s[name], [ct, 'sc:dual:aad']);`;
  } else if (fix) {
    const argsJson = JSON.stringify(fix);
    callBlock = `const args = ${argsJson};
const r1 = callSafe(d[name], args);
const r2 = callSafe(s[name], args);`;
  } else {
    callBlock = `const r1 = callSafe(d[name], []);
const r2 = callSafe(s[name], []);`;
  }
  return `#!/usr/bin/env node
'use strict';
const { loadBoth, callSafe, ok, summary } = require('../../helpers/dual-harness');
const { dump: d, sdk: s } = loadBoth();
const name = '${name}';
ok('dump has ' + name, typeof d[name] === 'function');
ok('sdk has ' + name, typeof s[name] === 'function');
${callBlock}
ok('dual ${name} same outcome class', r1.ok === r2.ok);
summary('DUAL-${String(idx).padStart(3, '0')}-${name}');
`;
}

// 30 תרחישים
for (const sc of SCENARIOS) {
  const fp = path.join(outDir, sc.file);
  fs.writeFileSync(fp, `#!/usr/bin/env node\n'use strict';\n${sc.body.trim()}\n`, 'utf8');
  n++;
  console.log('wrote', sc.file);
}

// 90 בדיקות יחידה dual לפי export
const pick = fnExports.filter((e) => !SCENARIOS.some((s) => s.file.includes(e)));
const forDual = pick.slice(0, 90);
for (let i = 0; i < forDual.length; i++) {
  const name = forDual[i];
  const file = `dual-export-${String(i + 1).padStart(3, '0')}-${name}.test.js`;
  fs.writeFileSync(path.join(outDir, file), wrapDualExport(name, i + 1), 'utf8');
  n++;
}

console.log('\\nGenerated', n, 'extended test files in', outDir);
