// FILE: _St/IG_StYellow/test_ext_immediate_top_download.js | ROLE: IG_StYellow 회귀 테스트
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const assert = require('assert');

const root = __dirname;
const side = fs.readFileSync(path.join(root, 'side.js'), 'utf8');
const inject = fs.readFileSync(path.join(root, 'js', 'instagram_inject.js'), 'utf8');
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8'));

assert.strictEqual(manifest.version, '2.3.8.3130');
assert.strictEqual(manifest.version_name, '2.3.8.313');
assert.ok(side.includes('const IGBLUE_PACKAGE_VERSION = IGBLUE_BUILD_TAG;'));

// Top 3이라도 첫 후보 No001만 준비되면 먼저 다운로드하고, 완료 후 다음 후보 탐색으로 돌아간다.
for (const token of [
  'let requiredLoadCount = Math.max(1, targetNo || 1);',
  '신규 대상 즉시 선정',
  'first_pending_snapshot',
  'S31_SNAPSHOT_DIRECT_NEXT',
  'return_to_swipe_search',
  'S32_CONTINUE_SEARCH',
  'swipe_for_next_pending_candidate',
  'continue_after_immediate_download'
]) assert.ok(side.includes(token), `missing immediate-download token: ${token}`);
assert.ok(!side.includes('const requiredLoadCount = Math.max(1, targetLimit, targetNo || 0);'));
assert.ok(side.includes('const uiTargetCount = Math.max(1'));
assert.ok(side.includes('target_count: uiTargetCount'), 'UI must keep Top target while loading the next single candidate');

// 이미 저장 완료된 shortcode는 순위가 재정렬돼도 다시 다운로드하지 않는다.
const helperStart = side.indexOf('function teAutoPyCompletedPostCount()');
const helperEnd = side.indexOf('function teAutoPyRecordCompletedPost(shortcode, reason)', helperStart);
assert.ok(helperStart >= 0 && helperEnd > helperStart, 'pending-rank helper block missing');
const pendingCtx = {
  Set,
  teAutoPyStats: {completed_post_count: 1, completed_shortcodes: ['DONE001']},
  teAutoPyUiState: {completed_count: 1}
};
vm.createContext(pendingCtx);
vm.runInContext(`${side.slice(helperStart, helperEnd)}; this.pending=teAutoPyFirstPendingRankNo;`, pendingCtx);
assert.strictEqual(pendingCtx.pending([{code:'DONE001'}, {code:'NEW002'}]), 2);
assert.strictEqual(pendingCtx.pending([{code:'DONE001'}]), 0);
assert.strictEqual(pendingCtx.pending([{code:'NEW001'}]), 1);

// 완전한 snapshot은 Main 시각 No와 카드 Current를 기다리지 않고 AD-03 직접 대상이 된다.
for (const token of [
  'function teAutoPySnapshotDirectReady(media, rankNo)',
  'snapshot_shortcode_owner_media_ready',
  'skip_main_preload',
  'skip_main_download_point_wait',
  'use_snapshot_without_main_visual_current'
]) assert.ok(side.includes(token), `missing snapshot-direct token: ${token}`);

// 후보가 없을 때 swipe는 날짜/페이지 끝까지 계속하며, 끝 근거가 생긴 뒤에만 다음 계정으로 넘긴다.
for (const token of [
  'date_filter_range_exhausted_empty',
  'date_filter_range_exhausted',
  'current_url_end_reached',
  'teAutoPySkipCurrentAccountAndAdvanceIfAny',
  'teAutoPyAdvanceAccountQueueIfAny'
]) assert.ok(side.includes(token), `missing exhaustion/advance token: ${token}`);

// 파일 끝의 IIFE 밖 단독 호출은 ReferenceError 원인이므로 없어야 한다. 내부 정상 호출은 유지한다.
assert.ok(inject.includes('function teMainEnsureHoverAccountStyle()'));
assert.ok(inject.includes('teMainEnsureHoverAccountStyle();'));
assert.ok(!inject.trimEnd().endsWith('teMainEnsureHoverAccountStyle();'));
const standaloneFinal = /\}\)\(\);\s*teMainEnsureHoverAccountStyle\(\);\s*$/m;
assert.ok(!standaloneFinal.test(inject), 'out-of-scope final hover-style call remains');

console.log('OK test_ext_immediate_top_download v272');
