<template>
  <div class="min-h-screen">
    <BootScreen :visible="showBootScreen" @ready="onServerReady" />
    <div v-if="!showBootScreen" class="min-h-screen flex items-center justify-center">
      <div class="flex flex-col items-center gap-4 opacity-0 root-redirect-fade">
        <svg class="animate-spin h-8 w-8 text-white/60" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
          <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
          <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
        </svg>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { isOnboardingComplete } from '@/composables/useOnboarding'
import BootScreen from '@/components/BootScreen.vue'

const router = useRouter()
const showBootScreen = ref(false)

function log(msg: string, data?: unknown) {
  const ts = new Date().toISOString()
  const entry = `[RootRedirect ${ts}] ${msg}` + (data !== undefined ? ` ${JSON.stringify(data)}` : '')
  console.log(entry)
  const prev = sessionStorage.getItem('archipelago_boot_log') || ''
  sessionStorage.setItem('archipelago_boot_log', prev + entry + '\n')
}

async function quickHealthCheck(): Promise<boolean> {
  try {
    const ac = new AbortController()
    const t = setTimeout(() => ac.abort(), 2000)
    const res = await fetch('/rpc/v1', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'server.echo', params: { message: 'ping' } }),
      signal: ac.signal,
    })
    clearTimeout(t)
    const ok = res.status !== 502 && res.status !== 503
    log('healthCheck', { status: res.status, ok })
    return ok
  } catch (e) {
    log('healthCheck FAILED', { error: String(e) })
    return false
  }
}

async function checkOnboarded(): Promise<boolean> {
  // No hard timeout here. isOnboardingComplete() already retries with
  // backoff (see useOnboarding.ts). A 3s Promise.race that resolves to
  // `false` on timeout was previously the main cause of the intro
  // flashing on already-onboarded nodes after browser-clear / reboot /
  // update: if the backend was slow to warm up, we'd force a 'false'
  // and route the user back through the setup wizard.
  try {
    const result = await isOnboardingComplete()
    log('checkOnboarded', { result })
    return result
  } catch (e) {
    const fallback = localStorage.getItem('neode_onboarding_complete') === '1'
    log('checkOnboarded ERROR, localStorage fallback', { error: String(e), fallback })
    return fallback
  }
}

async function proceedToApp() {
  const devMode = import.meta.env.VITE_DEV_MODE
  if (devMode === 'setup' || devMode === 'existing') {
    log('proceedToApp devMode', { devMode })
    router.replace('/login').catch(() => {})
    return
  }

  const onboarded = await checkOnboarded()
  const dest = onboarded ? '/login' : '/onboarding/intro'
  log('proceedToApp navigating', { onboarded, dest })
  router.replace(dest).catch(() => {})
}

function onServerReady() {
  if (import.meta.env.DEV) console.log('[RootRedirect] onServerReady — setting flag and reloading')
  localStorage.removeItem('neode_intro_seen')
  // Do NOT clear neode_onboarding_complete here — that flag must persist
  // across boot screen reloads so completed onboarding isn't lost.
  sessionStorage.setItem('archipelago_from_boot', '1')
  window.location.href = '/'
}

onMounted(async () => {
  const devMode = import.meta.env.VITE_DEV_MODE
  log('mounted', { devMode, from_boot: sessionStorage.getItem('archipelago_from_boot'), from_splash: sessionStorage.getItem('archipelago_from_splash') })

  if (sessionStorage.getItem('archipelago_from_boot') === '1') {
    log('from_boot=1, deferring to SplashScreen')
    return
  }

  if (sessionStorage.getItem('archipelago_from_splash') === '1') {
    log('from_splash=1, proceedToApp')
    proceedToApp()
    return
  }

  if (devMode === 'setup' || devMode === 'existing') {
    log('devMode shortcut', { devMode })
    proceedToApp()
    return
  }

  if (devMode === 'boot') {
    log('devMode=boot, showing boot screen')
    showBootScreen.value = true
    return
  }

  const isUp = await quickHealthCheck()
  log('production flow', { isUp })

  if (isUp) {
    const onboarded = await checkOnboarded()
    if (onboarded) {
      log('server up + onboarded → proceedToApp')
      proceedToApp()
      return
    }
    log('server up + NOT onboarded → onboarding intro')
    router.replace('/onboarding/intro').catch(() => {})
    return
  }

  // Server not ready. The full BootScreen is meant for a genuine
  // cold-start (fresh install), not for the brief blip during an
  // OTA update where the backend restarts. If onboarding has already
  // completed we just keep the spinner and retry until the server
  // responds again.
  const wasOnboardedBefore = localStorage.getItem('neode_onboarding_complete') === '1'
  if (wasOnboardedBefore) {
    log('server down + onboarded → polling without boot screen')
    let retries = 0
    const maxRetries = 30 // 30 * 2s = 60s before giving up and showing boot screen
    const poll = setInterval(async () => {
      retries++
      if (await quickHealthCheck()) {
        clearInterval(poll)
        proceedToApp()
        return
      }
      if (retries >= maxRetries) {
        clearInterval(poll)
        log('server still down after retries → falling back to boot screen')
        showBootScreen.value = true
      }
    }, 2000)
    return
  }
  showBootScreen.value = true
})
</script>

<style scoped>
.root-redirect-fade {
  animation: root-fade-in 0.3s ease 0.5s forwards;
}
@keyframes root-fade-in {
  to { opacity: 1; }
}
</style>
