import fs from 'node:fs'
const read=p=>fs.readFileSync(p,'utf8')
const failures=[];const check=(ok,msg)=>{if(!ok)failures.push(msg)}

const pref=read('apps/web/src/preferences/screenPreferenceStore.ts')
for(const token of ["kbx.screen.preference.v2","KbxScreenPreferenceScopeKey","screenVersion","MAX_STORED_BYTES","MAX_SEARCH_KEYS","try { localStorage.setItem","try { raw = localStorage.getItem"])
  check(pref.includes(token),`screen preference hardening missing: ${token}`)
check(pref.includes('encodeURIComponent(scope)')&&pref.includes('encodeURIComponent(screenId)')&&pref.includes('encodeURIComponent(screenVersion)'),'screen preference key must be scope + ScreenId + ScreenVersion')
check(!pref.includes("PREFIX + screenId"),'legacy unscoped ScreenId-only preference key must not return')

const orderVm=read('apps/web/src/modules/oms/orders/search/useOrderSearch.ts')
for(const token of ['rememberSearch','savedSearchDefaults','safePersistableSearch','saveCurrentSearchDefaults','setRememberSearch','loadScreenPreference','saveScreenPreference','Free-text keyword is intentionally never restored'])
  check(orderVm.includes(token),`OMS T01 safe preference flow missing: ${token}`)
const persistBlock=orderVm.match(/function safePersistableSearch\(\) \{([\s\S]*?)\n  \}/)?.[1]??''
check(persistBlock&&!/keyword\s*:/.test(persistBlock),'persistable OMS search must exclude free-text keyword')
const orderPage=read('apps/web/src/modules/oms/orders/search/OrderListPage.vue')
for(const token of ['remember','saved-search',':remember-checked="vm.rememberSearch.value"','@update:remember-checked="vm.setRememberSearch"','@saved-search-requested="vm.saveCurrentSearchDefaults"'])check(orderPage.includes(token),`OMS T01 SearchPanel preference binding missing: ${token}`)

const frame=read('apps/web/src/shell/KbxAppFrame.vue')
for(const token of ['KbxScreenPreferenceScopeKey','provide(KbxScreenPreferenceScopeKey,screenPreferenceScope)','runtimePanelTargetId','toggleRuntimePanel(\'operations\',item.operationId)',':active-id="runtimePanelTargetId"'])check(frame.includes(token),`Home/context navigation hardening missing: ${token}`)
const operations=read('packages/kbx-ui/src/components/KbxOperationCenter.vue')
const notifications=read('packages/kbx-ui/src/components/KbxNotificationCenter.vue')
for(const [name,source,id] of [['Operation',operations,'data-operation-id'],['Notification',notifications,'data-notification-id']]){
  check(source.includes('activeId?: string | null'),`${name} center active target contract missing`)
  check(source.includes(id)&&source.includes('aria-current')&&source.includes('scrollIntoView'),`${name} center must focus/identify Home target`)
}

const ref=read('apps/web/fe-reference/kbx-fe.js')
for(const token of ['orderRememberSearch','orderSavedDefaults','safeOrderDefaults','기본조건 저장','자유검색어는 저장하지'])check(ref.includes(token),`FE Reference safe search preference parity missing: ${token}`)
const refCss=read('apps/web/fe-reference/kbx-fe.css')
check(refCss.includes('.search-preference')&&refCss.includes('@media (forced-colors: active)'),'safe preference UI must coexist with theme/high-contrast CSS')

if(failures.length){console.error('FE preference/navigation v45 validation failed:');for(const f of failures)console.error(`- ${f}`);process.exit(1)}
console.log('FE preference/navigation v45 PASS: scoped versioned preferences, sensitive free-text exclusion, Home target focus, themed Reference parity.')
