<script setup lang="ts" generic="T extends { id: string; current?: boolean; trusted?: boolean }">
// "..." menu for a single device row. The menu is teleported to <body> so it
// escapes any overflow/clip on the table — same pattern as the React design
// source. Closes on outside-click, Escape, or scroll.
//
// Generic over the device type so the emitted payload keeps the caller's full
// device shape (label, os, …) instead of narrowing to a subset.

defineProps<{ device: T }>()
const emit = defineEmits<{
  rename: [T]
  trust: [T]
  history: [T]
  revoke: [T]
}>()

const open = ref(false)
const triggerRef = ref<HTMLElement | null>(null)
const menuRef = ref<HTMLElement | null>(null)
const pos = ref({ top: 0, right: 0 })

function toggle(e: MouseEvent) {
  e.stopPropagation()
  if (!triggerRef.value) return
  const r = triggerRef.value.getBoundingClientRect()
  pos.value = { top: r.bottom + 4, right: window.innerWidth - r.right }
  open.value = !open.value
}

function close() { open.value = false }

function onDoc(e: MouseEvent) {
  if (!open.value) return
  const t = e.target as Node
  if (menuRef.value?.contains(t) || triggerRef.value?.contains(t)) return
  open.value = false
}
function onKey(e: KeyboardEvent) {
  if (e.key === 'Escape') open.value = false
}
function onScroll() { open.value = false }

onMounted(() => {
  document.addEventListener('mousedown', onDoc)
  document.addEventListener('keydown', onKey)
  window.addEventListener('scroll', onScroll, true)
  window.addEventListener('resize', onScroll)
})
onBeforeUnmount(() => {
  document.removeEventListener('mousedown', onDoc)
  document.removeEventListener('keydown', onKey)
  window.removeEventListener('scroll', onScroll, true)
  window.removeEventListener('resize', onScroll)
})
</script>

<template>
  <span ref="triggerRef" class="more-wrap">
    <UiButton size="sm" variant="ghost" @click="toggle">
      <UiIcon name="more" :size="14" />
    </UiButton>
  </span>
  <Teleport to="body">
    <Transition name="pop">
      <div v-if="open" ref="menuRef" class="menu" :style="{ top: pos.top + 'px', right: pos.right + 'px' }">
        <button @click="close(); emit('rename', device)"><UiIcon name="brush" :size="14" /> Rename device</button>
        <button @click="close(); emit('trust', device)"><UiIcon name="shield" :size="14" /> {{ device.trusted ? 'Distrust device' : 'Trust this device · skip MFA for 30d' }}</button>
        <button @click="close(); emit('history', device)"><UiIcon name="file" :size="14" /> View sign-in history</button>
        <span class="sep" />
        <button
          class="danger"
          :disabled="device.current"
          @click="close(); !device.current && emit('revoke', device)"
        >
          <UiIcon name="logout" :size="14" />
          {{ device.current ? 'Cannot revoke current device' : 'Revoke session' }}
        </button>
      </div>
    </Transition>
  </Teleport>
</template>

<style scoped>
.more-wrap { display: inline-flex; }

.menu {
  position: fixed;
  min-width: 260px;
  padding: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  z-index: 100;
}
.menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border-radius: 5px;
  background: transparent;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  text-align: left;
  color: var(--text);
}
.menu button:hover { background: var(--row-hover); }
.menu button:disabled { opacity: 0.5; cursor: not-allowed; }
.menu .danger { color: var(--bad); }
.menu .sep { display: block; height: 1px; background: var(--border); margin: 4px 0; }

.pop-enter-active, .pop-leave-active { transition: opacity 0.12s, transform 0.12s; }
.pop-enter-from, .pop-leave-to { opacity: 0; transform: translateY(-4px); }
</style>
