<script setup lang="ts">
import { nextTick, ref, watch } from 'vue'

const props = withDefaults(defineProps<{
  open:boolean
  title?:string
  canSave?:boolean
  saving?:boolean
}>(), {
  title:'저장하지 않은 변경사항이 있습니다.',
  canSave:true,
  saving:false,
})
const emit = defineEmits<{ stay:[]; discard:[]; save:[] }>()

const dialog = ref<HTMLElement | null>(null)
let restoreFocus:HTMLElement | null = null

function focusables() {
  if (!dialog.value) return []
  return Array.from(dialog.value.querySelectorAll<HTMLElement>('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'))
    .filter(element => element.offsetParent !== null)
}

function onKeydown(event:KeyboardEvent) {
  if (event.key === 'Escape' && !props.saving) {
    event.preventDefault()
    emit('stay')
    return
  }
  if (event.key !== 'Tab') return
  const nodes = focusables()
  if (!nodes.length) return
  const first = nodes[0]
  const last = nodes[nodes.length - 1]
  if (event.shiftKey && document.activeElement === first) {
    event.preventDefault()
    last.focus()
  } else if (!event.shiftKey && document.activeElement === last) {
    event.preventDefault()
    first.focus()
  }
}

watch(() => props.open, async (open, previous) => {
  if (open) {
    restoreFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null
    await nextTick()
    focusables()[0]?.focus()
  } else if (previous) {
    await nextTick()
    restoreFocus?.focus()
    restoreFocus = null
  }
})
</script>

<template>
  <div v-if="props.open" class="overlay" @mousedown.self="!props.saving && emit('stay')">
    <section
      ref="dialog"
      class="dialog"
      role="dialog"
      aria-modal="true"
      aria-labelledby="unsaved-title"
      aria-describedby="unsaved-detail"
      @keydown="onKeydown"
    >
      <h2 id="unsaved-title">{{props.title}}</h2>
      <p id="unsaved-detail">변경사항을 어떻게 처리할지 선택하세요.</p>
      <div class="actions">
        <button type="button" :disabled="props.saving" @click="emit('stay')">계속 편집</button>
        <button type="button" class="danger" :disabled="props.saving" @click="emit('discard')">변경 버리기</button>
        <button v-if="props.canSave" type="button" class="primary" :disabled="props.saving" @click="emit('save')">{{props.saving?'저장 중...':'저장 후 이동'}}</button>
      </div>
    </section>
  </div>
</template>

<style scoped>
.overlay{position:fixed;inset:0;z-index:220;display:flex;align-items:center;justify-content:center;background:var(--kbx-color-overlay)}
.dialog{width:min(440px,calc(100vw - 24px));padding:18px;border:1px solid var(--kbx-color-border);border-radius:var(--kbx-radius-md);background:var(--kbx-color-surface);box-shadow:var(--kbx-shadow-overlay)}
h2{font-size:var(--kbx-font-lg);margin:0 0 8px}p{color:var(--kbx-color-text-muted)}
.actions{display:flex;justify-content:flex-end;gap:6px;margin-top:18px}.actions button{height:34px;padding:0 12px;border:1px solid var(--kbx-color-border);border-radius:var(--kbx-radius-sm);background:var(--kbx-color-surface)}
.actions .primary{background:var(--kbx-color-primary);border-color:var(--kbx-color-primary);color:white}.actions .danger{color:var(--kbx-color-danger)}
</style>
