<template>
  <div class="app-session-root">
  <Teleport to="body" :disabled="isInlinePanel">
  <div
    :class="backdropClasses"
    @click.self="handleBackdropClick"
  >
    <div
      ref="sessionRef"
      :class="panelClasses"
      @click.stop
    >
      <AppSessionHeader
        :app-title="appTitle"
        :is-refreshing="isRefreshing"
        :display-mode="displayMode"
        @go-back="iframeGoBack"
        @go-forward="iframeGoForward"
        @refresh="refresh"
        @open-new-tab="openNewTab"
        @close="closeSession"
        @set-mode="setMode"
      />

      <AppSessionFrame
        ref="frameRef"
        :app-url="appUrl"
        :app-id="appId"
        :app-title="appTitle"
        :loading="loading"
        :iframe-blocked="iframeBlocked"
        :must-open-new-tab="mustOpenNewTab"
        :auto-retry-count="autoRetryCount"
        :refresh-key="refreshKey"
        @iframe-load="onLoad"
        @iframe-error="onError"
        @refresh="refresh"
        @open-new-tab-and-back="openNewTabAndBack"
      />

      <!-- Mobile: gamepad for botfights (with utility buttons), browser bar for everything else -->
      <MobileGamepad
        v-if="isMobile && appId === 'botfights'"
        :iframe-ref="iframeRef ?? null"
        :player="1"
        @refresh="refresh"
        @openBrowser="openNewTab"
        @close="closeSession"
      />
      <div v-else class="md:hidden app-session-mobile-bar">
        <button class="app-session-bar-btn" aria-label="Back" @click="iframeGoBack">
          <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
          </svg>
        </button>
        <button class="app-session-bar-btn" aria-label="Forward" @click="iframeGoForward">
          <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
          </svg>
        </button>
        <button class="app-session-bar-btn" aria-label="Open in new tab" @click="openNewTab">
          <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
          </svg>
        </button>
        <button class="app-session-bar-btn" aria-label="Close" @click="closeSession">
          <svg 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>
      </div>
    </div>

    <NostrIdentityPicker
      :show="showIdentityPicker"
      :app-name="appTitle"
      @select="identity.onIdentitySelected"
      @cancel="showIdentityPicker = false"
    />
  </div>
  </Teleport>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useAppLauncherStore } from '@/stores/appLauncher'
import NostrIdentityPicker from '@/components/NostrIdentityPicker.vue'
import AppSessionHeader from './appSession/AppSessionHeader.vue'
import AppSessionFrame from './appSession/AppSessionFrame.vue'
import MobileGamepad from './appSession/MobileGamepad.vue'
import {
  type DisplayMode, DISPLAY_MODE_KEY, NEW_TAB_APPS, IFRAME_BLOCKED_APPS,
  resolveAppUrl, resolveAppTitle,
} from './appSession/appSessionConfig'
import { useAppIdentity } from './appSession/useAppIdentity'
import { useNostrBridge } from './appSession/useNostrBridge'

const props = defineProps<{
  appIdProp?: string
}>()

const emit = defineEmits<{
  close: []
}>()

/** True when rendered inline via store (panel mode), false when route-based */
const isInlinePanel = computed(() => !!props.appIdProp)

const route = useRoute()
const router = useRouter()

const sessionRef = ref<HTMLElement | null>(null)
const frameRef = ref<InstanceType<typeof AppSessionFrame> | null>(null)
const loading = ref(true)
const isRefreshing = ref(false)
const iframeBlocked = ref(false)
const refreshKey = ref(0)
const showIdentityPicker = ref(false)
const autoRetryCount = ref(0)
let loadTimeoutId: ReturnType<typeof setTimeout> | null = null
let autoRetryId: ReturnType<typeof setTimeout> | null = null
let iframeCheckId: ReturnType<typeof setTimeout> | null = null

// Display mode -- persisted in localStorage
const displayMode = ref<DisplayMode>(
  (localStorage.getItem(DISPLAY_MODE_KEY) as DisplayMode) || 'panel'
)

const appId = computed(() => {
  const id = props.appIdProp || (route.params.appId as string)
  if (typeof id !== 'string' || !/^[a-z0-9][a-z0-9._-]*$/.test(id) || id.length > 64) {
    router.replace('/apps')
    return ''
  }
  return id
})

const appTitle = computed(() => resolveAppTitle(appId.value))
const isMobile = typeof window !== 'undefined' && window.innerWidth < 768
// On mobile (Android WebView), all apps load in the iframe — X-Frame-Options
// doesn't apply since the WebView is the top-level browsing context.
const mustOpenNewTab = computed(() => isMobile ? false : NEW_TAB_APPS.has(appId.value))

const appUrl = computed(() => {
  return resolveAppUrl(appId.value, route.query.path as string | undefined)
})

// --- Identity & Nostr bridge ---

const iframeRef = computed(() => frameRef.value?.iframeRef ?? null)

const identity = useAppIdentity(appId, iframeRef, showIdentityPicker)
const nostrBridge = useNostrBridge(identity.getStoredIdentity, () => appUrl.value)

// --- Display mode ---

function setMode(mode: DisplayMode) {
  if (displayMode.value === 'fullscreen' && document.fullscreenElement) {
    document.exitFullscreen().catch(() => {})
  }
  displayMode.value = mode
  localStorage.setItem(DISPLAY_MODE_KEY, mode)

  // Switch from inline panel to route-based overlay/fullscreen
  if (isInlinePanel.value && mode !== 'panel') {
    const id = appId.value
    emit('close')
    router.push({ name: 'app-session', params: { appId: id } })
    return
  }

  // Switch from route-based to inline panel
  if (!isInlinePanel.value && mode === 'panel') {
    const id = appId.value
    const launcher = useAppLauncherStore()
    router.push({ name: 'apps' }).then(() => {
      launcher.panelAppId = id
    })
    return
  }

  if (mode === 'fullscreen' && sessionRef.value && !document.fullscreenElement) {
    sessionRef.value.requestFullscreen().catch(() => {})
  }
}

// Reactive classes based on display mode
const backdropClasses = computed(() => {
  if (isInlinePanel.value) return 'app-session-backdrop-inline'
  return 'app-session-backdrop-overlay'
})

const panelClasses = computed(() => {
  const base = 'app-session-panel glass-card'
  if (isInlinePanel.value) return `${base} app-session-inline`
  if (displayMode.value === 'fullscreen') return `${base} app-session-fullscreen`
  return `${base} app-session-overlay`
})

// --- Lifecycle handlers ---

function onLoad() {
  if (loadTimeoutId) { clearTimeout(loadTimeoutId); loadTimeoutId = null }
  if (autoRetryId) { clearTimeout(autoRetryId); autoRetryId = null }
  loading.value = false
  isRefreshing.value = false
  autoRetryCount.value = 0
  // Check if iframe actually loaded content (same-origin only)
  iframeCheckId = setTimeout(() => {
    try {
      const iframe = frameRef.value?.iframeRef
      const doc = iframe?.contentDocument
      if (doc) {
        const body = doc.body
        if (!body || (body.children.length === 0 && body.innerText.trim() === '')) {
          iframeBlocked.value = true
        }
      }
    } catch {
      // Cross-origin -- can't check, assume OK
    }
  }, 1000)
  identity.onIframeLoadIdentity()
}

function onError() {
  if (loadTimeoutId) { clearTimeout(loadTimeoutId); loadTimeoutId = null }
  loading.value = false
  isRefreshing.value = false
  iframeBlocked.value = true
  // Auto-retry up to 6 times (60s total) for apps that are still starting
  if (!mustOpenNewTab.value && autoRetryCount.value < 6) {
    autoRetryId = setTimeout(() => {
      autoRetryCount.value++
      refresh()
    }, 10000)
  }
}

function refresh() {
  if (autoRetryId) { clearTimeout(autoRetryId); autoRetryId = null }
  isRefreshing.value = true
  loading.value = true
  iframeBlocked.value = false
  refreshKey.value++
  startLoadTimeout()
}

function startLoadTimeout() {
  if (loadTimeoutId) clearTimeout(loadTimeoutId)
  loadTimeoutId = setTimeout(() => {
    if (loading.value) {
      loading.value = false
      iframeBlocked.value = true
    }
  }, 12000)
}

function openNewTabAndBack() {
  if (appUrl.value) window.open(appUrl.value, '_blank', 'noopener,noreferrer')
  closeSession()
}

function openNewTab() {
  if (appUrl.value) window.open(appUrl.value, '_blank', 'noopener,noreferrer')
}

function iframeGoBack() {
  try { frameRef.value?.iframeRef?.contentWindow?.history.back() } catch {}
}

function iframeGoForward() {
  try { frameRef.value?.iframeRef?.contentWindow?.history.forward() } catch {}
}

function handleBackdropClick() {
  closeSession()
}

function closeSession() {
  if (document.fullscreenElement) document.exitFullscreen().catch(() => {})
  if (isInlinePanel.value) emit('close')
  else router.back()
}

function onKeyDown(e: KeyboardEvent) {
  if (e.key === 'Escape') {
    if (document.fullscreenElement) document.exitFullscreen().catch(() => {})
    else closeSession()
    e.preventDefault()
  }
}

function onFullscreenChange() {
  if (!document.fullscreenElement && displayMode.value === 'fullscreen') {
    displayMode.value = 'overlay'
    localStorage.setItem(DISPLAY_MODE_KEY, 'overlay')
  }
}

function onMessage(e: MessageEvent) {
  if (e.data?.type === 'nostr-request') nostrBridge.handleNostrRequest(e)
  if (e.data?.type === 'archipelago:identity:request') identity.handleIdentityRequest()
}

// Enter fullscreen on mount if mode is fullscreen
watch(displayMode, (mode) => {
  if (mode === 'fullscreen' && sessionRef.value && !document.fullscreenElement) {
    sessionRef.value.requestFullscreen().catch(() => {})
  }
})

onMounted(() => {
  // Apps that block iframes (X-Frame-Options) -- open in new tab, close session
  if (mustOpenNewTab.value && appUrl.value) {
    window.open(appUrl.value, '_blank', 'noopener,noreferrer')
    if (isInlinePanel.value) emit('close')
    else router.back()
    return
  }

  window.addEventListener('keydown', onKeyDown, true)
  window.addEventListener('message', onMessage)
  document.addEventListener('fullscreenchange', onFullscreenChange)
  if (IFRAME_BLOCKED_APPS.has(appId.value)) {
    loading.value = false
    iframeBlocked.value = true
  } else {
    startLoadTimeout()
  }
  if (displayMode.value === 'fullscreen') {
    requestAnimationFrame(() => {
      sessionRef.value?.requestFullscreen().catch(() => {})
    })
  }
})

onBeforeUnmount(() => {
  if (loadTimeoutId) clearTimeout(loadTimeoutId)
  if (autoRetryId) clearTimeout(autoRetryId)
  if (iframeCheckId) clearTimeout(iframeCheckId)
  window.removeEventListener('keydown', onKeyDown, true)
  window.removeEventListener('message', onMessage)
  document.removeEventListener('fullscreenchange', onFullscreenChange)
  if (document.fullscreenElement) document.exitFullscreen().catch(() => {})
})
</script>

<style>
/* Unscoped so children can use these classes */
.app-session-root {
  width: 100%;
  height: 100%;
}
/* Inline panel mode -- fills content area, no blur, original layout */
.app-session-backdrop-inline {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.4);
  padding: 0;
}

.app-session-inline {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  width: 100%;
  height: 100%;
  border-radius: 0;
  border: none;
}

@media (min-width: 768px) {
  .app-session-backdrop-inline {
    padding: 1.5rem;
  }
  .app-session-inline {
    border-radius: 1rem;
    max-width: calc(100% - 1rem);
    max-height: calc(100vh - 6rem);
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
  }
}

/* Overlay mode -- covers entire viewport including sidebar */
.app-session-backdrop-overlay {
  position: fixed;
  inset: 0;
  z-index: 2400;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(12px);
}

@media (min-width: 768px) {
  .app-session-backdrop-overlay {
    padding: 2.5rem;
  }
}

.app-session-overlay {
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  width: 100%;
  height: 100%;
  border-radius: 0;
  border: none;
  box-shadow: none;
}

@media (min-width: 768px) {
  .app-session-overlay {
    max-width: calc(100vw - 5rem);
    max-height: calc(100vh - 5rem);
    border-radius: 1rem;
  }
}

/* Fullscreen mode */
.app-session-fullscreen {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  width: 100%;
  height: 100%;
  border-radius: 0 !important;
  max-width: none !important;
  max-height: none !important;
}

/* Shared */
.app-session-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  color: rgba(255, 255, 255, 0.7);
  transition: all 0.15s ease;
  flex-shrink: 0;
}
.app-session-btn:hover {
  background: rgba(255, 255, 255, 0.15);
  color: white;
}
.app-session-btn:disabled {
  opacity: 0.5;
}

/* Mode dropdown */
.mode-option {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 14px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.7);
  transition: all 0.15s ease;
  text-align: left;
}
.mode-option:hover {
  background: rgba(255, 255, 255, 0.08);
  color: white;
}
.mode-option-active {
  color: #fb923c;
  background: rgba(251, 146, 60, 0.08);
}

.menu-fade-enter-active,
.menu-fade-leave-active {
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.menu-fade-enter-from,
.menu-fade-leave-to {
  opacity: 0;
  transform: translateY(-4px);
}

.content-fade-enter-active,
.content-fade-leave-active {
  transition: opacity 0.2s ease;
}
.content-fade-enter-from,
.content-fade-leave-to {
  opacity: 0;
}

/* Mobile: full-bleed app sessions — no border, no radius, no shadow */
@media (max-width: 767px) {
  .app-session-panel.glass-card {
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
  }
  .app-session-backdrop-overlay {
    padding: 0;
    backdrop-filter: none;
    background: black;
  }
  /* Iframe frame: push content below status bar on mobile */
  .app-session-frame-safe {
    padding-top: var(--safe-area-top, env(safe-area-inset-top, 0px));
  }
  /* Iframe within padded container: fill remaining space */
  .app-session-frame-safe iframe {
    top: var(--safe-area-top, env(safe-area-inset-top, 0px));
    height: calc(100% - var(--safe-area-top, env(safe-area-inset-top, 0px)));
  }
}

/* Mobile bottom browser bar — sized like the main tab bar.
   Uses !important-free display so Tailwind md:hidden can override. */
@media (min-width: 768px) {
  .app-session-mobile-bar { display: none !important; }
}
.app-session-mobile-bar {
  display: flex;
  justify-content: space-around;
  align-items: center;
  flex-shrink: 0;
  padding: 12px 16px;
  padding-bottom: calc(12px + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
  background: rgba(0, 0, 0, 0.25);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.app-session-bar-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 14px;
  color: rgba(255, 255, 255, 0.65);
  transition: color 0.15s ease, background 0.15s ease;
}
.app-session-bar-btn svg {
  width: 24px;
  height: 24px;
}
.app-session-bar-btn:active {
  color: white;
  background: rgba(255, 255, 255, 0.12);
}
</style>
