<template>
  <Teleport to="body">
    <Transition name="app-launcher">
      <div
        v-if="store.isOpen"
        class="fixed inset-0 z-[2400] flex items-center justify-center p-6 md:p-10"
        @click.self="store.close()"
      >
        <!-- Backdrop - blur like spotlight -->
        <div class="absolute inset-0 bg-black/60 backdrop-blur-md"></div>

        <!-- Panel - inset with margins, glass style like spotlight -->
        <div
          class="app-launcher-panel relative z-10 flex flex-col overflow-hidden rounded-2xl shadow-2xl"
          :class="panelClasses"
        >
          <!-- Header bar - drag handle + title + close -->
          <div class="flex items-center gap-3 border-b border-white/10 px-4 py-3">
            <div class="flex items-center justify-center w-8 h-8 shrink-0 rounded cursor-grab hover:bg-white/10 transition-colors">
              <svg class="w-4 h-4 text-white/50" fill="currentColor" viewBox="0 0 24 24">
                <path d="M8 6h2v2H8V6zm0 5h2v2H8v-2zm0 5h2v2H8v-2zm5-10h2v2h-2V6zm0 5h2v2h-2v-2zm0 5h2v2h-2v-2z" />
              </svg>
            </div>
            <span class="flex-1 truncate text-sm font-medium text-white/90">{{ store.title || 'App' }}</span>
            <button
              type="button"
              class="flex items-center justify-center w-9 h-9 rounded-lg hover:bg-white/15 text-white/70 hover:text-white transition-colors disabled:opacity-70"
              aria-label="Refresh"
              :disabled="isRefreshing"
              @click="refreshIframe"
            >
              <svg
                class="w-5 h-5 transition-transform duration-300"
                :class="{ 'animate-spin': isRefreshing }"
                fill="none"
                stroke="currentColor"
                viewBox="0 0 24 24"
              >
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
              </svg>
            </button>
            <button
              ref="closeBtnRef"
              type="button"
              class="flex items-center justify-center w-9 h-9 rounded-lg hover:bg-white/15 text-white/70 hover:text-white transition-colors"
              aria-label="Close"
              @click="store.close()"
            >
              <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
              </svg>
            </button>
            <kbd class="hidden sm:inline-flex px-2 py-1 text-xs text-white/50 bg-white/10 rounded">Esc</kbd>
          </div>

          <!-- Iframe container - overflow hidden to clip inner scrollbars -->
          <div class="relative flex-1 min-h-0 bg-black/40 overflow-hidden">
            <iframe
              ref="iframeRef"
              v-if="store.url"
              :key="iframeRefreshKey"
              :src="store.url"
              class="absolute inset-0 w-full h-full border-0 iframe-scrollbar-hide"
              title="App content"
              @load="onIframeLoad"
            />
          </div>
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<script setup lang="ts">
import { ref, watch, onMounted, onBeforeUnmount } from 'vue'
import { useAppLauncherStore } from '@/stores/appLauncher'

const store = useAppLauncherStore()
const closeBtnRef = ref<HTMLButtonElement | null>(null)
const iframeRef = ref<HTMLIFrameElement | null>(null)
const iframeRefreshKey = ref(0)
const isRefreshing = ref(false)

function refreshIframe() {
  isRefreshing.value = true
  iframeRefreshKey.value++
}

function onIframeLoad() {
  injectScrollbarHideIfSameOrigin()
  isRefreshing.value = false
}

function injectScrollbarHideIfSameOrigin() {
  try {
    const doc = iframeRef.value?.contentDocument
    if (!doc) return
    const style = doc.createElement('style')
    style.textContent = `
      * { -ms-overflow-style: none; scrollbar-width: none; }
      *::-webkit-scrollbar { display: none; }
    `
    doc.head.appendChild(style)
    // Escape from inside iframe → close overlay and return focus to launcher
    doc.addEventListener('keydown', (e) => {
      if ((e as KeyboardEvent).key === 'Escape') {
        e.preventDefault()
        window.parent.postMessage({ type: 'app-launcher-escape' }, '*')
      }
    })
  } catch {
    /* Cross-origin: cannot access iframe document */
  }
}

const panelClasses = [
  'glass-card',
  'w-full h-full max-w-[calc(100vw-3rem)] max-h-[calc(100vh-5rem)]',
  'md:max-w-[calc(100vw-5rem)] md:max-h-[calc(100vh-5rem)]',
]

function onKeyDown(e: KeyboardEvent) {
  if (e.key === 'Escape' && store.isOpen) {
    store.close()
    e.preventDefault()
    e.stopPropagation()
  }
}

function onMessage(e: MessageEvent) {
  if (e.data?.type === 'app-launcher-escape' && store.isOpen) {
    store.close()
  }
}

watch(
  () => store.isOpen,
  (open) => {
    if (open) {
      closeBtnRef.value?.focus()
    } else {
      isRefreshing.value = false
    }
  }
)

onMounted(() => {
  window.addEventListener('keydown', onKeyDown, true)
  window.addEventListener('message', onMessage)
})

onBeforeUnmount(() => {
  window.removeEventListener('keydown', onKeyDown, true)
  window.removeEventListener('message', onMessage)
})
</script>

<style scoped>
.app-launcher-enter-active,
.app-launcher-leave-active {
  transition: opacity 0.25s ease;
}
.app-launcher-enter-active .app-launcher-panel,
.app-launcher-leave-active .app-launcher-panel {
  transition: transform 0.25s ease, opacity 0.25s ease;
}
.app-launcher-enter-from,
.app-launcher-leave-to {
  opacity: 0;
}
.app-launcher-enter-from .app-launcher-panel,
.app-launcher-leave-to .app-launcher-panel {
  transform: scale(0.96);
  opacity: 0;
}
</style>
