<script setup lang="ts">
import { computed, nextTick, ref } from 'vue'
import type { KbxWorkspaceTab } from '@kbx/contracts'
const props=withDefaults(defineProps<{tabs:KbxWorkspaceTab[];activeKey?:string|null;maxTabs?:number}>(),{activeKey:null,maxTabs:10})
const emit=defineEmits<{select:[tab:KbxWorkspaceTab];closeRequest:[tab:KbxWorkspaceTab];togglePin:[tab:KbxWorkspaceTab]}>()
const tabButtons=ref<Record<string,HTMLButtonElement|null>>({})
const visibleTabs=computed(()=>{if(props.tabs.length<=props.maxTabs)return props.tabs;const first=props.tabs.slice(0,props.maxTabs);const active=props.tabs.find(x=>x.key===props.activeKey);if(active&&!first.some(x=>x.key===active.key))first[first.length-1]=active;return first})
const overflowTabs=computed(()=>props.tabs.filter(tab=>!visibleTabs.value.some(x=>x.key===tab.key)))
const focusableKey=computed(()=>props.activeKey??visibleTabs.value[0]?.key??null)
async function selectAndFocus(tab:KbxWorkspaceTab){emit('select',tab);await nextTick();tabButtons.value[tab.key]?.focus()}
function move(tab:KbxWorkspaceTab,direction:'prev'|'next'|'first'|'last'){const list=visibleTabs.value;const index=list.findIndex(x=>x.key===tab.key);if(index<0)return;const target=direction==='first'?list[0]:direction==='last'?list.at(-1):list[(index+(direction==='next'?1:-1)+list.length)%list.length];if(target)void selectAndFocus(target)}
function keydown(event:KeyboardEvent,tab:KbxWorkspaceTab){if(event.key==='ArrowLeft'){event.preventDefault();move(tab,'prev')}else if(event.key==='ArrowRight'){event.preventDefault();move(tab,'next')}else if(event.key==='Home'){event.preventDefault();move(tab,'first')}else if(event.key==='End'){event.preventDefault();move(tab,'last')}}
function setTabButton(key:string,el:unknown){tabButtons.value[key]=el instanceof HTMLButtonElement?el:null}
</script>

<template>
  <nav class="workspace-tabs" role="tablist" aria-label="열린 업무">
    <div v-if="props.tabs.length===0" class="empty">열린 업무가 없습니다.</div>
    <div v-for="tab in visibleTabs" :key="tab.key" class="tab-shell" :class="{active:tab.key===props.activeKey}">
      <button :ref="el=>setTabButton(tab.key,el)" type="button" class="tab" role="tab" :aria-selected="tab.key===props.activeKey" :tabindex="tab.key===focusableKey?0:-1" @click="emit('select',tab)" @keydown="keydown($event,tab)" @auxclick.middle.prevent="emit('closeRequest',tab)"><span v-if="tab.pinned" class="pin-mark" aria-label="고정됨">◆</span><span v-if="tab.resumed&&!tab.dirty" class="resume-mark" title="이전 세션에서 복원된 업무 위치" aria-label="이전 세션에서 복원됨">↺</span><span class="title">{{tab.title}}</span><span v-if="tab.dirty" class="dirty" title="저장하지 않은 변경" aria-label="저장하지 않은 변경">●</span></button>
      <button type="button" class="pin" :aria-pressed="Boolean(tab.pinned)" :aria-label="tab.pinned?`${tab.title} 고정 해제`:`${tab.title} 고정`" @click="emit('togglePin',tab)">{{tab.pinned?'◆':'◇'}}</button>
      <button type="button" class="close" :aria-label="`${tab.title} 닫기`" @click="emit('closeRequest',tab)">×</button>
    </div>
    <details v-if="overflowTabs.length" class="overflow"><summary>더보기 {{ overflowTabs.length }}</summary><div class="overflow-menu"><button v-for="tab in overflowTabs" :key="tab.key" type="button" @click="emit('select',tab)"><span>{{tab.title}}</span><small v-if="tab.pinned">고정</small><small v-if="tab.resumed&&!tab.dirty">복원</small><small v-if="tab.dirty">미저장</small></button></div></details>
  </nav>
</template>

<style scoped>
.workspace-tabs{height:var(--kbx-workspace-tabs-height);display:flex;align-items:flex-end;gap:var(--kbx-border-width);overflow-x:auto;border-bottom:var(--kbx-border-width) solid var(--kbx-color-border);background:var(--kbx-color-surface-muted);padding:0 var(--kbx-space-2)}.tab-shell{height:var(--kbx-workspace-tab-height);max-width:var(--kbx-workspace-tab-max-width);display:flex;align-items:center;border:var(--kbx-border-width) solid transparent;border-bottom:0;background:transparent}.tab-shell.active{background:var(--kbx-color-surface);border-color:var(--kbx-color-border);box-shadow:inset 0 calc(var(--kbx-border-width) * 2) 0 var(--kbx-color-module-accent)}.tab{height:100%;min-width:0;display:flex;align-items:center;gap:var(--kbx-space-2);border:0;background:transparent;padding:0 var(--kbx-space-2);color:var(--kbx-color-text-muted);white-space:nowrap}.tab-shell.active .tab{color:var(--kbx-color-text);font-weight:500}.title{overflow:hidden;text-overflow:ellipsis}.dirty{font-size:var(--kbx-font-xs);color:var(--kbx-color-warning)}.pin-mark{font-size:var(--kbx-font-xs);color:var(--kbx-color-module-accent)}.resume-mark{font-size:var(--kbx-font-sm);color:var(--kbx-color-info);font-weight:600}.pin,.close{width:var(--kbx-control-xs);height:100%;border:0;background:transparent;color:var(--kbx-color-text-muted)}.pin{font-size:var(--kbx-font-xs)}.close{font-size:var(--kbx-font-xl)}.pin:hover,.close:hover,.pin:focus-visible,.close:focus-visible{background:var(--kbx-color-surface-hover)}.pin[aria-pressed="true"]{color:var(--kbx-color-module-accent)}.empty{font-size:var(--kbx-font-xs);color:var(--kbx-color-text-muted);padding:0 var(--kbx-space-2) var(--kbx-space-2);margin:auto}.overflow{position:relative;margin-left:auto;padding-bottom:var(--kbx-space-1)}.overflow summary{list-style:none;cursor:pointer;font-size:var(--kbx-font-xs);padding:var(--kbx-space-2)}.overflow-menu{position:absolute;right:0;top:var(--kbx-control-height);z-index:80;min-width:var(--kbx-workspace-overflow-width);padding:var(--kbx-space-1);border:var(--kbx-border-width) solid var(--kbx-color-border);background:var(--kbx-color-surface);box-shadow:var(--kbx-shadow-overlay)}.overflow-menu button{width:100%;min-height:var(--kbx-control-xs);display:flex;align-items:center;gap:var(--kbx-space-2);border:0;background:transparent;text-align:left;padding:0 var(--kbx-space-2)}.overflow-menu button span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.overflow-menu button small{font-size:var(--kbx-font-xs);color:var(--kbx-color-text-muted)}.overflow-menu button:hover{background:var(--kbx-color-surface-hover)}
</style>
