<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useDisplay, useTheme } from 'vuetify'
import { useRoute, useRouter } from 'vue-router'

import iconImage from '@/assets/images/icon.svg'
import { Visibility, routes } from '@/plugins/routesLayout'
import {
  AuthRequestError,
  ROLE_ADMIN,
  fetchCurrentUser,
  hasRole,
  logout,
  type CurrentUser,
} from '@/services/authSession'
import { useAppBannersStore } from '@/stores/appBanners'

const display = useDisplay()
const theme = useTheme()
const route = useRoute()
const router = useRouter()
const showDrawer = ref(true)
const currentYear = new Date().getFullYear()
const themeStorageKey = 'theme'
const currentUser = ref<CurrentUser | null>(null)
const isLoggingOut = ref(false)
const appBannersStore = useAppBannersStore()
const { banners } = storeToRefs(appBannersStore)

function normalizeRoutePath(path: string) {
  if (!path || path === '/') {
    return '/'
  }

  return path.endsWith('/') ? path.slice(0, -1) : path
}

function isWithinRoutePath(currentPath: string, targetPath: string) {
  const normalizedCurrentPath = normalizeRoutePath(currentPath)
  const normalizedTargetPath = normalizeRoutePath(targetPath)

  if (normalizedTargetPath === '/') {
    return normalizedCurrentPath === '/'
  }

  return (
    normalizedCurrentPath === normalizedTargetPath ||
    normalizedCurrentPath.startsWith(`${normalizedTargetPath}/`)
  )
}

function resolveVisibilityRoutes(path: string, visibilityRoute?: string | string[]) {
  if (Array.isArray(visibilityRoute)) {
    return visibilityRoute.map((entry) => entry.trim()).filter((entry) => entry.length > 0)
  }

  const normalizedVisibilityRoute = visibilityRoute?.trim()
  if (normalizedVisibilityRoute && normalizedVisibilityRoute.length > 0) {
    return [normalizedVisibilityRoute]
  }

  return [path]
}

const sidebarRoutes = computed(() =>
  routes.filter((item) => {
    if (item.visible === Visibility.Public) {
      return true
    }

    if (item.visible === Visibility.Authenticated) {
      return currentUser.value !== null
    }

    if (item.visible === Visibility.Unauthenticated) {
      return currentUser.value === null
    }

    if (item.visible === Visibility.Authorized) {
      if (!currentUser.value) {
        return false
      }

      if (!item.requiredRoles || item.requiredRoles.length === 0) {
        return true
      }

      return item.requiredRoles.every((role) => hasRole(currentUser.value, role))
    }

    if (item.visible !== Visibility.Route) {
      return false
    }

    return resolveVisibilityRoutes(item.path, item.visibilityRoute).some((targetPath) =>
      isWithinRoutePath(route.path, targetPath),
    )
  }),
)
const adminSidebarRoutes = computed(() =>
  sidebarRoutes.value.filter(
    (item) =>
      item.visible === Visibility.Authorized &&
      Array.isArray(item.requiredRoles) &&
      item.requiredRoles.some((role) => role.trim().toLowerCase() === ROLE_ADMIN),
  ),
)
const primarySidebarRoutes = computed(() =>
  sidebarRoutes.value.filter(
    (item) => !adminSidebarRoutes.value.some((adminItem) => adminItem.path === item.path),
  ),
)
const footerRoutes = computed(() => routes.filter((x) => x.visible === Visibility.Footer))

const activeRoute = computed(() => {
  const byName = routes.find((x) => x.meta?.name === route.name)
  if (byName) {
    return byName
  }

  return routes.find((x) => x.path === route.path)
})

const pageName = computed(() => activeRoute.value?.name ?? 'Hoard')
const pageDescription = computed(
  () => activeRoute.value?.description ?? 'Self-hosted Dateiablage im Browser',
)
const shouldShowFooter = computed(() => activeRoute.value?.disableFooter !== true)
const isDarkTheme = computed(() => theme.global.name.value === 'dark')
const themeIcon = computed(() => (isDarkTheme.value ? 'mdi-white-balance-sunny' : 'mdi-weather-night'))
const themeLabel = computed(() =>
  isDarkTheme.value ? 'Hellen Modus aktivieren' : 'Dunklen Modus aktivieren',
)
const isAuthenticated = computed(() => currentUser.value !== null)
const userLabel = computed(() => currentUser.value?.userName ?? 'Konto')

function applyTheme(nextTheme: 'light' | 'dark', persist = true) {
  theme.global.name.value = nextTheme
  document.documentElement.setAttribute('data-theme', nextTheme)
  if (persist) {
    localStorage.setItem(themeStorageKey, nextTheme)
  }
}

function toggleDrawer() {
  showDrawer.value = !showDrawer.value
  localStorage.setItem('drawer', showDrawer.value ? 'Y' : 'N')
}

function toggleTheme() {
  applyTheme(isDarkTheme.value ? 'light' : 'dark')
}

async function navigateToBrandTarget() {
  let resolvedUser = currentUser.value

  if (!resolvedUser) {
    try {
      resolvedUser = await fetchCurrentUser()
      currentUser.value = resolvedUser
    } catch {
      resolvedUser = null
      currentUser.value = null
    }
  }

  const targetRouteName = resolvedUser ? 'Dashboard' : 'Home'
  await router.push({ name: targetRouteName })
}

async function refreshAuthState(options: { force?: boolean } = {}) {
  try {
    currentUser.value = await fetchCurrentUser({ force: options.force === true })
  } catch {
    currentUser.value = null
  }
}

async function handleLogout() {
  if (isLoggingOut.value) {
    return
  }

  isLoggingOut.value = true

  try {
    await logout()
    currentUser.value = null
    await router.replace({ name: 'Login' })
  } catch (error) {
    if (error instanceof AuthRequestError) {
      appBannersStore.pushError(error.message, 'Abmeldung fehlgeschlagen')
    } else {
      appBannersStore.pushError('Abmeldung fehlgeschlagen. Bitte versuche es erneut.', 'Abmeldung fehlgeschlagen')
    }
  } finally {
    isLoggingOut.value = false
    void refreshAuthState()
  }
}

function dismissBanner(id: string) {
  appBannersStore.dismiss(id)
}

function changeWebsiteTitle() {
  if (activeRoute.value) {
    document.title = `Hoard | ${activeRoute.value.name}`
    return
  }

  document.title = 'Hoard'
}

onMounted(() => {
  const storedTheme = localStorage.getItem(themeStorageKey)
  const validTheme = storedTheme === 'dark' || storedTheme === 'light' ? storedTheme : 'light'
  applyTheme(validTheme, false)

  const storedDrawer = localStorage.getItem('drawer')
  showDrawer.value = storedDrawer ? storedDrawer.startsWith('Y') : !display.mobile.value
  void refreshAuthState({ force: true })
})

watch(
  () => route.fullPath,
  () => {
    changeWebsiteTitle()
    void refreshAuthState()
    if (display.mobile.value) {
      showDrawer.value = false
    }
  },
  { immediate: true },
)
</script>

<template>
  <v-app class="hoard-shell">
    <v-app-bar class="hoard-app-bar" elevation="0" height="64">
      <template #prepend>
        <v-app-bar-nav-icon
          v-tooltip="!showDrawer ? 'Menü öffnen' : 'Menü schließen'"
          @click="toggleDrawer()"
        />
      </template>

      <button type="button" class="brand-button" @click="navigateToBrandTarget">
        <span class="brand-mark">
          <img :src="iconImage" alt="Hoard Icon" class="brand-logo" />
        </span>
        <span class="brand-text">
          <span class="brand-title">Hoard</span>
          <span class="brand-subtitle">Dateien zuerst</span>
        </span>
      </button>

      <v-divider vertical class="mx-3 topbar-context-divider" />

      <div class="page-context">
        <p class="page-name">{{ pageName }}</p>
        <p class="page-description">{{ pageDescription }}</p>
      </div>

      <v-spacer />

      <div class="topbar-actions">
        <v-tooltip location="bottom">
          <template #activator="{ props }">
            <v-btn
              icon
              :aria-label="themeLabel"
              v-bind="props"
              @click="toggleTheme"
            >
              <v-icon>{{ themeIcon }}</v-icon>
            </v-btn>
          </template>
          {{ themeLabel }}
        </v-tooltip>

        <template v-if="isAuthenticated">
          <v-menu
            v-if="!display.smAndDown.value"
            open-on-hover
            :open-on-click="false"
            location="bottom end"
            offset="8"
          >
            <template #activator="{ props }">
              <v-btn
                class="account-button"
                variant="outlined"
                prepend-icon="mdi-account-check-outline"
                :to="{ name: 'Dashboard' }"
                v-bind="props"
              >
                {{ userLabel }}
              </v-btn>
            </template>

            <v-list density="compact" class="account-menu-list">
              <v-list-item
                prepend-icon="mdi-lock-reset"
                title="Passwort ändern"
                :to="{ name: 'ChangePassword' }"
              />
              <v-divider />
              <v-list-item
                prepend-icon="mdi-logout"
                :title="isLoggingOut ? 'Abmelden...' : 'Abmelden'"
                :disabled="isLoggingOut"
                @click="handleLogout"
              />
            </v-list>
          </v-menu>

          <v-menu
            v-else
            location="bottom end"
            offset="8"
          >
            <template #activator="{ props }">
              <v-btn icon v-bind="props" :aria-label="`Angemeldet als ${userLabel}`">
                <v-icon>mdi-account-check-outline</v-icon>
              </v-btn>
            </template>

            <v-list density="compact" class="account-menu-list">
              <v-list-item
                prepend-icon="mdi-view-dashboard-outline"
                title="Zum Dashboard"
                :to="{ name: 'Dashboard' }"
              />
              <v-list-item
                prepend-icon="mdi-lock-reset"
                title="Passwort ändern"
                :to="{ name: 'ChangePassword' }"
              />
              <v-divider />
              <v-list-item
                prepend-icon="mdi-logout"
                :title="isLoggingOut ? 'Abmelden...' : 'Abmelden'"
                :disabled="isLoggingOut"
                @click="handleLogout"
              />
            </v-list>
          </v-menu>
        </template>

        <template v-else>
          <v-tooltip v-if="!display.smAndDown.value" location="bottom">
            <template #activator="{ props }">
              <v-btn variant="outlined" prepend-icon="mdi-account-circle-outline" to="/login" v-bind="props">
                Konto
              </v-btn>
            </template>
            Zum Login
          </v-tooltip>

          <v-tooltip v-else location="bottom">
            <template #activator="{ props }">
              <v-btn icon to="/login" v-bind="props" aria-label="Zum Login">
                <v-icon>mdi-account-circle-outline</v-icon>
              </v-btn>
            </template>
            Zum Login
          </v-tooltip>
        </template>
      </div>
    </v-app-bar>

    <v-navigation-drawer
      v-model="showDrawer"
      :class="['hoard-drawer', { 'hoard-drawer--mobile': display.mobile.value }]"
      :location="display.mobile.value ? 'bottom' : 'left'"
      :temporary="display.mobile.value"
      :permanent="!display.mobile.value"
      :width="268"
      :elevation="display.mobile.value ? 6 : 0"
    >
      <div class="drawer-top">
        <p class="drawer-kicker hoard-kicker hoard-kicker--xs">Navigation</p>
      </div>

      <v-list nav :density="display.mobile.value ? 'default' : 'comfortable'" class="px-1">
        <template v-for="item in primarySidebarRoutes" :key="item.path">
          <v-list-item
            :to="item.path"
            :active="route.path === item.path"
            :prepend-icon="item.icon"
            :title="item.name"
            class="hoard-nav-item"
            rounded="lg"
            link
          />
        </template>

        <template v-if="adminSidebarRoutes.length > 0">
          <v-divider class="my-2 mx-2" />
          <div class="drawer-section-head">
            <p class="drawer-kicker hoard-kicker hoard-kicker--xs">Admin</p>
          </div>

          <v-list-item
            v-for="item in adminSidebarRoutes"
            :key="item.path"
            :to="item.path"
            :active="route.path === item.path"
            :prepend-icon="item.icon"
            :title="item.name"
            class="hoard-nav-item"
            rounded="lg"
            link
          />
        </template>
      </v-list>

      <template #append>
        <div v-if="!isAuthenticated" class="drawer-bottom">
          <v-btn variant="text" prepend-icon="mdi-home" to="/welcome" :block="display.mobile.value">
            Zur Startseite
          </v-btn>
        </div>
      </template>
    </v-navigation-drawer>

    <v-main class="hoard-main">
      <div class="main-shell">
        <router-view v-slot="{ Component }">
          <transition name="route-fade" mode="out-in">
            <component :is="Component" />
          </transition>
        </router-view>
      </div>

      <v-footer
        v-if="shouldShowFooter"
        class="hoard-footer d-flex align-center justify-space-between ga-2 flex-wrap py-3"
      >
        <div class="footer-links">
          <v-btn
            v-for="link in footerRoutes"
            :key="link.path"
            :to="link.path"
            :text="link.name"
            variant="text"
            rounded
          />
        </div>
        <p class="footer-copy">{{ currentYear }} - <strong>Hoard</strong></p>
      </v-footer>
    </v-main>

    <section
      v-if="banners.length > 0"
      class="app-banner-stack"
      aria-live="polite"
      aria-label="Systemmeldungen"
    >
      <transition-group name="app-banner-transition" tag="div" class="app-banner-list">
        <v-alert
          v-for="banner in banners"
          :key="banner.id"
          :class="['app-banner-item', `app-banner-item--${banner.type}`]"
          :type="banner.type"
          variant="tonal"
          border="start"
          closable
          @click:close="dismissBanner(banner.id)"
        >
          <p v-if="banner.title" class="app-banner-title">{{ banner.title }}</p>
          <p class="app-banner-text">{{ banner.message }}</p>
        </v-alert>
      </transition-group>
    </section>
  </v-app>
</template>

<style scoped>
.hoard-shell {
  min-height: 100vh;
}

.hoard-app-bar {
  padding-inline: var(--space-2);
}

.brand-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0;
  border: none;
  color: inherit;
  background: transparent;
  cursor: pointer;
  border-radius: var(--radius-md);
  transition:
    color var(--transition-fast),
    transform var(--transition-fast);
}

.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 54px;
  height: 54px;
  transition:
    transform var(--transition-fast);
}

.brand-button:hover .brand-mark {
  transform: translateY(-1px);
}

.brand-logo {
  width: 58px;
  height: 58px;
  object-fit: contain;
}

.brand-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.1;
}

.brand-title {
  color: var(--color-text);
  font-size: 16px;
  font-weight: 700;
}

.brand-subtitle {
  color: var(--color-text-muted);
  font-size: var(--font-size-xs);
  font-weight: 500;
}

.page-context {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.topbar-context-divider {
  display: flex;
}

.topbar-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.account-button {
  max-width: 220px;
}

:deep(.account-button .v-btn__content) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.page-name,
.page-description,
.drawer-kicker,
.drawer-text,
.footer-copy {
  margin: 0;
}

.page-name {
  color: var(--color-text);
  font-size: var(--font-size-md);
  font-weight: 600;
}

.page-description {
  max-width: min(360px, 32vw);
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (width <= 1360px) {
  .page-description {
    display: none;
  }

  .page-context {
    gap: 0;
  }
}

@media (width <= 1180px) {
  .topbar-context-divider {
    display: none;
  }
}

.hoard-drawer {
  padding-top: var(--space-2);
}

.hoard-drawer--mobile {
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
  max-height: min(72vh, 560px);
}

.drawer-top {
  padding: var(--space-2) var(--space-4) var(--space-4);
  border-bottom: 1px solid color-mix(in srgb, var(--color-border) 85%, var(--color-surface) 15%);
}

.drawer-title {
  margin: var(--space-2) 0 var(--space-1);
  color: var(--color-text);
  font-size: var(--font-size-lg);
}

.drawer-text {
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}

.drawer-bottom {
  padding: var(--space-3) var(--space-2) var(--space-4);
  border-top: 1px solid color-mix(in srgb, var(--color-border) 90%, var(--color-surface) 10%);
}

.drawer-section-head {
  padding: var(--space-2) var(--space-3) var(--space-1);
}

:deep(.hoard-nav-item) {
  min-height: 44px;
}

:deep(.hoard-nav-item .v-list-item-title) {
  font-weight: 500;
}

.hoard-main {
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 64px);
}

.main-shell {
  flex: 1;
  padding: var(--space-6);
}

.route-fade-enter-active,
.route-fade-leave-active {
  transition:
    opacity var(--transition-medium),
    transform var(--transition-medium);
}

.route-fade-enter-from {
  opacity: 0;
  transform: translateY(8px);
}

.route-fade-leave-to {
  opacity: 0;
  transform: translateY(-4px);
}

.hoard-footer {
  flex: 0 0 auto;
  height: auto !important;
  min-height: 0 !important;
  max-height: 88px;
  padding-inline: var(--space-6);
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.footer-copy {
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}

.app-banner-stack {
  position: fixed;
  right: max(var(--space-4), env(safe-area-inset-right));
  bottom: max(var(--space-4), env(safe-area-inset-bottom));
  z-index: 2100;
  pointer-events: none;
}

.app-banner-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: min(420px, calc(100vw - (2 * var(--space-4))));
}

:deep(.app-banner-item) {
  pointer-events: auto;
  border: 1px solid var(--color-border) !important;
  box-shadow: var(--shadow-md);
}

:deep(.app-banner-item .v-alert__overlay),
:deep(.app-banner-item .v-alert__underlay) {
  opacity: 0 !important;
}

:deep(.app-banner-item .v-alert__content) {
  line-height: 1.45;
}

:deep(.app-banner-item--error) {
  background-color: color-mix(in srgb, var(--color-danger) 10%, var(--color-surface) 90%) !important;
}

:deep(.app-banner-item--warning) {
  background-color: color-mix(in srgb, var(--color-warning) 10%, var(--color-surface) 90%) !important;
}

:deep(.app-banner-item--info) {
  background-color: color-mix(in srgb, var(--color-info) 10%, var(--color-surface) 90%) !important;
}

:deep(.app-banner-item--success) {
  background-color: color-mix(in srgb, var(--color-success) 10%, var(--color-surface) 90%) !important;
}

.app-banner-title,
.app-banner-text {
  margin: 0;
}

.app-banner-title {
  margin-bottom: 2px;
  color: var(--color-text);
  font-size: var(--font-size-sm);
  font-weight: 700;
}

.app-banner-text {
  color: var(--color-text-secondary);
  line-height: 1.45;
}

.app-banner-transition-enter-active,
.app-banner-transition-leave-active {
  transition: opacity 180ms ease, transform 180ms ease;
}

.app-banner-transition-enter-from,
.app-banner-transition-leave-to {
  opacity: 0;
  transform: translateY(10px);
}

.app-banner-transition-move {
  transition: transform 180ms ease;
}

@media (width <= 960px) {
  .hoard-app-bar {
    padding-inline:
      max(var(--space-1), env(safe-area-inset-left))
      max(var(--space-1), env(safe-area-inset-right));
  }

  .brand-button {
    min-width: 0;
    gap: var(--space-2);
  }

  .brand-logo {
    width: 48px;
    height: 48px;
  }

  .brand-mark {
    width: 46px;
    height: 46px;
  }

  .brand-title {
    font-size: 15px;
  }

  .topbar-actions {
    gap: 2px;
  }

  .topbar-context-divider,
  .page-context {
    display: none;
  }

  .hoard-drawer {
    padding-top: var(--space-1);
  }

  .drawer-top {
    padding: var(--space-3) var(--space-4);
  }

  .drawer-bottom {
    padding: var(--space-2) var(--space-3) calc(var(--space-3) + env(safe-area-inset-bottom));
  }

  :deep(.drawer-bottom .v-btn) {
    min-height: 44px;
  }

  .main-shell {
    padding: var(--space-4);
  }

  .brand-subtitle {
    display: none;
  }

  .hoard-footer {
    justify-content: center !important;
    gap: var(--space-2);
    padding-inline:
      max(var(--space-2), env(safe-area-inset-left))
      max(var(--space-2), env(safe-area-inset-right));
  }

  .footer-links {
    width: 100%;
    justify-content: center;
    gap: var(--space-2);
  }

  .app-banner-stack {
    right: var(--hoard-mobile-safe-right);
    left: var(--hoard-mobile-safe-left);
    bottom: calc(var(--hoard-mobile-safe-bottom) + var(--space-1));
  }

  .app-banner-list {
    width: 100%;
  }
}

@media (width <= 600px) {
  .main-shell {
    padding: var(--space-3);
  }

  .brand-title {
    font-size: var(--font-size-md);
  }

  .footer-links {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  }

  :deep(.footer-links .v-btn) {
    width: 100%;
    min-height: 44px;
  }
}
</style>
