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

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

assert.strictEqual(manifest.version, '2.3.8.2810');
assert.strictEqual(manifest.version_name, '2.3.8.281');
assert.ok(side.includes('const IGBLUE_PACKAGE_VERSION = "v281";'));

// 첫 후보는 Top 목표 전체를 먼저 채우지 않고 즉시 처리한다. 완료 후에는 다음 rank만 추가 로드한다.
assert.ok(side.includes('let requiredLoadCount = Math.max(1, targetNo || 1);'));
assert.ok(side.includes('후보를 발견하는 즉시 저장하고 목표가 남으면 다음 후보를 계속 탐색합니다.'));
assert.ok(side.includes('continue_after_immediate_download'));
assert.ok(side.includes('requiredLoadCount = Math.max(requested + 1'));
assert.ok(!side.includes('const requiredLoadCount = Math.max(1, targetLimit'));
assert.ok(!side.includes('const filterLoadNo ='));

// 완료 게시물은 shortcode 고유값으로만 증가하고 실제 완료 수가 목표에 도달해야 끝난다.
assert.ok(side.includes('completed_post_count'));
assert.ok(side.includes('completed_shortcodes'));
assert.ok(side.includes('function teAutoPyRecordCompletedPost(shortcode, reason)'));
assert.ok(side.includes('if (!list.includes(code)) list.push(code);'));
assert.ok(side.includes('(completed.length > 0 || alreadySaved.length > 0)'));
assert.ok(!side.includes('metadata_collected_done: metadataDone.length > 0')); // metadata-only는 자동 파일 완료가 아니다.
assert.ok(side.includes('const targetReached = completedCount >= targetCount;'));
assert.ok(side.includes('while (rankNo > 0 && rankNo <= maxRank && teAutoPyCompletedPostCount() < targetCount'));
assert.ok(!side.includes('const noMoreMedia = !!(exhausted || maxRank < targetCount);'));
assert.ok(side.includes('const noMoreMedia = !!exhausted;'));
assert.ok(side.includes('S32_CONTINUE_SEARCH'));
assert.ok(side.includes('swipe_for_next_pending_candidate'));
assert.ok(side.includes('S01_COMPLETED_SHORTCODE_SKIP'));
assert.ok(side.includes('completed_shortcode_not_downloaded_twice'));

// 기간 필터는 새로 로딩된 날짜가 전부 범위 밖이면 부분 대상을 처리하고 종료한다.
assert.ok(side.includes('new_media_all_older_than_filter'));
assert.ok(side.includes('consecutiveDateExhausted >= 1'));
assert.ok(side.includes('process_loaded_filtered_media'));
assert.ok(side.includes('date_filter_range_exhausted'));

// All time은 실제 게시물 끝까지 확인하고 부분 대상이 있으면 다운로드한다.
assert.ok(side.includes('current_url_end_reached'));
assert.ok(side.includes('게시물 처음까지 확인했습니다. 있는 대상만 다운로드합니다.'));
assert.ok(side.includes('action: hasPartial ? "process_loaded_media" : "skip_current_account_or_finish"'));

// 상태 제목은 탐색, Swipe, 초 단위 대기, 다운로드, 더이상없음, 자동종료를 구분한다.
for (const token of [
  '다운로드 · 게시물 탐색중',
  '다운로드 · Swipe 중',
  '초 대기중 · 완료',
  '다운로드 중',
  '더이상없음 · 자동종료',
  '다운로드 완료 · 목표',
  '자동종료'
]) assert.ok(side.includes(token), `missing auto status token: ${token}`);
assert.ok(side.includes('Math.round(((d / t) * 100) / 10) * 10'));

// 계정 목록 n/n 완료와 다음 실행 0/n 초기화는 실제 v257에서 실패가 확인됐다.
// 이 파일은 해당 동작을 완료 회귀로 보호하지 않으며 test_list.md의 FAIL·검증대기 항목으로 관리한다.
assert.ok(side.includes('function teAutoPyFinishAccountQueue'));
assert.ok(side.includes('function teAutoPyAdvanceAccountQueueIfAny'));

// 자동 종료 후 제목과 목록 상태를 즉시 다시 렌더링한다.
const autoEndStart = side.indexOf('function teAutoPyFinalizeAutomaticEnd');
const autoEndEnd = side.indexOf('function teAutoPyFinishAccountQueue', autoEndStart);
const autoEndBlock = side.slice(autoEndStart, autoEndEnd);
assert.ok(autoEndBlock.includes('automatic_end: true'));
assert.ok(autoEndBlock.includes('teAutoPyUpdateAccountQueueStatus()'));
assert.ok(autoEndBlock.includes('teAutoPyAnnounce("자동종료"'));

console.log('OK test_ext_auto_target_completion v272');
