// FILE: _St/IG_StYellow/test_ext_main_ui.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 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.2910');
assert.strictEqual(manifest.version_name, '2.3.8.291');

// 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'));

// v282: MAIN 우상단은 CDP/탭/OWNER를 한 줄로 통합하고 _/□만 보조 아이콘으로 둔다.
assert.ok(content.includes("toggle.id = 'igblue_main_diag_toggle'"));
assert.ok(content.includes("toggle.textContent = 'CDP:X · 탭 0 · OWNER 없음'"));
assert.ok(content.includes("minimize.id = 'igblue_main_diag_minimize'"));
assert.ok(content.includes("detail.id = 'igblue_main_diag_expand'"));
assert.ok(content.includes("panel.id = 'igblue_main_diag_panel'"));
assert.ok(content.includes("pre.id = 'igblue_main_diag_text'"));
assert.ok(content.includes("width:340px"));
assert.ok(content.includes("max-height:68vh"));
assert.ok(content.includes("클릭: 전체 진단 복사"));
assert.ok(!content.includes('window.setInterval(igblueRefreshMainDiag, 700)'));
for (const token of ['탭 제어 v288','OWNER tab_id:','최근 탭 이력','IGBLUE_GET_TAB_CONTROL_STATE','data-yellow-tab-control-state']) {
  assert.ok(content.includes(token), `missing tab-control 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)') && background.includes('igblueWaitForNavigationReady') && content.includes('igblue_navigation_ready_probe') && !side.includes('account_dom_not_ready'), '계정 이동은 고정 대기 대신 Content/Main 준비 확인 후 계속해야 함');
assert.ok(side.includes('teAutoPyTryCacheDownloadForRank') && !side.includes('allow_cache_fallback: false'), 'v262 정상 snapshot/캐시 다운로드 회복 경로를 유지해야 함');
assert.ok(side.includes('AD-03-E01') && side.includes('[복구완료]') && side.includes('actual_conflict'), 'Main 누락은 복구하고 실제 대상 충돌만 중단해야 함');
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({}, function(tabs)') && background.includes('function igblueGetTabControlSnapshot(callback)'), 'v280 background must inspect Instagram tabs for OWNER recovery');
assert.ok(!background.includes('chrome.tabs.query({url:['), 'URL pattern bulk query remains unused; v280 filters queried tabs in memory');
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');

// v279aaaa: account add target is selected only by clicking the Side IG account label.
assert.ok(sideHtml.includes('id="igblue_account_url_add_current"') && sideHtml.includes('disabled>IG 계정 선택</button>'), 'account add must start in click-selection state');
assert.ok(side.includes('let teCurrentAccountAddTarget = { username: "", source: "", shortcode: "" };'), 'shared Side-click target state missing');
assert.ok(side.includes('button.textContent = normalized ? `${normalized} 계정추가` : "IG 계정 선택";'), 'dynamic Side-click account button label missing');
assert.ok(side.includes('const username = teCurrentAccountAddTarget.username;'), 'add click must use the exact selected account state');
assert.ok(side.includes('function teSelectSideAccountAddTarget(media, element)'));
assert.ok(side.includes('teAutoPySetCurrentAccountAddTarget(account, "side_account_click", shortcode);'));
assert.ok(side.includes('accountLabel.addEventListener("click"'));
assert.ok(!side.includes('type: "igblue_get_current_account_target"'), 'Side must not poll Main for account-add target');
assert.ok(!side.includes('teAutoPySinglePayloadOwner()'), 'automatic single-owner fallback must be removed');
assert.ok(!side.includes('teAutoPyStartCurrentAccountAddTracking'), 'automatic account tracking must be removed');
