<template>
  <Teleport to="body">
    <Transition name="overlay-fade">
      <div
        v-if="visible"
        class="fixed inset-0 flex items-end sm:items-center justify-center p-4 z-[3000]"
        @click.self="dismiss"
      >
        <div class="absolute inset-0 bg-black/40 backdrop-blur-sm" />
        <div
          class="glass-card p-5 w-full max-w-sm relative z-10 mb-20 sm:mb-0 overflow-hidden"
          @click.stop
        >
          <button
            type="button"
            class="absolute right-3 top-3 h-8 w-8 rounded-full bg-white/5 border border-white/10 text-white/60 hover:text-white hover:bg-white/10 transition-colors z-10"
            aria-label="Close companion modal"
            @click="dismiss"
          >
            &times;
          </button>

          <Transition :name="slideName" mode="out-in">
            <!-- Screen 1: get the app -->
            <div v-if="step === 'download'" key="download">
              <div class="flex items-start gap-4 mb-4">
                <div class="w-12 h-12 rounded-xl bg-orange-500/15 border border-orange-500/30 flex items-center justify-center flex-shrink-0">
                  <svg class="w-7 h-7 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                    <rect x="3" y="7" width="18" height="11" rx="3" stroke-width="1.5" />
                    <rect x="7.5" y="10" width="2" height="5" rx="0.5" fill="currentColor" />
                    <rect x="6" y="11.5" width="5" height="2" rx="0.5" fill="currentColor" />
                    <circle cx="16" cy="11" r="1.2" fill="currentColor" />
                    <circle cx="14" cy="13.5" r="1.2" fill="currentColor" />
                  </svg>
                </div>
                <div class="min-w-0 flex-1">
                  <h3 class="text-lg font-semibold text-white mb-1">Remote Companion</h3>
                  <p class="text-sm text-white/60 leading-relaxed">
                    Install the Archipelago companion app on your phone, scan the code, and connect to the same node.
                  </p>
                </div>
              </div>

              <div class="hidden md:flex justify-center mb-4">
                <div class="w-[128px] rounded-2xl border border-white/10 bg-white/[0.03] p-1 overflow-hidden">
                  <img
                    v-if="qrDataUrl"
                    :src="qrDataUrl"
                    alt="Companion app download QR code"
                    class="block w-full max-w-full h-auto rounded-lg bg-white"
                  />
                  <div v-else class="w-full aspect-square rounded-lg bg-white/5"></div>
                </div>
              </div>

              <div class="flex gap-2">
                <a
                  :href="companionDownloadUrl"
                  class="flex-1 inline-flex items-center justify-center rounded-lg bg-orange-500/20 border border-orange-500/30 px-3 py-2.5 text-sm font-medium text-orange-400 hover:bg-orange-500/30 transition-colors text-center"
                  target="_blank"
                  rel="noopener noreferrer"
                  download
                >
                  Download app
                </a>
                <button
                  type="button"
                  class="flex-1 rounded-lg bg-white/5 border border-white/15 px-3 py-2.5 text-sm font-medium text-white/80 hover:bg-white/10 hover:text-white transition-colors"
                  @click="showPairScreen"
                >
                  I've installed it
                </button>
              </div>
            </div>

            <!-- Screen 2: pair the app with this node -->
            <div v-else key="pair">
              <div class="flex items-start gap-4 mb-4">
                <div class="w-12 h-12 rounded-xl bg-orange-500/15 border border-orange-500/30 flex items-center justify-center flex-shrink-0">
                  <svg class="w-7 h-7 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                    <rect x="4" y="4" width="7" height="7" rx="1.5" stroke-width="1.5" />
                    <rect x="13" y="4" width="7" height="7" rx="1.5" stroke-width="1.5" />
                    <rect x="4" y="13" width="7" height="7" rx="1.5" stroke-width="1.5" />
                    <path d="M13 13h3v3h-3zM17 17h3v3h-3z" fill="currentColor" stroke="none" />
                  </svg>
                </div>
                <div class="min-w-0 flex-1">
                  <h3 class="text-lg font-semibold text-white mb-1">Connect your app</h3>
                  <p class="text-sm text-white/60 leading-relaxed">
                    In the companion app, choose “Scan Node's QR” and point your phone here — the server details fill in automatically.
                  </p>
                </div>
              </div>

              <div class="flex justify-center mb-4">
                <div class="w-[128px] rounded-2xl border border-white/10 bg-white/[0.03] p-1 overflow-hidden">
                  <img
                    v-if="pairQrDataUrl"
                    :src="pairQrDataUrl"
                    alt="Companion app pairing QR code"
                    class="block w-full max-w-full h-auto rounded-lg bg-white"
                  />
                  <div v-else class="w-full aspect-square rounded-lg bg-white/5"></div>
                </div>
              </div>

              <!-- Same-device path: a QR on the phone's own screen can't be
                   scanned, so offer the deep link directly on small screens. -->
              <a
                v-if="pairingUrl"
                :href="pairingUrl"
                class="md:hidden inline-flex w-full items-center justify-center rounded-lg bg-orange-500/20 border border-orange-500/30 px-4 py-2.5 text-sm font-medium text-orange-400 hover:bg-orange-500/30 transition-colors mb-2"
              >
                Open in companion app
              </a>

              <button
                type="button"
                class="w-full py-2.5 rounded-lg bg-white/5 border border-white/15 text-white/80 text-sm font-medium hover:bg-white/10 hover:text-white transition-colors"
                @click="showDownloadScreen"
              >
                Back
              </button>
            </div>
          </Transition>
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import * as QRCode from 'qrcode'
import { IS_DEMO, DEMO_PASSWORD } from '@/composables/useDemoIntro'
import { companionIntroRequested } from '@/composables/useCompanionIntro'
import { useLoginTransitionStore } from '@/stores/loginTransition'
import { rpcClient } from '@/api/rpc-client'

const STORAGE_KEY = 'neode_companion_intro_seen'
// Absolute URL so the QR works when scanned by a phone (a relative path has no
// host to resolve). Points at the companion APK hosted on the 146 release server
// (publicly reachable) rather than the local node's /packages copy.
// The demo serves the APK from its own public origin instead, so the QR never
// exposes the release-server address.
const DEFAULT_DOWNLOAD_URL = IS_DEMO
  ? `${window.location.origin}/packages/archipelago-companion.apk`
  : 'http://146.59.87.168:3000/lfg2025/archy/raw/branch/main/neode-ui/public/packages/archipelago-companion.apk'

// Deep-link scheme the companion app registers; carries the server entry the
// app should create (see docs/companion-pairing-qr.md for the contract).
const PAIR_SCHEME = 'archipelago://pair'
const DEMO_SERVER_URL = 'https://demo.archipelago-foundation.org'

const visible = ref(false)
const step = ref<'download' | 'pair'>('download')
const slideName = ref('slide-forward')
const qrDataUrl = ref('')
const pairQrDataUrl = ref('')
const pairingUrl = ref('')
const companionDownloadUrl = import.meta.env.VITE_COMPANION_APK_URL || DEFAULT_DOWNLOAD_URL

const loginTransition = useLoginTransitionStore()

// Base delay before the popup may appear, and extra breathing room after the
// dashboard entrance cinematic ends so the popup never cuts into the reveal.
const BASE_DELAY_MS = 5000
const POST_INTRO_GRACE_MS = 2000

let calmTicker: ReturnType<typeof setInterval> | null = null

onMounted(() => {
  try {
    if (localStorage.getItem(STORAGE_KEY) !== '1') {
      setTimeout(maybeShow, BASE_DELAY_MS)
    }
  } catch {
    // localStorage unavailable
  }
})

onUnmounted(() => {
  if (calmTicker) clearInterval(calmTicker)
})

function maybeShow() {
  // Show only after the scene has been CONTINUOUSLY calm (no reveal
  // cinematic) for the full grace window. The previous point-in-time check
  // raced a slow-starting reveal — on a cold cache the entrance video can
  // begin buffering after the 5s base delay, so the flag was still false
  // when sampled and the popup cut straight into the cinematic.
  let calmSince = loginTransition.introCinematicPlaying ? null : Date.now()
  calmTicker = setInterval(() => {
    if (loginTransition.introCinematicPlaying) {
      calmSince = null
      return
    }
    if (calmSince === null) calmSince = Date.now()
    if (Date.now() - calmSince >= POST_INTRO_GRACE_MS) {
      if (calmTicker) clearInterval(calmTicker)
      calmTicker = null
      visible.value = true
    }
  }, 250)
}

// Manual open (App Store banner etc.) — ignores the once-per-browser gate.
watch(companionIntroRequested, (requested) => {
  if (!requested) return
  companionIntroRequested.value = false
  if (calmTicker) {
    clearInterval(calmTicker)
    calmTicker = null
  }
  step.value = 'download'
  visible.value = true
})

watch(visible, async (isVisible) => {
  if (!isVisible) return
  qrDataUrl.value = await QRCode.toDataURL(companionDownloadUrl, {
    width: 112,
    margin: 1,
    errorCorrectionLevel: 'M',
    color: {
      dark: '#111111',
      light: '#ffffff',
    },
  })
}, { immediate: true, flush: 'post' })

/**
 * The server URL the companion app should connect to. The demo advertises its
 * public https origin; a real node advertises whatever address the browser is
 * already using — except on the kiosk, where that is localhost and useless to
 * a phone, so fall back to the node's mDNS .local name.
 */
async function resolveServerUrl(): Promise<string> {
  if (IS_DEMO) return DEMO_SERVER_URL
  const { hostname, origin } = window.location
  if (hostname !== 'localhost' && hostname !== '127.0.0.1') return origin
  try {
    const res = await rpcClient.call<{ mdns_hostname?: string }>({ method: 'system.get-hostname' })
    if (res?.mdns_hostname) return `http://${res.mdns_hostname}`
  } catch {
    // RPC unavailable — fall through to the (localhost) origin
  }
  return origin
}

async function buildPairingUrl(): Promise<string> {
  const params = new URLSearchParams({ v: '1', url: await resolveServerUrl() })
  // Only the shared demo password ever rides in the QR; real node passwords
  // are never available to the frontend and stay typed-by-hand in the app.
  if (IS_DEMO) params.set('pw', DEMO_PASSWORD)
  return `${PAIR_SCHEME}?${params.toString()}`
}

async function showPairScreen() {
  slideName.value = 'slide-forward'
  step.value = 'pair'
  if (!pairQrDataUrl.value) {
    pairingUrl.value = await buildPairingUrl()
    pairQrDataUrl.value = await QRCode.toDataURL(pairingUrl.value, {
      width: 112,
      margin: 1,
      errorCorrectionLevel: 'M',
      color: {
        dark: '#111111',
        light: '#ffffff',
      },
    })
  }
}

function showDownloadScreen() {
  slideName.value = 'slide-back'
  step.value = 'download'
}

function dismiss() {
  visible.value = false
  step.value = 'download'
  try {
    localStorage.setItem(STORAGE_KEY, '1')
  } catch {
    // ignore
  }
}
</script>

<style scoped>
.overlay-fade-enter-active { transition: opacity 0.3s ease; }
.overlay-fade-leave-active { transition: opacity 0.2s ease; }
.overlay-fade-enter-from,
.overlay-fade-leave-to { opacity: 0; }
.overlay-fade-enter-active .glass-card { transition: transform 0.3s ease; }
.overlay-fade-enter-from .glass-card { transform: translateY(20px); }

/* Horizontal slide between the download and pairing screens */
.slide-forward-enter-active,
.slide-forward-leave-active,
.slide-back-enter-active,
.slide-back-leave-active { transition: transform 0.25s ease, opacity 0.25s ease; }
.slide-forward-enter-from { transform: translateX(40px); opacity: 0; }
.slide-forward-leave-to { transform: translateX(-40px); opacity: 0; }
.slide-back-enter-from { transform: translateX(-40px); opacity: 0; }
.slide-back-leave-to { transform: translateX(40px); opacity: 0; }
</style>
