<script setup>
import { useTranslation } from 'i18next-vue'
import { computed, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import AppUpdateDialog from './components/AppUpdateDialog.vue'
import BackupReminder from './components/BackupReminder.vue'
import FirstLoadWarning from './components/FirstLoadWarning.vue'
import InstallPrompt from './components/InstallPrompt.vue'
import SaveFileDialog from './components/SaveFileDialog.vue'
import { db } from './db/database.js'
import { bootstrapDatabase } from './db/bootstrap.js'
import { useBackupDownload } from './composables/useBackupDownload.js'

const { t } = useTranslation()
const route = useRoute()

function onNavKeydown(event) {
  if (!['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(event.key)) return
  const links = Array.from(event.currentTarget.querySelectorAll('a'))
  const idx = links.indexOf(document.activeElement)
  if (idx === -1) return
  event.preventDefault()
  const isForward = event.key === 'ArrowRight' || event.key === 'ArrowDown'
  links[(idx + (isForward ? 1 : -1) + links.length) % links.length].focus()
}

const dbReady = ref(false)
const dbError = ref(null)
const migrationPending = ref(null)

if (window.__dbReady) {
  window.__dbReady
    .then((status) => {
      if (status && status.migrationPending) {
        migrationPending.value = status.migrationPending
      } else {
        dbReady.value = true
      }
    })
    .catch((err) => {
      dbError.value = err.message || String(err)
    })
}

const dbErrorBody = computed(() => {
  if (dbError.value === 'DB_NEWER_THAN_APP') return t('error.dbNewerThanApp')
  if (dbError.value === 'DB_MIGRATION_FAILED') return t('error.dbMigrationFailed')
  if (dbError.value && dbError.value.startsWith('DB_LOAD_FAILED')) return t('error.dbLoadFailed')
  return t('error.browserNotCompatible')
})

// Retrying makes no sense when the data itself is ahead of the app or a
// migration just failed; every other init error (network, storage) may be
// transient.
const dbErrorRetryable = computed(
  () => dbError.value !== 'DB_NEWER_THAN_APP' && dbError.value !== 'DB_MIGRATION_FAILED',
)

async function onRetryInit() {
  dbError.value = null // back to the loading overlay while the retry runs
  try {
    const status = await bootstrapDatabase()
    if (status && status.migrationPending) {
      migrationPending.value = status.migrationPending
    } else {
      dbReady.value = true
    }
  } catch (err) {
    dbError.value = err.message || String(err)
  }
}

// Blocking migration screen: backup is mandatory before the update can run
const backupDownloaded = ref(false)
const migrating = ref(false)
const { downloadBackup } = useBackupDownload()

async function onMigrationBackup() {
  const saved = await downloadBackup()
  if (saved) backupDownloaded.value = true
}

async function onMigrationUpdate() {
  migrating.value = true
  try {
    await db.runPendingMigrations()
    migrationPending.value = null
    document.documentElement.removeAttribute('data-db-migration')
    document.documentElement.setAttribute('data-db-ready', 'true')
    dbReady.value = true
  } catch (err) {
    dbError.value = err.message || String(err)
    document.documentElement.setAttribute('data-db-error', dbError.value)
  } finally {
    migrating.value = false
  }
}

const dbConnectionLost = ref(false)
onMounted(() => {
  window.addEventListener('db-connection-lost', () => {
    dbConnectionLost.value = true
  })
})

function reloadPage() {
  window.location.reload()
}

const pageTitle = computed(() => {
  const name = route.name
  // Map sub-route names to their parent nav key
  const navKeyMap = {
    'exercise-new': 'exercises',
    'exercise-detail': 'exercises',
    'exercise-edit': 'exercises',
    'combo-new': 'combos',
    'combo-detail': 'combos',
    'combo-edit': 'combos',
    'session-new': 'sessions',
    'session-detail': 'sessions',
    'session-edit': 'sessions',
    'session-execute': 'sessions',
    'collection-new': 'collections',
    'collection-detail': 'collections',
    'collection-edit': 'collections',
  }
  const navKey = navKeyMap[name] || name
  if (navKey && t(`nav.${navKey}`, '')) {
    return t(`nav.${navKey}`)
  }
  return t('app.name')
})
</script>

<template>
  <div v-if="dbConnectionLost" class="db-overlay" data-testid="db-connection-lost">
    <div class="db-overlay-card">
      <i class="bi bi-plug db-error-icon"></i>
      <h1>{{ $t('error.dbInitFailed') }}</h1>
      <p>{{ $t('error.dbConnectionLost') }}</p>
      <button class="btn btn-primary" style="margin-top: 1rem" @click="reloadPage">
        {{ $t('error.reload') }}
      </button>
    </div>
  </div>

  <div v-else-if="dbError" class="db-overlay" data-testid="db-error">
    <div class="db-overlay-card">
      <i class="bi bi-exclamation-triangle db-error-icon"></i>
      <h1>{{ $t('error.dbInitFailed') }}</h1>
      <p>{{ dbErrorBody }}</p>
      <button
        v-if="dbErrorRetryable"
        class="btn btn-primary"
        style="margin-top: 1rem"
        data-testid="db-retry"
        @click="onRetryInit"
      >
        {{ $t('error.retry') }}
      </button>
    </div>
  </div>

  <div v-else-if="migrationPending" class="db-overlay" data-testid="db-migration">
    <div class="db-overlay-card">
      <i class="bi bi-database-up db-migration-icon"></i>
      <h1>{{ $t('migration.title') }}</h1>
      <p>
        {{ $t('migration.body', { from: migrationPending.from, to: migrationPending.to }) }}
      </p>
      <p>{{ $t('migration.backupRequired') }}</p>
      <div class="db-migration-actions">
        <button class="btn btn-primary" data-testid="migration-backup" @click="onMigrationBackup">
          <i class="bi bi-download"></i>
          {{ $t('migration.downloadBackup') }}
        </button>
        <button
          class="btn btn-primary"
          data-testid="migration-update"
          :disabled="!backupDownloaded || migrating"
          @click="onMigrationUpdate"
        >
          <span v-if="migrating" class="spinner spinner-inline"></span>
          {{ migrating ? $t('migration.updating') : $t('migration.update') }}
        </button>
      </div>
    </div>
  </div>

  <div v-else-if="!dbReady" class="db-overlay" data-testid="db-loading">
    <div class="db-overlay-card">
      <div class="spinner"></div>
      <p class="loading-text">{{ $t('app.loading') }}</p>
    </div>
  </div>

  <template v-else>
    <BackupReminder :key="route.fullPath" />
    <FirstLoadWarning />
    <InstallPrompt />
    <AppUpdateDialog />
    <nav class="navbar" :aria-label="$t('nav.mainNav')" @keydown="onNavKeydown">
      <div class="navbar-brand">{{ $t('app.name') }}</div>

      <router-link to="/home">
        <i class="bi bi-house"></i>
        <span>{{ $t('nav.home') }}</span>
      </router-link>

      <router-link to="/exercises">
        <i class="bi bi-person-arms-up"></i>
        <span>{{ $t('nav.exercises') }}</span>
      </router-link>

      <router-link to="/combos">
        <i class="bi bi-layers"></i>
        <span>{{ $t('nav.combos') }}</span>
      </router-link>

      <router-link to="/sessions">
        <i class="bi bi-journal-text"></i>
        <span>{{ $t('nav.sessions') }}</span>
      </router-link>

      <router-link to="/collections">
        <i class="bi bi-collection"></i>
        <span>{{ $t('nav.collections') }}</span>
      </router-link>

      <div class="navbar-spacer"></div>

      <router-link to="/stats">
        <i class="bi bi-graph-up"></i>
        <span>{{ $t('nav.stats') }}</span>
      </router-link>

      <router-link to="/settings">
        <i class="bi bi-gear"></i>
        <span>{{ $t('nav.settings') }}</span>
      </router-link>
    </nav>

    <div class="content">
      <div class="actionbar">
        <h1>{{ pageTitle }}</h1>
        <div id="actionbar-actions"></div>
      </div>

      <div class="content-body">
        <router-view />
      </div>
    </div>
  </template>

  <SaveFileDialog />
</template>

<style scoped>
.db-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-background, #f5f5f5);
  z-index: 9999;
  padding: 2rem;
}

.db-overlay-card {
  text-align: center;
  max-width: 480px;
}

.db-error-icon {
  font-size: 3rem;
  color: var(--color-danger, #dc3545);
}

.db-migration-icon {
  font-size: 3rem;
  color: var(--color-primary, #4a90d9);
}

.db-migration-actions {
  display: flex;
  justify-content: center;
  gap: 1rem;
  margin-top: 1.5rem;
}

.spinner-inline {
  width: 1em;
  height: 1em;
  border-width: 2px;
  display: inline-block;
  vertical-align: -0.15em;
  margin: 0 0.35em 0 0;
}

.db-overlay-card h1 {
  margin: 1rem 0 0.5rem;
  font-size: 1.5rem;
}

.db-overlay-card p {
  color: var(--color-text-secondary, #666);
  line-height: 1.5;
}

.spinner {
  width: 48px;
  height: 48px;
  border: 4px solid var(--color-border, #ddd);
  border-top-color: var(--color-primary, #4a90d9);
  border-radius: 50%;
  margin: 0 auto 1rem;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.loading-text {
  font-size: 1.1rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 18px;
  border-radius: 8px;
  border: none;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
}

.btn-primary {
  background: var(--btn-primary-bg, #4a90d9);
  color: var(--btn-primary-color, #fff);
}
</style>
