<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {
  KbxAccessDenied,
  KbxApplicationShell,
  KbxHomePage,
  KbxPermissionHostKey,
  KbxNotificationCenter,
  KbxOperationCenter,
  KbxRuntimeBanner,
  KbxUnsavedChangesDialog,
  KbxUtilityRail,
  KbxScreenUtilityHostKey,
  resolveKbxSafeRecentPath,
  resolveKbxSafeWorkspacePath,
  useKbxShortcuts,
  applyKbxTheme,
} from '@kbx/ui'
import type { KbxAiAnswer, KbxAiScreenContext, KbxHomeAttentionItem, KbxHomeLaunchItem, KbxNavigationSection, KbxRecentNavigation, KbxScreenDefinition, KbxSuggestionRequest, KbxWorkspaceTab } from '@kbx/contracts'
import type { KbxScreenUtilityHost, KbxScreenUtilityTab } from '@kbx/ui'
import { generatedScreens } from '../registry/screens.generated'
import { navigationSections, resolvedNavigationEntries } from './navigationCatalog'
import { screenRoutePatterns } from '../router/appRoutes'
import { useKbxWorkspaceStore } from './workspaceStore'
import { canWorkspaceSave, requestWorkspaceSave } from './workspaceLifecycleRegistry'
import { useKbxRuntime } from '../runtime/useKbxRuntime'
import { kbxTelemetry } from '../telemetry/kbxTelemetryClient'
import { helpRegistry } from '../help/helpRegistry'
import { askKbxAssistant } from '../utility/aiAssistantApi'
import { submitKbxSuggestion } from '../utility/suggestionApi'
import { kbxAiCapabilities } from '../permissions/authorizationPolicy'
import { KbxScreenPreferenceScopeKey } from '../preferences/screenPreferenceStore'
import { loadWorkspaceSession, saveWorkspaceSession } from './workspaceSessionStore'

const props=withDefaults(defineProps<{productName?:string;grantedPermissions?:string[];preferenceScope?:string;appVersion?:string;userRole?:string}>(),{productName:'KBX',grantedPermissions:()=>[],preferenceScope:'',appVersion:'unknown',userRole:'user'})
const frameEmit=defineEmits<{profile:[]}>()
const router=useRouter();const route=useRoute();const store=useKbxWorkspaceStore();const pendingClose=ref<KbxWorkspaceTab|null>(null);const savingClose=ref(false);const runtimePanel=ref<'operations'|'notifications'|null>(null);const runtimePanelTargetId=ref<string|null>(null);const runtimePanelEl=ref<HTMLElement|null>(null);const runtimeReturnFocus=ref<HTMLElement|null>(null)
const { notice, operations, notifications, unreadCount, refresh, markRead }=useKbxRuntime()
const themeMode=computed(()=>(store.preference.themeMode==='dark'?'dark':'light') as 'light'|'dark')
const screenPreferenceScope=computed(()=>props.preferenceScope)
provide(KbxScreenPreferenceScopeKey,screenPreferenceScope)
watch(themeMode,mode=>applyKbxTheme(mode),{immediate:true})
const screenById=new Map(generatedScreens.map(screen=>[screen.id,screen]))
const hasPermission=(required?:string[])=>!required?.length || required.every(permission=>props.grantedPermissions.includes(permission))
provide(KbxPermissionHostKey,{has:(permission:string)=>props.grantedPermissions.includes(permission)})
const allowedEntries=computed(()=>resolvedNavigationEntries.filter(entry=>hasPermission(entry.permissions)&&entry.menuVisible!==false))
const allowedIds=computed(()=>new Set(allowedEntries.value.map(x=>x.screenId)))
const sections=computed<KbxNavigationSection[]>(()=>navigationSections.map(section=>({...section,entries:section.entries.filter(entry=>allowedIds.value.has(entry.screenId))})).filter(section=>section.entries.length))
const favorites=computed(()=>store.favoriteEntries(allowedEntries.value))
function screenFallback(screenId:string){return allowedEntries.value.find(entry=>entry.screenId===screenId)?.path??'/home'}
function safeWorkspacePath(screenId:string,candidate:string){return resolveKbxSafeWorkspacePath(screenRoutePatterns[screenId]??[],candidate,screenFallback(screenId))}
const recents=computed<KbxRecentNavigation[]>(()=>store.preference.recents.flatMap(item=>{const nav=allowedEntries.value.find(entry=>entry.screenId===item.screenId);if(!nav)return [];const recent=resolveKbxSafeRecentPath(nav,item.path);return [{...item,title:nav.title,path:safeWorkspacePath(item.screenId,recent)}]}))
const allowedTabs=computed(()=>store.tabs.filter(tab=>{const screen=screenById.get(tab.screenId);return Boolean(screen&&hasPermission(screen.permissions)&&safeWorkspacePath(tab.screenId,tab.path)===tab.path)}))
const isHome=computed(()=>route.path==='/home')
const activeScreenId=computed(()=>String(route.meta.screenId??''))
const activeScreen=computed(()=>screenById.get(activeScreenId.value))
const activeScreenKnown=computed(()=>!activeScreenId.value || Boolean(activeScreen.value))
const activeScreenAllowed=computed(()=>activeScreenKnown.value && (!activeScreen.value || hasPermission(activeScreen.value.permissions)))
const activeModule=computed(()=>isHome.value?'':activeScreen.value?.module??'')
const runningOperationCount=computed(()=>operations.value.filter(x=>x.status==='running'||x.status==='queued').length)
const failedOperationCount=computed(()=>operations.value.filter(x=>x.status==='failed'||x.status==='partially-completed').length)
const urgentUnreadCount=computed(()=>notifications.value.filter(x=>!x.readAt&&(x.severity==='warning'||x.severity==='error')).length)
const runtimeScreenAllowed=(screenId?:string)=>!screenId||Boolean(screenById.get(screenId)&&hasPermission(screenById.get(screenId)?.permissions))
const homeOperations=computed(()=>operations.value.filter(item=>runtimeScreenAllowed(item.sourceScreenId)))
const homeNotifications=computed(()=>notifications.value.filter(item=>runtimeScreenAllowed(item.screenId)))
const utilityTab=ref<KbxScreenUtilityTab|null>(null)
const utilityScreen=ref<KbxScreenDefinition|null>(null)
const utilityAiAnswer=ref<KbxAiAnswer|null>(null)
const utilityAiLoading=ref(false)
const utilityAiError=ref('')
const utilitySuggestionSubmitting=ref(false)
const utilityHelp=computed(()=>utilityScreen.value?.helpKey?helpRegistry[utilityScreen.value.helpKey]:undefined)
const utilityCapabilities=computed(()=>kbxAiCapabilities(props.grantedPermissions,['explain','suggest','draft']))
const utilityAiContext=computed<KbxAiScreenContext|undefined>(()=>utilityScreen.value&&utilityCapabilities.value.length?{screenId:utilityScreen.value.id,screenVersion:utilityScreen.value.version,allowedCapabilities:utilityCapabilities.value}:undefined)
const utilitySuggestionContext=computed(()=>utilityScreen.value&&props.grantedPermissions.includes('common.suggestion.create')?{screenId:utilityScreen.value.id,screenVersion:utilityScreen.value.version,route:route.fullPath,appVersion:props.appVersion,userRole:props.userRole,activeFilters:[]}:undefined)
const utilityHost:KbxScreenUtilityHost={
  available(screen){const tabs:KbxScreenUtilityTab[]=[];if(screen.helpKey&&helpRegistry[screen.helpKey])tabs.push('help');if(utilityCapabilities.value.length)tabs.push('ai');if(props.grantedPermissions.includes('common.suggestion.create'))tabs.push('suggestion');return tabs},
  open(screen,tab){closeRuntimePanel(false);utilityScreen.value=screen;utilityTab.value=tab;utilityAiAnswer.value=null;utilityAiError.value=''},
}
provide(KbxScreenUtilityHostKey,utilityHost)
async function askUtilityAi(question:string){if(!utilityAiContext.value)return;utilityAiLoading.value=true;utilityAiError.value='';try{utilityAiAnswer.value=await askKbxAssistant({question,context:utilityAiContext.value})}catch{utilityAiError.value='네트워크 상태를 확인한 후 다시 질문하세요.'}finally{utilityAiLoading.value=false}}
async function submitUtilitySuggestion(request:KbxSuggestionRequest){utilitySuggestionSubmitting.value=true;try{await submitKbxSuggestion(request);utilityTab.value=null}finally{utilitySuggestionSubmitting.value=false}}
const permittedScreenIds=computed(()=>new Set(generatedScreens.filter(screen=>hasPermission(screen.permissions)).map(screen=>screen.id)))
function restoreWorkspaceForScope(scope:string){
  const snapshot=loadWorkspaceSession(scope)
  if(!snapshot)return {restored:0,dirtyDiscarded:0}
  const restored:KbxWorkspaceTab[]=[]
  for(const item of snapshot.tabs){
    const screen=screenById.get(item.screenId)
    if(!screen||!hasPermission(screen.permissions))continue
    const safe=safeWorkspacePath(item.screenId,item.path)
    if(safe!==item.path)continue
    restored.push({key:`${item.screenId}:${safe}`,screenId:item.screenId,title:screen.title,path:safe,pinned:item.pinned,dirty:false,resumed:true,openedAt:item.openedAt,lastActivatedAt:item.lastActivatedAt})
  }
  return {restored:store.restoreTabs(restored),dirtyDiscarded:snapshot.dirtyDiscardedCount}
}
function persistWorkspaceSession(){
  const scope=screenPreferenceScope.value
  if(scope)saveWorkspaceSession(scope,store.tabs)
}

watch(permittedScreenIds,ids=>{const removed=store.pruneTabs(ids);if(removed)store.shellNotice='권한 변경으로 더 이상 사용할 수 없는 열린 업무를 안전하게 닫았습니다.'},{immediate:true})
watch(allowedIds,ids=>store.reconcilePreference(ids),{immediate:true})

async function syncCurrentRoute(){
  if(isHome.value)return
  const screenId=activeScreenId.value
  if(!screenId)return
  const screen=screenById.get(screenId)
  if(!screen||!hasPermission(screen.permissions))return
  const safe=safeWorkspacePath(screenId,route.fullPath)
  if(safe!==route.fullPath){store.shellNotice='허용되지 않은 화면 경로를 차단했습니다.';await router.replace('/home');return}
  const navEntry=resolvedNavigationEntries.find(entry=>entry.screenId===screenId&&entry.menuVisible!==false&&hasPermission(entry.permissions))
  const recent=navEntry&&navEntry.recentPolicy!=='none'?{screenId,title:screen.title,path:navEntry.recentPolicy==='route'?resolveKbxSafeRecentPath(navEntry,safe):navEntry.path,visitedAt:new Date().toISOString()}:null
  const synced=store.syncRoute(screenId,screen.title,safe,recent)
  if(!synced&&route.path!=='/home')await router.replace('/home')
}
watch(()=>props.preferenceScope,(scope,previous)=>{
  store.setPreferenceScope(scope);utilityTab.value=null;utilityScreen.value=null;closeRuntimePanel(false)
  const resume=scope?restoreWorkspaceForScope(scope):{restored:0,dirtyDiscarded:0}
  if(resume.restored||resume.dirtyDiscarded)void nextTick(()=>store.notifyShell(`${resume.restored?`이전 세션 업무 ${resume.restored}개를 위치만 안전하게 복원했습니다. `:''}${resume.dirtyDiscarded?`미저장 편집 ${resume.dirtyDiscarded}개는 데이터 정합성을 위해 복원하지 않았습니다.`:''}`.trim()))
  if(previous!==undefined&&scope!==previous&&route.path!=='/home')void router.replace('/home')
},{immediate:true})
watch(()=>props.grantedPermissions.join('|'),()=>{utilityTab.value=null;utilityScreen.value=null;closeRuntimePanel(false)})
watch(()=>store.tabs.map(tab=>[tab.key,Boolean(tab.dirty),Boolean(tab.pinned),Boolean(tab.resumed),tab.lastActivatedAt]).join('|'),persistWorkspaceSession,{flush:'post'})
watch(()=>route.fullPath,()=>{closeRuntimePanel(false);void syncCurrentRoute()},{immediate:true})
watch(activeScreen,(screen,previous)=>{utilityTab.value=null;utilityScreen.value=null;
  if(previous?.telemetry?.enabled&&hasPermission(previous.permissions))kbxTelemetry.track('screen.close',{screenId:previous.id,screenVersion:previous.version,attributes:{module:previous.module,closeReason:'navigate'}})
  if(screen?.telemetry?.enabled&&hasPermission(screen.permissions))kbxTelemetry.track('screen.open',{screenId:screen.id,screenVersion:screen.version,attributes:{module:screen.module,launchMode:'tab'}})
},{immediate:true})


function closeRuntimePanel(restoreFocus=true){
  const target=runtimeReturnFocus.value
  runtimePanel.value=null
  runtimePanelTargetId.value=null
  runtimeReturnFocus.value=null
  if(restoreFocus&&target)void nextTick(()=>target.focus())
}
function openMenuSearch(){closeRuntimePanel(false);store.menuSearchOpen=true}
async function toggleRuntimePanel(kind:'operations'|'notifications',targetId:string|null=null){
  if(runtimePanel.value===kind&&!targetId){closeRuntimePanel();return}
  utilityTab.value=null;utilityScreen.value=null
  runtimeReturnFocus.value=document.activeElement instanceof HTMLElement?document.activeElement:null
  runtimePanel.value=kind
  runtimePanelTargetId.value=targetId
  await nextTick();if(!targetId)runtimePanelEl.value?.focus()
}

async function openHome(){store.deactivate();if(route.path!=='/home')await router.push('/home')}
async function openEntry(requested:(typeof resolvedNavigationEntries)[number]){const entry=allowedEntries.value.find(item=>item.screenId===requested.screenId);if(!entry)return;store.menuSearchOpen=false;await router.push(entry.path)}
async function openRecent(entry:KbxRecentNavigation){const nav=allowedEntries.value.find(item=>item.screenId===entry.screenId);if(!nav)return;const candidate=safeWorkspacePath(entry.screenId,resolveKbxSafeRecentPath(nav,entry.path));await router.push(candidate)}
async function selectTab(tab:KbxWorkspaceTab){const stored=store.tabs.find(item=>item.key===tab.key);if(!stored||!permittedScreenIds.value.has(stored.screenId))return;const safe=safeWorkspacePath(stored.screenId,stored.path);if(safe==='/home'&&stored.path!=='/home'){store.close(stored.key);store.shellNotice='변조되었거나 더 이상 유효하지 않은 열린 업무 경로를 제거했습니다.';await openHome();return}store.activate(stored.key);if(route.fullPath!==safe)await router.push(safe)}
async function launchHomeItem(item:KbxHomeLaunchItem){if(item.tabKey){const tab=store.tabs.find(tab=>tab.key===item.tabKey);if(tab)return selectTab(tab)}const nav=allowedEntries.value.find(entry=>entry.screenId===item.screenId);if(!nav)return;const candidate=item.source==='recent'?resolveKbxSafeRecentPath(nav,item.path):nav.path;await router.push(safeWorkspacePath(item.screenId,candidate))}
async function openHomeAttention(item:KbxHomeAttentionItem){
  if(item.tabKey){const tab=store.tabs.find(tab=>tab.key===item.tabKey);if(tab)return selectTab(tab)}
  if(item.operationId){await toggleRuntimePanel('operations',item.operationId);return}
  if(item.notificationId){
    await markRead(item.notificationId)
    if(item.screenId){
      const nav=allowedEntries.value.find(entry=>entry.screenId===item.screenId)
      if(nav){
        const candidate=item.path??nav.path
        const safe=safeWorkspacePath(item.screenId,candidate)
        if(item.path&&safe!==item.path){store.shellNotice='알림에 포함된 허용되지 않은 이동 경로를 차단했습니다.';await toggleRuntimePanel('notifications');return}
        await router.push(safe);return
      }
    }
    await toggleRuntimePanel('notifications',item.notificationId)
  }
}
function closeTab(tab:KbxWorkspaceTab){if(tab.dirty){pendingClose.value=tab;return}void discardAndClose(tab)}
async function discardAndClose(tab:KbxWorkspaceTab){const wasCurrent=route.fullPath===tab.path;store.close(tab.key);pendingClose.value=null;if(wasCurrent){const next=store.activeTab;if(next)await selectTab(next);else await openHome()}}
async function saveAndClose(){const tab=pendingClose.value;if(!tab)return;savingClose.value=true;try{const saved=await requestWorkspaceSave(tab.path);if(saved)await discardAndClose(tab)}finally{savingClose.value=false}}
useKbxShortcuts([{key:'Ctrl+K',scope:'application',execute:openMenuSearch},{key:'Esc',scope:'application',priority:20,enabled:()=>Boolean(runtimePanel.value),execute:()=>closeRuntimePanel()}])
onMounted(()=>kbxTelemetry.start());onBeforeUnmount(()=>kbxTelemetry.stop())
</script>

<template>
  <KbxApplicationShell
    :product-name="props.productName" :active-module="activeModule" :active-screen-id="activeScreenId" :sections="sections" :all-entries="allowedEntries" :favorites="favorites" :recents="recents" :tabs="allowedTabs" :active-tab-key="store.activeKey" :side-nav-collapsed="store.preference.sideNavCollapsed" :menu-search-open="store.menuSearchOpen" :operation-count="runningOperationCount" :notification-count="unreadCount" :operations-open="runtimePanel==='operations'" :notifications-open="runtimePanel==='notifications'" :home-active="isHome" :workspace-max-tabs="store.preference.workspaceMaxTabs" :shell-notice="store.shellNotice" :theme-mode="themeMode"
    @home="openHome" @open="openEntry" @open-recent="openRecent" @select-tab="selectTab" @close-tab="closeTab" @toggle-tab-pin="tab=>store.togglePinned(tab.key)" @toggle-favorite="store.toggleFavorite" @toggle-side-nav="store.toggleSideNav" @menu-search-open="openMenuSearch" @menu-search-close="store.menuSearchOpen=false" @operations="toggleRuntimePanel('operations')" @notifications="toggleRuntimePanel('notifications')" @theme-toggle="store.toggleTheme" @profile="frameEmit('profile')" @dismiss-shell-notice="store.dismissShellNotice"
  >
    <template #runtime><KbxRuntimeBanner :notice="notice" @retry="refresh" /><aside v-if="runtimePanel" id="kbx-runtime-panel" ref="runtimePanelEl" class="runtime-panel" tabindex="-1" role="region" :aria-labelledby="runtimePanel==='operations'?'kbx-runtime-panel-title-operations':'kbx-runtime-panel-title-notifications'"><header class="runtime-panel__header"><strong :id="runtimePanel==='operations'?'kbx-runtime-panel-title-operations':'kbx-runtime-panel-title-notifications'">{{runtimePanel==='operations'?'작업 센터':'알림 센터'}}</strong><button type="button" aria-label="패널 닫기" @click="closeRuntimePanel()">×</button></header><KbxOperationCenter v-if="runtimePanel==='operations'" :operations="operations" :active-id="runtimePanelTargetId" /><KbxNotificationCenter v-else :notifications="notifications" :active-id="runtimePanelTargetId" @read="markRead" /></aside></template>
    <KbxHomePage v-if="isHome" :entries="allowedEntries" :favorites="favorites" :recents="recents" :operation-count="runningOperationCount" :operation-failure-count="failedOperationCount" :notification-count="unreadCount" :urgent-notification-count="urgentUnreadCount" :operations="homeOperations" :notifications="homeNotifications" :tabs="allowedTabs" @open="openEntry" @launch="launchHomeItem" @attention="openHomeAttention" @toggle-favorite="store.toggleFavorite" @menu-search="openMenuSearch" @operations="toggleRuntimePanel('operations')" @notifications="toggleRuntimePanel('notifications')" />
    <KbxAccessDenied v-else-if="!activeScreenAllowed" @home="openHome" @menu-search="openMenuSearch" />
    <router-view v-else />
  </KbxApplicationShell>
  <KbxUnsavedChangesDialog :open="Boolean(pendingClose)" :can-save="pendingClose?canWorkspaceSave(pendingClose.path):false" :saving="savingClose" @stay="pendingClose=null" @discard="pendingClose&&discardAndClose(pendingClose)" @save="saveAndClose" />
  <KbxUtilityRail triggerless :open-tab="utilityTab" :help="utilityHelp" :ai-context="utilityAiContext" :ai-answer="utilityAiAnswer" :ai-loading="utilityAiLoading" :ai-error="utilityAiError" :ai-current-screen-label="utilityScreen?.title" :suggestion-context="utilitySuggestionContext" :suggestion-submitting="utilitySuggestionSubmitting" @close="utilityTab=null" @ai-ask="askUtilityAi" @suggestion-submit="submitUtilitySuggestion" />
</template>

<style scoped>.runtime-panel__header{min-height:var(--kbx-control-height);display:flex;align-items:center;justify-content:space-between;gap:var(--kbx-space-2);padding:0 var(--kbx-space-2);border-bottom:var(--kbx-border-width) solid var(--kbx-color-border)}.runtime-panel__header button{width:var(--kbx-control-xs);height:var(--kbx-control-xs);border:0;border-radius:var(--kbx-radius-sm);background:transparent;color:var(--kbx-color-text)}.runtime-panel__header button:hover{background:var(--kbx-color-surface-hover)}.runtime-panel{position:fixed;z-index:var(--kbx-z-runtime-panel);right:var(--kbx-space-2);top:var(--kbx-runtime-panel-top);width:min(var(--kbx-runtime-panel-width),calc(100vw - var(--kbx-space-6)));max-height:70vh;overflow:auto;padding:var(--kbx-space-2);border:var(--kbx-border-width) solid var(--kbx-color-border);border-radius:var(--kbx-radius-md);background:var(--kbx-color-surface);box-shadow:var(--kbx-shadow-overlay)}</style>
