// FILE: _St/IG_StYellow/test_ext_account_shuffle_v311.js | ROLE: 수집계정 수동/자동 섞기·Chrome 공통 이력·IG_DBG 콘솔 억제 회귀
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const vm = require('vm');

const root = __dirname;
const side = fs.readFileSync(path.join(root, 'side.js'), 'utf8');
const html = fs.readFileSync(path.join(root, 'side.html'), 'utf8');
const content = fs.readFileSync(path.join(root, 'content_instagram.js'), 'utf8');
const background = fs.readFileSync(path.join(root, 'background.js'), 'utf8');
const inject = fs.readFileSync(path.join(root, 'js', 'instagram_inject.js'), 'utf8');

function extractFunction(source, name) {
  const start = source.indexOf(`function ${name}(`);
  assert.ok(start >= 0, `${name} must exist`);
  const open = source.indexOf('{', start);
  let depth = 0;
  for (let i = open; i < source.length; i += 1) {
    if (source[i] === '{') depth += 1;
    else if (source[i] === '}') {
      depth -= 1;
      if (depth === 0) return source.slice(start, i + 1);
    }
  }
  throw new Error(`${name} closing brace not found`);
}

// UI placement: manual shuffle immediately before fold square; auto shuffle immediately after auto-run checkbox group.
const headPos = html.indexOf('id="igblue_account_queue_shuffle"');
const foldPos = html.indexOf('class="igblue-fold-icon"', headPos);
assert.ok(headPos >= 0 && foldPos > headPos, 'manual shuffle button must be before fold square');
const autoUsePos = html.indexOf('id="igblue_auto_py_down"');
const autoShufflePos = html.indexOf('id="igblue_auto_shuffle"');
const startPos = html.indexOf('id="igblue_auto_py_start"');
assert.ok(autoUsePos >= 0 && autoShufflePos > autoUsePos && startPos > autoShufflePos, 'auto shuffle must sit between auto-run checkbox and start');

// Common Chrome storage, not site localStorage, owns shuffle history/toggle.
assert.ok(side.includes('IGBLUE/account_queue_shuffle_v311'));
assert.ok(side.includes('IGBLUE/account_queue_auto_shuffle_v311'));
assert.ok(side.includes('chrome.storage.local.get(keys'));
assert.ok(side.includes('chrome.storage.local.set(payload'));
assert.ok(side.includes('schema: "igblue_account_shuffle_history_v311"'));
assert.ok(side.includes('current_order: after, history'));
assert.ok(side.includes('IGBLUE_ACCOUNT_SHUFFLE_HISTORY_LIMIT = 50'));

// Automatic shuffle is only at exact 1/N at a new user start, before fresh-start preview picks account 1.
const autoGate = side.indexOf('if (newSession && !resumeProgress && teAutoPyAutoShuffleChecked())');
const exactOne = side.indexOf('if (shuffleIndex === 1 && shuffleTotal > 1)', autoGate);
const autoCall = side.indexOf('teAutoPyShuffleAccountQueue("auto_start_1_of_n")', exactOne);
const preview = side.indexOf('const indexedStartPreview = (newSession || resumeProgress) ? teAutoPyFreshStartPreview()', autoCall);
assert.ok(autoGate >= 0 && exactOne > autoGate && autoCall > exactOne && preview > autoCall);
assert.ok(side.includes('teAutoPySetAccountQueueIndex(1);'));
assert.ok(side.includes('AS-SH-01'));

// Fisher-Yates helper must produce a different order even when RNG would keep every element in place.
const sandbox = {
  teAutoPyNormalizeAccountQueueEntries(entries) {
    return (entries || []).map((x) => ({ username: String(x.username), deleted: !!x.deleted, enabled: x.enabled !== false }));
  },
  Math,
  Number,
  Object,
};
vm.createContext(sandbox);
vm.runInContext(`${extractFunction(side, 'teAutoPyShuffleAccountQueueEntries')};`, sandbox);
const shuffle = vm.runInContext('teAutoPyShuffleAccountQueueEntries', sandbox);
const input = [{username:'a'}, {username:'b'}, {username:'c'}, {username:'d'}];
const output = shuffle(input, () => 0.999999);
assert.deepStrictEqual(Array.from(output, x => x.username).sort(), ['a','b','c','d']);
assert.notDeepStrictEqual(Array.from(output, x => x.username), ['a','b','c','d']);
assert.deepStrictEqual(input.map(x => x.username), ['a','b','c','d'], 'shuffle must not mutate caller entries');

// Repetitive IG_DBG console lines are muted while debug snapshots remain available.
for (const [name, src] of [['side', side], ['content', content], ['background', background]]) {
  assert.ok(src.includes('const IG_DBG_CONSOLE_ON = 0;'), `${name} console flag missing`);
  assert.ok(src.includes('IG_DBG_CONSOLE_ON) console.log(`[IG_DBG]'), `${name} IG_DBG console must be gated`);
}
assert.ok(inject.includes('IG_DBG_CONSOLE_ON=0'));
assert.ok(inject.includes('IG_DBG_CONSOLE_ON&&console.log(`[IG_DBG][inject]'));
assert.ok(side.includes('localStorage.setItem("IG_dbg/side.js/_last", out);'), 'debug snapshot storage retained');

console.log('test_ext_account_shuffle_v311: OK');
