﻿<template>
  <n-layout has-sider class="app-root app-backdrop">
    <n-layout-sider
      collapse-mode="width"
      :collapsed-width="isFullscreen ? 0 : 72"
      :width="siderWidth"
      :collapsed="isFullscreen || authStore.layoutCollapsed"
      class="layout-sider"
      :class="{
        'layout-sider-collapsed': isSiderCollapsed,
        'layout-sider-hidden': isFullscreen
      }"
      :show-trigger="!isFullscreen"
      @collapse="authStore.layoutCollapsed = true"
      @expand="authStore.layoutCollapsed = false"
    >
      <div class="brand-mark">
        <span class="brand-dot"></span>
        <span class="brand-name" :title="appTitle">{{ appTitle }}</span>
      </div>
      <div class="menu-wrap">
        <AppMenu />
      </div>
      <div
        v-if="!isSiderCollapsed"
        class="sider-resizer"
        title="拖动调整菜单宽度"
        @pointerdown="startSiderResize"
      ></div>
    </n-layout-sider>

    <n-layout class="app-main" :class="{ 'app-main-fullscreen': isFullscreen }">
      <header v-if="!isFullscreen" class="app-header">
        <div class="header-main">
          <n-button
            tertiary
            circle
            size="small"
            class="header-refresh"
            title="刷新当前页面"
            @click="authStore.refreshTab(route.path)"
          >
            <template #icon>
              <n-icon><RefreshCw /></n-icon>
            </template>
          </n-button>
          <n-button
            tertiary
            circle
            size="small"
            class="header-fullscreen"
            :title="isFullscreen ? '退出全屏' : '全屏'"
            @click="toggleFullscreen"
          >
            <template #icon>
              <n-icon>
                <Minimize2 v-if="isFullscreen" />
                <Maximize2 v-else />
              </n-icon>
            </template>
          </n-button>
          <div class="breadcrumb-group">
            <AppBreadcrumb />
          </div>
          <div class="summary-actions">
            <n-tag type="info" size="small">{{ userDisplayName }}</n-tag>
            <n-button tertiary size="small" @click="handleLogout">退出</n-button>
          </div>
        </div>
        <AppTabs />
      </header>

      <n-layout-content embedded class="layout-content bg-transparent">
        <div class="content-scroll">
          <router-view v-slot="{ Component, route: currentRoute }">
            <transition name="fade-slide" mode="out-in">
              <keep-alive :include="cachedRouteNames">
                <component
                  :is="Component"
                  :key="`${currentRoute.fullPath}-${authStore.tabRefreshMarks[currentRoute.path === '/workbench' ? '/dashboard' : currentRoute.path] ?? 0}`"
                />
              </keep-alive>
            </transition>
          </router-view>
        </div>
      </n-layout-content>
    </n-layout>
  </n-layout>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { Maximize2, Minimize2, RefreshCw } from 'lucide-vue-next'
import { useRoute, useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import AppMenu from '@/components/AppMenu.vue'
import AppTabs from '@/components/AppTabs.vue'
import AppBreadcrumb from '@/components/AppBreadcrumb.vue'
import { appEnv } from '@/config/env'
import { resetDynamicRoutes } from '@/router'

const route = useRoute()
const router = useRouter()
const authStore = useAuthStore()
const appTitle = appEnv.appTitle
const isFullscreen = ref(false)
const SIDER_WIDTH_KEY = 'platform.layout.siderWidth'
const MIN_SIDER_WIDTH = 200
const MAX_SIDER_WIDTH = 360
const DEFAULT_SIDER_WIDTH = 248
const storedSiderWidth = Number(localStorage.getItem(SIDER_WIDTH_KEY))
const siderWidth = ref(
  Number.isFinite(storedSiderWidth)
    ? Math.min(MAX_SIDER_WIDTH, Math.max(MIN_SIDER_WIDTH, storedSiderWidth))
    : DEFAULT_SIDER_WIDTH
)
const isSiderCollapsed = computed(() => isFullscreen.value || authStore.layoutCollapsed)

const cachedRouteNames = computed(() =>
  router
    .getRoutes()
    .filter((item) => item.meta.keepAlive && typeof item.name === 'string')
    .map((item) => item.name as string)
)

const userDisplayName = computed(() => {
  const nickname = authStore.user?.nickname?.trim()
  return nickname || authStore.user?.username || '访客'
})

watch(
  () => route.fullPath,
  () => {
    if (!route.meta.requiresAuth || route.path === '/403' || route.path === '/404') return
    const name = (route.name as string | undefined) ?? route.path
    authStore.addTab({
      name,
      title: (route.meta.title as string | undefined) ?? '未命名页面',
      path: route.path,
      closable: route.path !== '/dashboard' && route.path !== '/workbench'
    })
  },
  { immediate: true }
)

function syncFullscreenState() {
  isFullscreen.value = Boolean(document.fullscreenElement)
}

onMounted(() => {
  syncFullscreenState()
  document.addEventListener('fullscreenchange', syncFullscreenState)
})

onBeforeUnmount(() => {
  document.removeEventListener('fullscreenchange', syncFullscreenState)
  stopSiderResize()
})

async function toggleFullscreen() {
  if (document.fullscreenElement) {
    await document.exitFullscreen()
    return
  }
  await document.documentElement.requestFullscreen()
}

function startSiderResize(event: PointerEvent) {
  event.preventDefault()
  document.body.classList.add('sider-resizing')
  window.addEventListener('pointermove', resizeSider)
  window.addEventListener('pointerup', stopSiderResize)
}

function resizeSider(event: PointerEvent) {
  const nextWidth = Math.min(MAX_SIDER_WIDTH, Math.max(MIN_SIDER_WIDTH, event.clientX))
  siderWidth.value = nextWidth
  localStorage.setItem(SIDER_WIDTH_KEY, String(nextWidth))
}

function stopSiderResize() {
  document.body.classList.remove('sider-resizing')
  window.removeEventListener('pointermove', resizeSider)
  window.removeEventListener('pointerup', stopSiderResize)
}

async function handleLogout() {
  await authStore.logout()
  resetDynamicRoutes()
  await router.replace('/login')
}
</script>

<style scoped>
.fade-slide-enter-active,
.fade-slide-leave-active {
  transition: all 0.2s ease;
}

.fade-slide-enter-from,
.fade-slide-leave-to {
  opacity: 0;
  transform: translateY(6px);
}

.app-root {
  height: 100vh;
  overflow: hidden;
}

.app-main {
  height: 100vh;
  min-width: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 0;
}

.app-main-fullscreen .layout-content {
  padding: 0;
}

.app-header {
  flex: 0 0 auto;
  border-bottom: 1px solid #e2e8f0;
  background: #ffffff;
  padding: 10px 18px 8px;
}

.header-main {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 40px;
}

.header-refresh,
.header-fullscreen {
  flex: 0 0 auto;
}

.breadcrumb-group {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
}

.summary-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

.layout-content {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  padding: 16px 18px 18px;
}

.content-scroll {
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

.app-backdrop {
  background: #f5f7fb;
}

.layout-sider {
  position: relative;
  background: #ffffff;
  border-right: 1px solid #e2e8f0;
}

.layout-sider-hidden {
  border-right: 0;
}

.brand-mark {
  display: flex;
  align-items: center;
  gap: 11px;
  height: 64px;
  padding: 0 18px;
  border-bottom: 1px solid #eef2f7;
}

.brand-dot {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: linear-gradient(135deg, #111827, #2563eb);
  box-shadow: 0 10px 22px rgba(37, 99, 235, 0.18);
}

.brand-name {
  color: #111827;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-wrap {
  padding: 12px 10px;
}

.sider-resizer {
  position: absolute;
  top: 0;
  right: -3px;
  z-index: 3;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  touch-action: none;
}

.sider-resizer::after {
  content: '';
  position: absolute;
  top: 0;
  right: 2px;
  width: 2px;
  height: 100%;
  background: transparent;
  transition: background 0.16s ease;
}

.sider-resizer:hover::after,
:global(.sider-resizing) .sider-resizer::after {
  background: #2563eb;
}

:global(.sider-resizing) {
  cursor: col-resize;
  user-select: none;
}

.layout-sider-collapsed .brand-mark {
  justify-content: center;
  padding: 0;
}

.layout-sider-collapsed .brand-name {
  display: none;
}
</style>
