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

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

assert.strictEqual(manifest.version, '2.3.8.2910');
assert.strictEqual(manifest.version_name, '2.3.8.291');
assert.ok(side.includes('const IGBLUE_PACKAGE_VERSION = "v288";'));

// MAIN injection does not accumulate thumbnails as Base64 strings.
assert.ok(!inject.includes('new FileReader'));
assert.ok(!inject.includes('readAsDataURL'));
assert.ok(!inject.includes('.img_b64=i.result'));
assert.ok(inject.includes('delete e.img_b64'));

// Full post arrays are sent only on change or 30-second keepalive.
assert.ok(inject.includes('const TE_STATS_DISPATCH_KEEPALIVE_MS=30000'));
assert.ok(inject.includes('function teStatsDispatchSignature(e)'));
assert.ok(inject.includes('n===teStatsLastDispatchSignature&&o-teStatsLastDispatchAt<TE_STATS_DISPATCH_KEEPALIVE_MS'));

// v261 keeps every card DOM and unloads only image resources outside the observer margin.
assert.ok(side.includes('const teSideListState = { items: [], rankCtx: null, cards: new Map() };'));
assert.ok(side.includes('function teSideListRender(items)'));
assert.ok(side.includes('teSideListRender(items);'));
assert.ok(!side.includes('TE_SIDE_VIRTUAL_MAX_CARDS'));
assert.ok(!side.includes('teSideVirtualRange'));
assert.ok(!side.includes('teSideVirtualMeasuredRowHeight'));
assert.ok(!side.includes('window.scrollBy(0, delta)'));
assert.ok(!side.includes('window.addEventListener("scroll"'));

assert.ok(side.includes('const TE_SIDE_THUMB_ROOT_MARGIN = "900px 0px 1400px 0px";'));
assert.ok(side.includes('new IntersectionObserver'));
assert.ok(side.includes('teSideThumbLoad(img)'));
assert.ok(side.includes('teSideThumbUnload(img)'));
assert.ok(side.includes('img.removeAttribute("src");'));
assert.ok(side.includes('img.removeAttribute("srcset");'));
assert.ok(side.includes('teSideThumbObserver.observe(img);'));
assert.ok(side.includes('if (img.dataset.igblueIntersecting === "1") teSideThumbLoad(img);'));
assert.ok(side.includes('else teSideThumbLoad(img);'));
assert.ok(side.includes('teSideThumbObserver.unobserve(img)'));
assert.ok(side.includes('img.dataset.igblueFallbackSrc'));
assert.ok(!side.includes('TE_SIDE_THUMB_PLACEHOLDER'));
assert.ok(!side.includes('const teSrc = n?.img_b64'));
assert.ok(side.includes('loading="lazy" decoding="async" fetchpriority="low"'));

// Image load/unload never changes card geometry.
assert.ok(html.includes('aspect-ratio: 16 / 9;'));
assert.ok(html.includes('object-fit: cover;'));
assert.ok(html.includes('content-visibility: auto;'));
assert.ok(html.includes('contain: layout paint style;'));
assert.ok(!side.includes('naturalWidth'));
assert.ok(!side.includes('naturalHeight'));
assert.ok(!html.includes('.igblue-virtual-spacer'));

// Statistics snapshot/incremental logic remains unchanged.
assert.ok(side.includes('const TE_STATS_BATCH_SIZE = 100;'));
assert.ok(side.includes('let teStatsPendingSnapshot = null;'));
assert.ok(side.includes('while (teStatsPendingSnapshot)'));
assert.ok(side.includes('items: Array.isArray(info) ? info.map(teStatsCompactMedia) : []'));
assert.ok(side.includes('function teStatsTenMinuteBucket(observedAt)'));
assert.ok(side.includes('function teStatsChangedLocalItems(items, sourceUrl)'));
assert.ok(side.includes('for (const chunk of teStatsChunk(rows))'));
assert.ok(side.includes('if (!teStatsDrainRunning) teStatsObservationQueue = teDrainStatsPayloadQueue().catch(() => {});'));
assert.ok(!side.includes('teStatsObservationQueue.catch(() => {}).then(() => teDrainStatsPayloadQueue())'));

assert.ok(side.includes('function teStatsHourState(account, hourBucket)'));
assert.ok(side.includes('state.sent[shortcode]'));
assert.ok(side.includes('same_hour_no_change'));
assert.ok(side.includes('for (const chunk of teStatsChunk(pending))'));
assert.ok(side.includes('teStatsSaveHourState(account, state);'));
assert.ok(!side.includes('teLocalSet(key, hourBucket);'));

console.log('OK test_ext_memory_windowing v272');
