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 content = read('content_instagram.js');
const inject = read('js/instagram_inject.js');
const side = read('side.js');
const sideHtml = read('side.html');
const background = read('background.js');
const manifest = JSON.parse(read('manifest.json'));

assert.strictEqual(manifest.version, '2.3.8.276');
assert.strictEqual(manifest.version_name, '2.3.8.276');

// Right-side origin/status box is no longer shown directly.
assert.ok(content.includes('#IGBLUE_NoTop > .igblue-origin-panel'));
assert.ok(content.includes('#IGBLUE_NoTop > .igblue-status-box'));
assert.ok(content.includes('display:none !important'));

// A compact square diagnostic button lives next to the existing CDP gate.
assert.ok(content.includes("toggle.id = 'igblue_main_diag_toggle'"));
assert.ok(content.includes("toggle.textContent = '⋮'"));
assert.ok(content.includes("const r = cdp && cdp.getBoundingClientRect"));
assert.ok(content.includes("let left = r ? r.right + gap"));
assert.ok(content.includes("panel.id = 'igblue_main_diag_panel'"));
assert.ok(content.includes("width:174px"));
assert.ok(content.includes("max-height:58vh"));
assert.ok(content.includes("font:700 9px/1.26"));

// Hidden diagnostics still mirror the live original values.
for (const token of ['IGBLUE_OriginInfo', 'IGBLUE_SyncLog', 'IGBLUE_HoverStatus', 'IGBLUE_DevStatus']) {
  assert.ok(content.includes(`text('${token}'`), `missing diagnostic source: ${token}`);
}

// v268: Side rank is restored, while Main rank visuals and visible No navigation remain removed.
for (const token of ['Side Rank badge / No 000','teCreateRankBadgeSettings','teRankBadgeSettings','teApplySideRankLabel','igblue-side-rank','igblue-rank-badge']) {
  assert.ok(side.includes(token) || sideHtml.includes(token), `Side rank token missing: ${token}`);
}
assert.ok(side.includes('{ title: "순위"'), 'Side rank settings tab must exist');
for (const token of ['igblue-main-rank-row','igblue-main-kind-row','IGBLUE_NoNav_Current','IGBLUE_NoNav_Next','igblue-nav-box']) {
  assert.ok(!inject.includes(token), `Main rank visual token survived: ${token}`);
}
assert.ok(inject.includes('IGBLUE/internal_no_nav_state'), 'internal No navigation state missing');
assert.ok(inject.includes('function teMainNoNavCurrent') && inject.includes('function teMainNoNavNext'), 'internal Current/Next handlers missing');
assert.ok(inject.includes('document.querySelectorAll(".ndy_ins_info[data-code]")'), 'shortcode card lookup missing');
assert.ok(content.includes('igblueReadInternalNoNavState'), 'content internal state bridge missing');
assert.ok(!content.includes('querySelector("#IGBLUE_NoNav_'), 'content must not depend on visual No buttons');
const rankSettings=side.slice(side.indexOf('function teCreateRankBadgeSettings()'),side.indexOf('function teCreateActiveDownloadSettings()'));
assert.ok(!rankSettings.includes('mkCheck("Main"') && !rankSettings.includes('main:'), 'Side rank settings must not revive Main rank');

// v263: hovered Explore/Reels thumbnails have a distinct cyan selection state.
assert.ok(inject.includes('function teMainBindHoverAccountHost'));
assert.ok(inject.includes('teMainRefreshHoverAccountTargetFromCard(t,"data_code_relinked")'));
assert.ok(inject.includes('data-igblue-account-hover="1"'));
assert.ok(inject.includes('background:rgba(6,182,212,.94)'));

console.log('OK test_ext_main_ui v272');

assert.ok(side.includes('📊 통계 연동') && side.includes('📊 통계 단독'));

// v255: v249 자동 흐름 복구 / 현재 활성 탭만 사용 / Analyze8780 상태 버튼
assert.ok(sideHtml.includes('id="channel_lock"') && sideHtml.includes('현재 채널만'), '현재 채널만 UI가 있어야 함');
assert.ok(!sideHtml.includes('id="igblue_account_queue_disabled"'), '중복 목록 미사용 체크박스는 제거되어야 함');
assert.ok(sideHtml.includes('id="igblue_analyze8780_open"'), 'Analyze8780 열기 버튼이 있어야 함');
assert.ok(side.includes('chrome.runtime.sendMessage({ type: "IGBLUE_GET_TARGET_STATE" }'), '새 자동 세션 시작 시 현재 활성 Instagram 탭을 읽어야 함');
assert.ok(!side.includes('teAutoRunSessionActive') && !side.includes('teAutoRunTabId'), '폐기된 v250 fixed-tab 구현은 되살리면 안 됨');
assert.ok(side.includes('teAutoPySessionTarget') && side.includes('teAutoPyAcquireStartTab'), 'v261 시작 탭 보존 세션이 있어야 함');
assert.ok(!side.includes('teAutoPyAcquireFixedTabSession') && !side.includes('IGBLUE_GET_CONNECTED_TARGET_STATE'), '연결 탭 복구·고정 세션을 사용하면 안 됨');
assert.ok(side.includes('await teSleep(4500)') && !side.includes('account_dom_not_ready'), '계정 이동은 v249 대기 후 계속 흐름이어야 함');
assert.ok(side.includes('teAutoPyTryCacheDownloadForRank') && side.includes('allow_cache_fallback: false'), '캐시 함수는 유지하되 자동 시작 다운로드는 AD-03 이후여야 함');
assert.ok(side.includes('auto_snapshot_rank_target') && side.includes('set_internal_current_no_without_download'), 'Main 시각 No 없이 snapshot Current를 복구해야 함');
assert.ok(!side.includes('owner_account_mismatch') && !side.includes('target_account_mismatch'), 'v250 owner 강제중단 조건은 제거되어야 함');
assert.ok(background.includes('chrome.tabs.query({active:true, lastFocusedWindow:true}'), 'background는 현재 활성 탭 한 개만 읽어야 함');
assert.ok(!background.includes('chrome.tabs.query({url:['), 'Instagram 탭 전체 검색은 금지됨');
assert.ok(!background.includes('IGBLUE_GET_CONNECTED_TARGET_STATE'), '고정 연결 탭 조회 API는 제거되어야 함');


// v263: Analyze8780 is a real browser link and never retains a stale offline href.
assert.ok(sideHtml.includes('<a class="igblue-local-service-open" id="igblue_analyze8780_open" target="_blank" rel="noopener noreferrer" aria-disabled="true" tabindex="-1">8780 열기</a>'), 'Analyze8780 must be a native anchor');
assert.ok(!sideHtml.includes('<button class="igblue-local-service-open" id="igblue_analyze8780_open"'), 'legacy Analyze8780 JavaScript button must be removed');
assert.ok(side.includes('link.href = url;') && side.includes('link.removeAttribute("href");'), 'Analyze8780 href must update online and clear offline');
assert.ok(side.includes('link.setAttribute("aria-disabled", "false")') && side.includes('link.setAttribute("aria-disabled", "true")'), 'Analyze8780 link availability state missing');
assert.ok(!side.includes('chrome.tabs.create({ url: teAnalyze8780OpenUrl()') && !side.includes('window.open(teAnalyze8780OpenUrl()'), 'forced tab/window opening must be removed');

// v263: current-account add renders and clicks from one shared username state.
assert.ok(sideHtml.includes('id="igblue_account_url_add_current"') && sideHtml.includes('disabled>계정 확인중</button>'), 'current-account add must start unresolved and disabled');
assert.ok(side.includes('let teCurrentAccountAddTarget = { username: "", source: "", shortcode: "" };'), 'shared current-account target state missing');
assert.ok(side.includes('button.textContent = normalized ? `${normalized} 계정추가` : "계정 확인중";'), 'dynamic current-account button label missing');
assert.ok(side.includes('const username = teCurrentAccountAddTarget.username;'), 'click must use the exact rendered account state');
assert.ok(side.includes('type: "igblue_get_current_account_target"'), 'Side must request the current Main media target');
assert.ok(content.includes('function igblueReadCurrentAccountTarget()'), 'Main current-media resolver missing');
assert.ok(content.includes('msg.type === "igblue_get_current_account_target"'), 'current-media account bridge missing');
assert.ok(content.includes('active_download_target') && content.includes('viewport_current_card'), 'selected and currently visible media fallbacks missing');
assert.ok(side.includes('teAutoPySinglePayloadOwner()'), 'single-account Main fallback missing');
assert.ok(side.includes('return teAutoPySetCurrentAccountAddTarget("", "unresolved", "");'), 'unresolved account must clear the previous username');
