// FILE: _St/IG_StYellow/test_ext_jpg_manual_original_v323.js | ROLE: 자동 MP4 전용 + 수동 JPG 원래 브라우저 다운로드 회귀
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 content = fs.readFileSync(path.join(root, 'content_instagram.js'), 'utf8');
const inject = fs.readFileSync(path.join(root, 'js', 'instagram_inject.js'), 'utf8');

function block(source, startToken, endToken) {
  const start = source.indexOf(startToken);
  const end = source.indexOf(endToken, start + startToken.length);
  assert.ok(start >= 0 && end > start, `block missing: ${startToken}`);
  return source.slice(start, end);
}

// 자동다운로드 후보는 MP4 하나뿐이다. 이미지 URL이 있어도 JPG가 후보로 들어가면 안 된다.
const kindsBlock = block(side, 'function teAutoPyDownloadableKindsFromCache(media)', 'function teAutoPyCacheTargetForMedia(media, rankNo)');
assert.ok(kindsBlock.includes('out.push("mp4")'));
assert.ok(!kindsBlock.includes('out.push("jpg")'));
assert.ok(!kindsBlock.includes('teDownloadImageSave'));
const sandbox = { teDownloadVideoSave: () => true, String, Object };
vm.createContext(sandbox);
vm.runInContext(`${kindsBlock}; this.kinds = teAutoPyDownloadableKindsFromCache;`, sandbox);
assert.deepStrictEqual(Array.from(sandbox.kinds({video_url:'https://cdn/x.mp4', img_origin:'https://cdn/x.jpg'})), ['mp4']);
assert.deepStrictEqual(Array.from(sandbox.kinds({img_origin:'https://cdn/x.jpg'})), []);

const cachePair = block(side, 'async function teAutoPyDownloadMediaPairFromCache(media, rankNo, cycleBase, reason)', 'async function teAutoPyTryCacheDownloadForRank(rankNo, list, ctx, cycleBase, reason)');
const directPair = block(side, 'async function teAutoPyDownloadMediaPairDirect(currentMedia, target, rankNo, cycleBase)', 'async function teAutoPyStopCycle(stage, base, code, detail, extra)');
assert.ok(cachePair.includes('for (const kind of ["mp4"])'));
assert.ok(directPair.includes('for (const kind of ["mp4"])'));
assert.ok(!cachePair.includes('for (const kind of ["mp4", "jpg"])'));
assert.ok(!directPair.includes('for (const kind of ["mp4", "jpg"])'));

// 자동 완료/진행 UI에 JPG 개수 문구가 다시 들어오면 실패한다.
const unified = block(side, 'function teUnifiedProgressText()', 'function teAutoPyStatsText()');
assert.ok(unified.includes('`${label}: 게시물 ${posts}개 · MP4 ${mp4}개`'));
assert.ok(!unified.includes('JPG'));

// 자동 상태 객체/과거 다운로드 완료목록에도 JPG가 다시 들어오지 않는다.
const autoHead = side.slice(0, side.indexOf('function teUnifiedProgressText()'));
assert.ok(!autoHead.match(/jpg_count:\s*0/));
assert.ok(!autoHead.match(/jpg_total_ms:\s*0/));
const targetRead = block(side, 'function teAutoPyTargetReadCheck(target, media, rankNo)', 'function teAutoPyMediaNorm(mediaType)');
assert.ok(!targetRead.includes('teAutoPySourceUrlForMedia(m, {}, "jpg")'));
assert.ok(targetRead.includes('const hasDirectSource = !!videoUrl'));
const downNormalize = block(side, 'function teNormalizeDownItems(items)', 'let teDownItems = []');
assert.ok(downNormalize.includes('!/video|mp4/.test(kind)'));
assert.ok(!downNormalize.includes('image|jpg|jpeg'));
const downKey = block(side, 'function teDownItemKey(item)', 'function teRankLabelFromItem(item)');
const normalizeSandbox = { String, Number, Array, Object, JSON, Set };
vm.createContext(normalizeSandbox);
vm.runInContext(`${downKey}; ${downNormalize}; this.normalize = teNormalizeDownItems;`, normalizeSandbox);
const normalizedItems = Array.from(normalizeSandbox.normalize([
  {shortcode:'V1', asset_kind:'video', file_name:'v1.mp4'},
  {shortcode:'J1', asset_kind:'image', file_name:'j1.jpg'}
]));
assert.strictEqual(normalizedItems.length, 1);
assert.strictEqual(normalizedItems[0].shortcode, 'V1');
assert.strictEqual(normalizedItems[0].asset_kind, 'video');
const downRender = block(side, 'function teRenderDownList(items, account)', 'function teNormalizeDownTxt(text)');
assert.ok(downRender.includes('kind.textContent = "MP4"'));
assert.ok(!downRender.includes('? "JPG" : "MP4"'));

// Side 수동 JPG는 시작 강조 없이 MAIN 원래 downloader로 보내며 WS direct API를 타지 않는다.
const sideQ = block(side, 'async function Q(e, t, r, meta)', 'const TE_SIDE_THUMB_ROOT_MARGIN');
assert.ok(sideQ.includes('type: "igblue_manual_original_download"'));
assert.ok(sideQ.includes('asset_kind: "image"'));
assert.ok(sideQ.includes('silent: true'));
const imageClick = block(side, 'actions?.[0].addEventListener("click"', 'actions?.[1].addEventListener("click"');
assert.ok(imageClick.includes('Q(media.img_origin'));
assert.ok(!imageClick.includes('teNotifyManualDownloadStart'));

// Content는 Side 요청을 페이지 MAIN으로 그대로 넘긴다.
assert.ok(content.includes('if (msg.type === "igblue_manual_original_download")'));
assert.ok(content.includes('type: "igblue_manual_original_download"'));
assert.ok(content.includes('route: "main_original_download"'));

// MAIN의 JPG 클릭은 RTC/WS 판단 전에 원래 fetch→Blob→a.download 경로로 직행하고 상태 UI를 조용히 유지한다.
const mainQ = block(inject, 'function q(e,t,o,mediaIndex=0,totalMedia=1)', '/*\n * S(code)');
const imageBranch = mainQ.indexOf('kind==="image"||kind==="jpg"||kind==="jpeg"');
const rtcBranch = mainQ.indexOf('if(!teMainIsRtcConnected())');
const bgDispatch = mainQ.indexOf('document.dispatchEvent(new CustomEvent("ndy_ig_bg_down"');
assert.ok(imageBranch >= 0 && rtcBranch > imageBranch && bgDispatch > rtcBranch);
assert.ok(mainQ.includes('teMainOriginalManualDownload(e,o||t||"media",mediaIndex,totalMedia,"image",true);return'));
const original = block(inject, 'function teMainOriginalManualDownload(', 'window.addEventListener("message"');
for (const token of ['fetch(e,{})','window.URL.createObjectURL(e)','document.createElement("a")','n.download=`${stem}.${ext}`','window.URL.revokeObjectURL(t)']) assert.ok(original.includes(token), token);
assert.ok(original.includes('quiet=!!silent'));
assert.ok(original.includes('if(!quiet)'));

console.log('test_ext_jpg_manual_original_v323: OK');
