import fs from 'node:fs'

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

const app=read('apps/web/src/App.vue')
const appIteration=Number(app.match(/app-version="v(\d+)-fe-/)?.[1]??0)
check(appIteration>=44,'App artifact version must identify v44+ FE runtime')

const header=read('packages/kbx-ui/src/shell/KbxGlobalHeader.vue')
for(const token of ['operationsOpen?: boolean','notificationsOpen?: boolean','id="kbx-runtime-operations-trigger"','id="kbx-runtime-notifications-trigger"','aria-controls="kbx-runtime-panel"',':aria-expanded="props.operationsOpen"',':aria-expanded="props.notificationsOpen"'])check(header.includes(token),`Global Header runtime-panel contract missing: ${token}`)

const shell=read('packages/kbx-ui/src/shell/KbxApplicationShell.vue')
check(shell.includes('operationsOpen?:boolean')&&shell.includes('notificationsOpen?:boolean'),'Application Shell must carry runtime center open state')
check(shell.includes(':operations-open="props.operationsOpen"')&&shell.includes(':notifications-open="props.notificationsOpen"'),'Application Shell must bind runtime center state to header')

const frame=read('apps/web/src/shell/KbxAppFrame.vue')
for(const token of ['runtimePanelEl=ref<HTMLElement|null>(null)','runtimeReturnFocus=ref<HTMLElement|null>(null)','function closeRuntimePanel','async function toggleRuntimePanel','runtimePanelEl.value?.focus()','target.focus()','id="kbx-runtime-panel"','role="region"','aria-label="패널 닫기"',"key:'Esc'",'enabled:()=>Boolean(runtimePanel.value)'])check(frame.includes(token),`AppFrame runtime panel recovery missing: ${token}`)
check(frame.includes("watch(()=>route.fullPath,()=>{closeRuntimePanel(false);void syncCurrentRoute()}") ,'route changes must close non-modal runtime panel')
check(frame.includes('function openMenuSearch(){closeRuntimePanel(false);store.menuSearchOpen=true}')&&frame.includes("execute:openMenuSearch")&&frame.includes('@menu-search-open="openMenuSearch"')&&frame.includes('@menu-search="openMenuSearch"'),'menu search must not stack over runtime panel from keyboard, header, or Home')

const generator=read('scripts/generate-design-tokens.mjs')
const generatedCss=read('packages/kbx-ui/src/tokens/kbx.css')
for(const token of ['@media (forced-colors: active)','--kbx-color-surface: Canvas','--kbx-color-text: CanvasText','--kbx-color-primary: LinkText','--kbx-color-focus: Highlight']){
  check(generator.includes(token),`token generator forced-colors bridge missing: ${token}`)
  check(generatedCss.includes(token),`generated token CSS forced-colors bridge missing: ${token}`)
}
const appCss=read('apps/web/src/styles/app.css')
check(appCss.includes('@media (forced-colors: active)')&&appCss.includes('[aria-current="page"]')&&appCss.includes('[aria-selected="true"]'),'app high-contrast selected/current-state visibility missing')

const boundary=read('packages/kbx-ui/src/components/KbxTemplateStateBoundary.vue')
check(boundary.includes('emptyActionLabel?: string')&&boundary.includes("emptyAction:[]")&&boundary.includes("emit('emptyAction')"),'template empty-state recovery contract missing')
const list=read('packages/kbx-ui/src/components/KbxListPage.vue')
check(list.includes('emptyActionLabel?: string')&&list.includes("emptyAction: []")&&list.includes('@empty-action="emit(\'emptyAction\')"'),'T01 template must surface empty-state recovery')
const orderVm=read('apps/web/src/modules/oms/orders/search/useOrderSearch.ts')
check(orderVm.includes('async function resetAndSearch()')&&orderVm.includes("exceptionOnly:false")&&orderVm.includes('await executeSearch()'),'OMS Golden T01 must implement one-step empty recovery')
const orderPage=read('apps/web/src/modules/oms/orders/search/OrderListPage.vue')
check(orderPage.includes('empty-action-label="조회조건 초기화"')&&orderPage.includes('@empty-action="vm.resetAndSearch"'),'OMS Golden T01 must connect empty recovery to real FE state')

const refHtml=read('apps/web/fe-reference/index.html')
const refCss=read('apps/web/fe-reference/kbx-fe.css')
const refJs=read('apps/web/fe-reference/kbx-fe.js')
for(const token of ['id="systemPanel"','id="jobsButton"','id="noticeButton"','aria-controls="systemPanel"'])check(refHtml.includes(token),`FE Reference non-modal system center missing: ${token}`)
for(const token of ['.system-panel{','@media (forced-colors: active)','forced-color-adjust:auto'])check(refCss.includes(token),`FE Reference theme/state CSS missing: ${token}`)
for(const token of ['function closeSystemPanel','function showSystemPanel','systemPanelRestoreFocus','systemPanel.focus()','closeSystemPanel(false)'])check(refJs.includes(token),`FE Reference system center JS missing: ${token}`)
check(!/showSystemPanel\(kind\)[\s\S]{0,500}openDrawerContent\('작업 센터'/.test(refJs),'jobs/notifications must not regress to modal Drawer implementation')

if(failures.length){console.error('FE state/navigation v44 validation failed:');for(const f of failures)console.error(`- ${f}`);process.exit(1)}
console.log('FE state/navigation v44 PASS: runtime center focus/recovery, forced-colors, T01 empty recovery, Reference non-modal parity.')
