<template>
  <div class="drawer h-screen flex flex-col bg-base-100 overflow-hidden">
    <a
      href="#main-content"
      class="absolute left-0 -top-20 z-[1200] bg-primary text-primary-content px-4 py-2 rounded-b transition-[top] duration-200 focus:top-0 focus:outline-none focus:ring-2 focus:ring-primary-content"
    >
      Skip to main content
    </a>
    <input
      id="nav-drawer"
      ref="drawerCheckboxRef"
      type="checkbox"
      class="drawer-toggle"
      @change="onDrawerChange"
    >
    <div class="drawer-content flex flex-col h-screen overflow-hidden">
      <header class="navbar relative z-[1100] bg-base-100/80 backdrop-blur-xl border-b border-base-300/50 px-4 gap-2 shrink-0">
        <NuxtLink to="/" class="flex items-center gap-2 text-lg font-semibold hover:opacity-80 transition-all duration-200">
          <icons-icon-map class="size-6 text-primary shrink-0" aria-hidden="true" />
          <span>{{ title }}</span>
        </NuxtLink>
        <div class="flex-1" />
        <!-- Desktop (md+): Login / connection + user dropdown -->
        <template v-if="!isLogin">
          <NuxtLink
            v-if="!me"
            to="/login"
            class="btn btn-ghost btn-sm btn-primary hidden md:inline-flex"
          >
            Login
          </NuxtLink>
          <div
            v-if="me"
            class="hidden md:flex items-center gap-2 shrink-0 min-h-9"
          >
            <div class="flex items-center shrink-0 min-h-9">
              <span
                class="inline-flex items-center gap-1.5 text-xs text-base-content/70 leading-none"
                :class="live ? 'text-success' : 'text-base-content/50'"
              >
                <span
                  class="size-2 rounded-full shrink-0"
                  :class="live ? 'bg-success animate-pulse' : 'bg-base-content/40'"
                  aria-hidden="true"
                />
                {{ live ? 'Live' : 'Offline' }}
              </span>
            </div>
            <div class="dropdown dropdown-end flex items-center">
              <details ref="userDropdownRef" class="dropdown group">
                <summary class="btn btn-ghost btn-sm gap-2 flex items-center min-h-9 h-9 cursor-pointer list-none [&::-webkit-details-marker]:hidden">
                  <UserAvatar :username="me.username" :email="me.email" :size="32" />
                  <span class="max-w-[8rem] truncate font-medium">{{ me.username }}</span>
                  <svg class="size-4 opacity-70 shrink-0 transition-transform group-open:rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
                  </svg>
                </summary>
                <ul
                  class="dropdown-content menu bg-base-200 rounded-xl z-[1100] mt-2 w-52 border border-base-300/50 shadow-xl p-2"
                >
                  <li>
                    <NuxtLink to="/" :class="route.path === '/' ? 'active' : ''" @click="closeDropdown">
                      <icons-icon-map />
                      Map
                    </NuxtLink>
                  </li>
                  <li>
                    <NuxtLink to="/profile" :class="route.path === '/profile' ? 'active' : ''" @click="closeDropdown">
                      <icons-icon-user />
                      Profile
                    </NuxtLink>
                  </li>
                  <li v-if="isAdmin">
                    <NuxtLink to="/admin" :class="route.path.startsWith('/admin') ? 'active' : ''" @click="closeDropdown">
                      <icons-icon-shield />
                      Admin
                    </NuxtLink>
                  </li>
                  <li class="menu-divider my-1" />
                  <li class="[&_.menu-item]:flex [&_.menu-item]:items-center [&_.menu-item]:gap-3">
                    <label class="flex cursor-pointer items-center gap-3 py-2 px-2 rounded-lg hover:bg-base-300/50 w-full min-h-0">
                      <icons-icon-sun v-if="!dark" class="size-4 shrink-0 opacity-80" />
                      <icons-icon-moon v-else class="size-4 shrink-0 opacity-80" />
                      <span class="flex-1 text-sm">Dark theme</span>
                      <input
                        type="checkbox"
                        class="toggle toggle-sm toggle-primary shrink-0"
                        :checked="dark"
                        @change="onThemeToggle"
                      >
                    </label>
                  </li>
                  <li>
                    <button type="button" class="text-error hover:bg-error/10 rounded-lg" @click="doLogout">
                      <icons-icon-logout />
                      Logout
                    </button>
                  </li>
                </ul>
              </details>
            </div>
          </div>
        </template>
        <!-- Mobile: hamburger to open drawer -->
        <label
          ref="hamburgerRef"
          for="nav-drawer"
          class="btn btn-ghost btn-square md:hidden flex items-center justify-center min-h-[2.75rem] min-w-[2.75rem]"
          aria-label="Open menu"
        >
          <icons-icon-menu class="size-6" />
        </label>
      </header>
      <main id="main-content" class="flex-1 min-h-0 overflow-y-auto relative flex flex-col" tabindex="-1">
        <AdminBreadcrumbs v-if="route.path.startsWith('/admin') && route.path !== '/admin'" />
        <div class="flex-1 min-h-0">
          <slot />
        </div>
      </main>
      <ToastContainer />
    </div>
    <!-- Drawer sidebar (mobile menu) -->
    <div ref="drawerSideRef" class="drawer-side z-[1100]">
      <label
        for="nav-drawer"
        aria-label="Close menu"
        class="drawer-overlay"
        tabindex="0"
        @keydown.enter.prevent="closeDrawer"
        @keydown.space.prevent="closeDrawer"
      />
      <aside class="bg-base-200/95 backdrop-blur-xl min-h-full w-72 p-4 flex flex-col">
        <!-- Mobile: user + live when logged in -->
        <div v-if="!isLogin && me" class="flex items-center gap-3 pb-4 mb-2 border-b border-base-300/50">
          <UserAvatar :username="me.username" :email="me.email" :size="40" />
          <div class="flex-1 min-w-0">
            <p class="font-medium truncate">{{ me.username }}</p>
            <p class="text-xs flex items-center gap-1.5" :class="live ? 'text-success' : 'text-base-content/60'">
              <span class="size-1.5 rounded-full" :class="live ? 'bg-success animate-pulse' : 'bg-base-content/40'" />
              {{ live ? 'Live' : 'Offline' }}
            </p>
          </div>
        </div>
        <ul class="menu p-0 flex-1 text-base">
          <li v-if="!me && !isLogin">
            <NuxtLink to="/login" class="min-h-12 touch-manipulation" @click="closeDrawer">
              Login
            </NuxtLink>
          </li>
          <template v-else>
            <li>
              <NuxtLink to="/" :class="route.path === '/' ? 'active' : ''" class="min-h-12 touch-manipulation" @click="closeDrawer">
                <icons-icon-map />
                Map
              </NuxtLink>
            </li>
            <li>
              <NuxtLink to="/profile" :class="route.path === '/profile' ? 'active' : ''" class="min-h-12 touch-manipulation" @click="closeDrawer">
                <icons-icon-user />
                Profile
              </NuxtLink>
            </li>
            <li v-if="isAdmin">
              <NuxtLink to="/admin" :class="route.path.startsWith('/admin') ? 'active' : ''" class="min-h-12 touch-manipulation" @click="closeDrawer">
                <icons-icon-shield />
                Admin
              </NuxtLink>
            </li>
            <li class="menu-divider my-1" />
            <li class="[&_.menu-item]:flex [&_.menu-item]:items-center [&_.menu-item]:gap-3 min-h-12">
              <label class="flex cursor-pointer items-center gap-3 py-3 px-2 rounded-lg hover:bg-base-300/50 w-full min-h-0 touch-manipulation">
                <icons-icon-sun v-if="!dark" class="size-4 shrink-0 opacity-80" />
                <icons-icon-moon v-else class="size-4 shrink-0 opacity-80" />
                <span class="flex-1 text-sm">Dark theme</span>
                <input
                  type="checkbox"
                  class="toggle toggle-sm toggle-primary shrink-0"
                  :checked="dark"
                  @change="onThemeToggle"
                >
              </label>
            </li>
            <li>
              <button type="button" class="text-error hover:bg-error/10 rounded-lg min-h-12 touch-manipulation w-full justify-start" @click="doLogout">
                <icons-icon-logout />
                Logout
              </button>
            </li>
          </template>
        </ul>
      </aside>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { MeResponse } from '~/types/api'

const route = useRoute()
const router = useRouter()
const THEME_KEY = 'hnh-map-theme'

function getInitialDark(): boolean {
  if (import.meta.client) {
    const stored = localStorage.getItem(THEME_KEY)
    if (stored === 'dark') return true
    if (stored === 'light') return false
    return window.matchMedia('(prefers-color-scheme: dark)').matches
  }
  return false
}

const title = ref('HnH Map')
const dark = ref(false)
/** Live when at least one of current user's characters is on the map (set by MapView). */
const live = useState<boolean>('mapLive', () => false)
const me = useState<MeResponse | null>('me', () => null)
const userDropdownRef = ref<HTMLDetailsElement | null>(null)
const drawerCheckboxRef = ref<HTMLInputElement | null>(null)
const drawerSideRef = ref<HTMLDivElement | null>(null)
const hamburgerRef = ref<HTMLLabelElement | null>(null)
let drawerTrapCleanup: (() => void) | null = null

function closeDropdown() {
  userDropdownRef.value?.removeAttribute('open')
}

function closeDrawer() {
  if (drawerCheckboxRef.value) drawerCheckboxRef.value.checked = false
}

function getDrawerFocusables(): HTMLElement[] {
  const side = drawerSideRef.value
  if (!side) return []
  const selector = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
  return Array.from(side.querySelectorAll<HTMLElement>(selector)).filter(
    (el) => el.offsetParent !== null && !el.hasAttribute('aria-hidden')
  )
}

function onDrawerChange() {
  const open = drawerCheckboxRef.value?.checked ?? false
  if (drawerTrapCleanup) {
    drawerTrapCleanup()
    drawerTrapCleanup = null
  }
  if (open) {
    nextTick(() => {
      const focusables = getDrawerFocusables()
      const first = focusables.find((el) => el.closest('aside')) ?? focusables[0]
      if (first) first.focus()
      function handleKeydown(e: KeyboardEvent) {
        if (e.key !== 'Tab') return
        const focusables = getDrawerFocusables()
        if (focusables.length === 0) return
        const i = focusables.indexOf(document.activeElement as HTMLElement)
        if (e.shiftKey) {
          if (i <= 0) {
            e.preventDefault()
            focusables[focusables.length - 1]?.focus()
          }
        } else {
          if (i === focusables.length - 1 || i < 0) {
            e.preventDefault()
            focusables[0]?.focus()
          }
        }
      }
      document.addEventListener('keydown', handleKeydown, true)
      drawerTrapCleanup = () => document.removeEventListener('keydown', handleKeydown, true)
    })
  } else if (import.meta.client && hamburgerRef.value && typeof hamburgerRef.value.focus === 'function') {
    nextTick(() => hamburgerRef.value?.focus())
  }
}

const { isLoginPath } = useAppPaths()
const isLogin = computed(() => isLoginPath(route.path))
const isAdmin = computed(() => !!me.value?.auths?.includes('admin'))

let loadId = 0

async function loadMe() {
  if (isLogin.value) return
  try {
    me.value = await useMapApi().me()
  } catch {
    me.value = null
  }
}

async function loadConfig(loadToken: number) {
  if (isLogin.value) return
  if (loadToken !== loadId) return
  try {
    const config = await useMapApi().getConfig()
    if (loadToken !== loadId) return
    if (config?.title) title.value = config.title
  } catch { /* ignore */ }
}

onMounted(() => {
  dark.value = getInitialDark()
  applyTheme()
})

watch(
  () => route.path,
  (path) => {
    if (!isLoginPath(path)) {
      const currentLoadId = ++loadId
      loadMe().then(() => loadConfig(currentLoadId))
    }
  },
  { immediate: true }
)

function onThemeToggle() {
  dark.value = !dark.value
  applyTheme()
}

function applyTheme() {
  if (!import.meta.client) return
  const html = document.documentElement
  const theme = dark.value ? 'dark' : 'light'
  html.setAttribute('data-theme', theme)
  localStorage.setItem(THEME_KEY, theme)
}

async function doLogout() {
  await useMapApi().logout()
  closeDrawer()
  await router.push('/login')
  me.value = null
}

onBeforeUnmount(() => {
  if (drawerTrapCleanup) {
    drawerTrapCleanup()
    drawerTrapCleanup = null
  }
})
</script>
