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 background = fs.readFileSync(path.join(root, 'background.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.276');
assert.strictEqual(manifest.version_name, '2.3.8.276');
assert.ok(side.includes('const IGBLUE_PACKAGE_VERSION = "v276";'));

// Current values become the defaults.
for (const token of [
  'barHeight: 3', 'itemGap: 4', 'textBarGap: 2', 'itemHeight: 18',
  'function teThumbWidthDefault() { return 200; }',
  'label: "Views", max: 1000000',
  'label: "Likes", max: 100000',
  'label: "Comments", max: 10000',
  'label: "Reposts", max: 10000'
]) assert.ok(side.includes(token), `missing visual default: ${token}`);

// Side/Main must remain independent through load and save for metric and date settings.
assert.ok(side.includes('"main" in n && (e.items[r].main = n.main !== false);'));
assert.ok(side.includes('"main" in n && (e.levels[r].main = n.main !== false);'));
assert.strictEqual((side.match(/main: i\?\.main !== false,/g) || []).length, 2);
assert.ok(!side.includes('e.items[r].main = e.items[r].side;'));
assert.ok(!side.includes('e.levels[r].main = e.levels[r].side;'));
assert.ok(!side.includes('main: i?.side !== false,'));

// MAIN world must use Main, while Side continues to use Side.
assert.ok(inject.includes('if(!i||!1===i.enabled||i.main===false)return;'));
assert.ok(inject.includes('a&&!1!==a.enabled&&a.main!==false&&'));
assert.ok(side.includes('if (!t.enabled || !t.side || !Number.isFinite(Number(r))'));
assert.ok(side.includes('if (!a || !a.enabled || !a.side) return;'));

// Visual settings must persist in chrome.storage and reach content/Main on changes.
assert.ok(background.includes('msg.type === "IGBLUE_SAVE_VISUAL_SETTINGS"'));
assert.ok(background.includes('chrome.storage.local.set({"ndy_ig_visual_settings": settings}'));
assert.ok(content.includes('function postDownloadProgress(value)'));
assert.ok(content.includes('msg.type === "ndy_down_progress"'));
assert.ok(content.includes('type: "igblue_download_progress"'));
assert.ok(background.includes('chrome.tabs.sendMessage(tabId, message'));
assert.ok(background.includes('const progressIdentity = {'));

// The old button-size meaning is removed from the settings UI and old active-target styling is disabled.
assert.ok(side.includes('다운로드 상태 강조'));
assert.ok(side.includes('MP4 다운로드 중'));
assert.ok(!side.includes('다운버튼 크게 표시'));
assert.ok(!side.includes('버튼 가로폭을 50%로 줄여'));
assert.ok(inject.includes('return{enabled:false,color:teVColor(e.color,"#ff0000")'));

// Execute the isolated v272 patch helpers to verify state classification and configuration.
const marker = '// IGblue v272: configured Main DOM download state emphasis.';
const patchIndex = inject.indexOf(marker);
assert.ok(patchIndex >= 0, 'v272 download status patch missing');
const patch = inject.slice(patchIndex);
const listeners = {};
const fakeDocument = {
  getElementById: () => null,
  createElement: () => ({
    id: '', className: '', textContent: '', style: {},
    classList: { add() {}, remove() {} },
    appendChild() {}, remove() {}, querySelector() { return null; }, querySelectorAll() { return []; }
  }),
  querySelectorAll: () => [],
  head: { appendChild() {} },
  documentElement: { appendChild() {} }
};
const fakeWindow = {
  addEventListener(type, fn) { (listeners[type] ||= []).push(fn); }
};
fakeWindow.window = fakeWindow;
const context = {
  window: fakeWindow,
  document: fakeDocument,
  CSS: { escape: (v) => String(v) },
  MutationObserver: class { observe() {} },
  Map, Set, Number, String, Array, Math, Date,
  setInterval: () => 0,
  console
};
vm.runInNewContext(patch, context, { filename: 'instagram_download_status_v272.js' });
const helper = fakeWindow.__IGBLUE_V272_DOWNLOAD_STATUS_TEST__;
assert.ok(helper, 'v272 helper export missing');
assert.deepStrictEqual(JSON.parse(JSON.stringify(helper.normalizeConfig({enabled:true, glow:true, color:'#123456', borderWidth:4, radius:3, glowOpacity:.45, scale:1.2}))), {
  enabled:true, glow:true, color:'#123456', borderWidth:4, radius:3, glowOpacity:.45, scale:1.2
});
assert.strictEqual(helper.normalizeAsset('video'), 'mp4');
assert.strictEqual(helper.normalizeAsset('jpeg'), 'jpg');
assert.strictEqual(helper.statusFromPayload({status:'start'}), 'running');
assert.strictEqual(helper.statusFromPayload({status:'done'}), 'success');
assert.strictEqual(helper.statusFromPayload({status:'already_done'}), 'success');
assert.strictEqual(helper.statusFromPayload({status:'partial'}), 'warning');
assert.strictEqual(helper.statusFromPayload({status:'error'}), 'error');
assert.strictEqual(helper.statusFromPayload({status:'feature_check'}), '');
assert.ok(helper.updateState({status:'start', shortcode:'ABC123', asset_kind:'video', percent:22}));
assert.ok(helper.updateState({status:'done', shortcode:'ABC123', asset_kind:'video', percent:100}));
const snapshot = helper.snapshot();
assert.strictEqual(snapshot[0][0], 'ABC123');
assert.strictEqual(snapshot[0][1][0][0], 'mp4');
assert.strictEqual(snapshot[0][1][0][1].kind, 'success');
assert.strictEqual(helper.stateText('mp4', {kind:'running', percent:42}, false), 'MP4 다운로드 중 42%');
assert.strictEqual(helper.stateText('jpg', {kind:'success', already:false}, false), '✅ JPG');
assert.strictEqual(helper.stateText('jpg', {kind:'error'}, false), '⚠ JPG 다운로드 실패');

console.log('OK test_ext_visual_settings_download_status v272');
