import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
import type { KbxNavigationPreference, KbxNavigationResolvedEntry, KbxRecentNavigation, KbxWorkspaceTab } from '@kbx/contracts'

const STORAGE_PREFIX='kbx.navigation.preference.v3'
const LEGACY_STORAGE_PREFIX='kbx.navigation.preference.v2'
const MAX_STORED_PREFERENCE_BYTES=64_000
const MAX_SCOPE_LENGTH=160
const MAX_ID_LENGTH=128
const MAX_TITLE_LENGTH=120
const MAX_RECENT_PATH_LENGTH=1024
const defaultPreference=():KbxNavigationPreference=>({favorites:[],recents:[],sideNavCollapsed:false,workspaceMaxTabs:10,themeMode:'light'})
function boundedString(value:unknown,max:number,truncate=false){if(typeof value!=='string')return '';const clean=value.trim();if(clean.length<=max)return clean;return truncate?clean.slice(0,max):''}
function normalizeScope(value?:string|null){const scope=boundedString(value,MAX_SCOPE_LENGTH);return scope||null}
function storageKey(scope:string,prefix=STORAGE_PREFIX){return `${prefix}:${encodeURIComponent(scope)}`}
function uniqueStrings(values:unknown[],limit:number){return [...new Set(values.map(value=>boundedString(value,MAX_ID_LENGTH)).filter(Boolean))].slice(0,limit)}
function normalizePreference(value:unknown,scope:string|null):KbxNavigationPreference{
  const raw=(value&&typeof value==='object'?value:{}) as Partial<KbxNavigationPreference>
  const favorites=Array.isArray(raw.favorites)?uniqueStrings(raw.favorites,50):[]
  const recentsRaw=Array.isArray(raw.recents)?raw.recents.flatMap(item=>{
    if(!item||typeof item!=='object')return []
    const candidate=item as Partial<KbxRecentNavigation>
    const screenId=boundedString(candidate.screenId,MAX_ID_LENGTH)
    const title=boundedString(candidate.title,MAX_TITLE_LENGTH,true)
    const path=boundedString(candidate.path,MAX_RECENT_PATH_LENGTH)
    const visitedAt=boundedString(candidate.visitedAt,64)
    if(!screenId||!title||!path.startsWith('/')||!visitedAt||Number.isNaN(Date.parse(visitedAt)))return []
    return [{screenId,title,path,visitedAt} satisfies KbxRecentNavigation]
  }):[]
  const recents=[...new Map(recentsRaw.map(item=>[item.screenId,item])).values()].sort((a,b)=>b.visitedAt.localeCompare(a.visitedAt)).slice(0,12)
  const workspaceMaxTabs=Math.min(12,Math.max(8,Number.isFinite(Number(raw.workspaceMaxTabs))?Number(raw.workspaceMaxTabs):10))
  const themeMode=raw.themeMode==='dark'?'dark':'light'
  return {favorites,recents,sideNavCollapsed:Boolean(raw.sideNavCollapsed),workspaceMaxTabs,themeMode,...(scope?{scopeKey:scope}:{})}
}
function parseStoredPreference(raw:string|null,scope:string){
  if(!raw||raw.length>MAX_STORED_PREFERENCE_BYTES)return null
  try{return normalizePreference(JSON.parse(raw),scope)}catch{return null}
}
function loadPreference(scope:string|null):KbxNavigationPreference{
  if(!scope || typeof localStorage==='undefined')return defaultPreference()
  let currentRaw:string|null=null;let legacyRaw:string|null=null
  try{currentRaw=localStorage.getItem(storageKey(scope));legacyRaw=localStorage.getItem(storageKey(scope,LEGACY_STORAGE_PREFIX))}catch{return normalizePreference({},scope)}
  const current=parseStoredPreference(currentRaw,scope)
  if(current)return current
  const legacy=parseStoredPreference(legacyRaw,scope)
  if(legacy){try{localStorage.setItem(storageKey(scope),JSON.stringify(legacy))}catch{};return legacy}
  return normalizePreference({},scope)
}

export const useKbxWorkspaceStore=defineStore('kbx-workspace',()=>{
  const scopeKey=ref<string|null>(null)
  const preference=ref<KbxNavigationPreference>(defaultPreference())
  const tabs=ref<KbxWorkspaceTab[]>([])
  const activeKey=ref<string|null>(null)
  const menuSearchOpen=ref(false)
  const shellNotice=ref<string|null>(null)

  function persist(){
    if(!scopeKey.value||typeof localStorage==='undefined')return
    try{localStorage.setItem(storageKey(scopeKey.value),JSON.stringify(normalizePreference(preference.value,scopeKey.value)))}
    catch{shellNotice.value='개인화 설정을 저장하지 못했습니다. 현재 세션에서는 계속 사용할 수 있습니다.'}
  }
  function clearWorkspaceSession(){tabs.value=[];activeKey.value=null;menuSearchOpen.value=false;shellNotice.value=null}
  function setPreferenceScope(value?:string|null){
    const next=normalizeScope(value)
    if(next===scopeKey.value)return
    clearWorkspaceSession()
    scopeKey.value=next
    preference.value=loadPreference(next)
  }
  function key(screenId:string,path:string){return `${screenId}:${path}`}
  function evictForCapacity(){
    if(tabs.value.length<preference.value.workspaceMaxTabs)return true
    const candidate=[...tabs.value]
      .filter(tab=>!tab.dirty&&!tab.pinned&&tab.key!==activeKey.value)
      .sort((a,b)=>a.lastActivatedAt.localeCompare(b.lastActivatedAt))[0]
    if(!candidate){shellNotice.value=`열린 업무가 최대 ${preference.value.workspaceMaxTabs}개입니다. 미저장 또는 고정된 업무를 정리한 후 다시 여세요.`;return false}
    close(candidate.key)
    return true
  }
  function restoreTabs(restored:KbxWorkspaceTab[]){
    const limit=preference.value.workspaceMaxTabs
    const normalized=restored.filter(tab=>!tab.dirty).slice(0,limit).map(tab=>({...tab,dirty:false,resumed:true}))
    tabs.value=normalized
    activeKey.value=null
    return normalized.length
  }
  function notifyShell(message:string){shellNotice.value=boundedString(message,240,true)||null}
  function syncRoute(screenId:string,title:string,path:string,recent?:KbxRecentNavigation|null){
    const now=new Date().toISOString();const tabKey=key(screenId,path);let tab=tabs.value.find(x=>x.key===tabKey)
    if(!tab){if(!evictForCapacity())return false;tab={key:tabKey,screenId,title,path,openedAt:now,lastActivatedAt:now,dirty:false};tabs.value.push(tab)}else{tab.title=title;tab.path=path;tab.lastActivatedAt=now;tab.resumed=false}
    activeKey.value=tabKey;shellNotice.value=null
    if(recent)recordRecent(recent)
    return true
  }
  function setDirty(tabKey:string,dirty:boolean){const tab=tabs.value.find(x=>x.key===tabKey);if(tab)tab.dirty=dirty}
  function setActiveDirty(dirty:boolean){if(activeKey.value)setDirty(activeKey.value,dirty)}
  function togglePinned(tabKey:string){const tab=tabs.value.find(x=>x.key===tabKey);if(tab)tab.pinned=!tab.pinned}
  function close(tabKey:string){const index=tabs.value.findIndex(x=>x.key===tabKey);if(index<0)return;const wasActive=activeKey.value===tabKey;tabs.value.splice(index,1);if(wasActive){const next=tabs.value[Math.min(index,tabs.value.length-1)]??tabs.value.at(-1);activeKey.value=next?.key??null}}
  function activate(tabKey:string){const tab=tabs.value.find(x=>x.key===tabKey);if(tab){activeKey.value=tabKey;tab.lastActivatedAt=new Date().toISOString();tab.resumed=false}}
  function deactivate(){activeKey.value=null}
  function pruneTabs(allowedScreenIds:Set<string>){
    const active=activeKey.value;const before=tabs.value.length
    tabs.value=tabs.value.filter(tab=>allowedScreenIds.has(tab.screenId))
    if(active&&!tabs.value.some(tab=>tab.key===active))activeKey.value=tabs.value.at(-1)?.key??null
    return before-tabs.value.length
  }
  function reconcilePreference(allowedScreenIds:Set<string>){
    const favorites=preference.value.favorites.filter(id=>allowedScreenIds.has(id))
    const recents=preference.value.recents.filter(item=>allowedScreenIds.has(item.screenId))
    const changed=favorites.length!==preference.value.favorites.length||recents.length!==preference.value.recents.length
    if(changed){preference.value={...preference.value,favorites,recents};persist()}
    return changed
  }
  function toggleFavorite(screenId:string){const normalized=boundedString(screenId,MAX_ID_LENGTH);if(!normalized)return;const list=preference.value.favorites;const index=list.indexOf(normalized);if(index>=0)list.splice(index,1);else list.unshift(normalized);preference.value.favorites=uniqueStrings(list,50);persist()}
  function recordRecent(recent:KbxRecentNavigation){const normalized=normalizePreference({...preference.value,recents:[recent,...preference.value.recents.filter(x=>x.screenId!==recent.screenId)]},scopeKey.value).recents;preference.value.recents=normalized;persist()}
  function toggleSideNav(){preference.value.sideNavCollapsed=!preference.value.sideNavCollapsed;persist()}
  function toggleTheme(){preference.value.themeMode=preference.value.themeMode==='dark'?'light':'dark';persist()}
  function favoriteEntries(entries:KbxNavigationResolvedEntry[]){const map=new Map(entries.filter(x=>x.favoriteAllowed!==false).map(x=>[x.screenId,x]));return preference.value.favorites.map(id=>map.get(id)).filter((x):x is KbxNavigationResolvedEntry=>Boolean(x))}
  function dismissShellNotice(){shellNotice.value=null}
  const activeTab=computed(()=>tabs.value.find(x=>x.key===activeKey.value)??null)
  return {scopeKey,preference,tabs,activeKey,activeTab,menuSearchOpen,shellNotice,setPreferenceScope,clearWorkspaceSession,restoreTabs,notifyShell,syncRoute,setDirty,setActiveDirty,togglePinned,close,activate,deactivate,pruneTabs,reconcilePreference,toggleFavorite,recordRecent,toggleSideNav,toggleTheme,favoriteEntries,dismissShellNotice}
})
