<template>
  <!-- ========== PC 端布局 ========== -->
  <el-container v-if="!isMobile" class="layout-container">
    <el-aside :width="isCollapse ? '64px' : '220px'" class="layout-aside">
      <div class="logo-area">
        <img src="@/assets/logo.svg" alt="logo" class="logo-icon" />
        <span v-show="!isCollapse" class="logo-text">机器任务管理</span>
      </div>
      <el-menu
        :default-active="activeMenu"
        :collapse="isCollapse"
        router
        background-color="#001529"
        text-color="#ffffffa6"
        active-text-color="#ffffff"
        class="aside-menu"
      >
        <el-menu-item v-if="!isMonitorMode" index="/dashboard">
          <el-icon><Monitor /></el-icon>
          <template #title>首页</template>
        </el-menu-item>
        <el-menu-item index="/task/chain">
          <el-icon><List /></el-icon>
          <template #title>任务链管理</template>
        </el-menu-item>
        <el-menu-item index="/elevator">
          <el-icon><SetUp /></el-icon>
          <template #title>提升机查看</template>
        </el-menu-item>
        <el-menu-item index="/station/queue">
          <el-icon><Tickets /></el-icon>
          <template #title>站点排队</template>
        </el-menu-item>
        <el-menu-item index="/resource/extension">
          <el-icon><Box /></el-icon>
          <template #title>拓展资源</template>
        </el-menu-item>
        <el-menu-item v-if="!isMonitorMode" index="/vendor/load">
          <el-icon><DataLine /></el-icon>
          <template #title>厂商负载</template>
        </el-menu-item>
        <el-menu-item v-if="!isMonitorMode" index="/generate">
          <el-icon><Plus /></el-icon>
          <template #title>任务生成</template>
        </el-menu-item>
        <el-menu-item index="/docs/exception">
          <el-icon><Document /></el-icon>
          <template #title>异常处理</template>
        </el-menu-item>
      </el-menu>
    </el-aside>

    <el-container>
      <el-header class="layout-header">
        <div class="header-left">
          <el-icon class="collapse-btn" @click="isCollapse = !isCollapse">
            <Fold v-if="!isCollapse" />
            <Expand v-else />
          </el-icon>
          <el-tooltip content="刷新当前页" placement="bottom">
            <el-icon class="refresh-btn" @click="refreshCurrentTab"><Refresh /></el-icon>
          </el-tooltip>
          <el-breadcrumb separator="/">
            <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item v-if="currentTitle">{{ currentTitle }}</el-breadcrumb-item>
          </el-breadcrumb>
        </div>
        <div class="header-right">
          <el-dropdown @command="handleCommand">
            <span class="user-info">
              <el-icon><User /></el-icon>
              <span>{{ userName }}</span>
              <el-icon class="el-icon--right"><ArrowDown /></el-icon>
            </span>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item command="logout">退出登录</el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </div>
      </el-header>
      <!-- PC 端标签栏 -->
      <div v-if="tabsStore.tabs.length > 0" class="layout-tabs">
        <div class="tabs-scroll">
          <template v-for="(t, index) in tabsStore.tabs" :key="t.path">
            <div
              v-if="dragFromIndex >= 0 && dragOverIndex === index"
              class="tab-drop-indicator"
            />
            <div
              :class="['tab-item', { 'tab-active': route.path === t.path, 'tab-dragging': dragFromIndex === index }]"
              draggable="true"
              @click="goTab(t.path)"
              @contextmenu.prevent="showTabMenu($event, t.path, index)"
              @dragstart="onTabDragStart($event, index)"
              @dragover.prevent="onTabDragOver($event, index)"
              @dragleave="onTabDragLeave"
              @drop.prevent="onTabDrop($event, index)"
              @dragend="onTabDragEnd"
            >
              <el-tooltip :content="t.title" placement="top" :show-after="300">
                <span class="tab-title">{{ t.title }}</span>
              </el-tooltip>
              <span class="tab-close" @click.stop="closeTab(t.path)">×</span>
            </div>
          </template>
        </div>
        <div
          v-if="tabMenuVisible"
          class="tab-context-menu"
          :style="{ left: tabMenuX + 'px', top: tabMenuY + 'px' }"
          @mousedown.stop
        >
          <div class="tab-menu-item" @mousedown.prevent.stop="closeOthersTab">关闭其他</div>
          <div
            v-if="hasRightTabs"
            class="tab-menu-item"
            @mousedown.prevent.stop="closeRightTab"
          >
            关闭右边
          </div>
        </div>
      </div>
      <el-main ref="layoutMainRef" class="layout-main">
        <router-view v-slot="{ Component }">
          <keep-alive :include="keepAliveNames" :max="20">
            <component :is="Component" :key="componentKey" />
          </keep-alive>
        </router-view>
      </el-main>
    </el-container>
  </el-container>

  <!-- ========== 移动端布局 ========== -->
  <div v-else class="mobile-layout">
    <header class="mobile-header">
      <span class="mobile-header-title">{{ currentTitle || '首页' }}</span>
      <div class="mobile-header-actions">
        <el-icon class="mobile-refresh-btn" @click="refreshCurrentTab"><Refresh /></el-icon>
        <el-dropdown @command="handleCommand">
          <el-icon class="mobile-header-avatar"><User /></el-icon>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item command="logout">退出登录</el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
      </div>
    </header>

    <main ref="mobileMainRef" class="mobile-main">
      <router-view v-slot="{ Component }">
        <keep-alive :include="keepAliveNames" :max="20">
          <component :is="Component" :key="componentKey" />
        </keep-alive>
      </router-view>
    </main>

    <nav class="mobile-tab-bar">
      <router-link
        v-for="tab in tabs"
        :key="tab.path"
        :to="tab.path"
        :class="['tab-item', { 'tab-active': activeTab === tab.path }]"
      >
        <el-icon :size="22"><component :is="tab.icon" /></el-icon>
        <span class="tab-label">{{ tab.label }}</span>
      </router-link>
    </nav>
  </div>
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user'
import { useDevice } from '@/composables/useDevice'
import { Monitor, List, SetUp, Plus, DataLine, Fold, Expand, User, ArrowDown, Refresh, Tickets, Box, Document } from '@element-plus/icons-vue'
import { useTabsStore } from '@/stores/tabs'

const { isMobile } = useDevice()
const route = useRoute()
const router = useRouter()
const tabsStore = useTabsStore()
const refreshKeys = ref<Record<string, number>>({})
const layoutMainRef = ref<{ $el?: HTMLElement } | HTMLElement | null>(null)
const mobileMainRef = ref<HTMLElement | null>(null)
const scrollPositions = ref<Record<string, number>>({})
const skipNextScrollSavePaths = new Set<string>()
const tabMenuVisible = ref(false)
const tabMenuX = ref(0)
const tabMenuY = ref(0)
const tabMenuPath = ref('')
const tabMenuIndex = ref(-1)
const dragFromIndex = ref(-1)
const dragOverIndex = ref(-1)

const hasRightTabs = computed(() => tabMenuIndex.value >= 0 && tabMenuIndex.value < tabsStore.tabs.length - 1)

const componentKey = computed(() => {
  const k = refreshKeys.value[route.path] ?? 0
  return `${route.fullPath}-${k}`
})

function refreshCurrentTab() {
  clearScrollPosition(route.fullPath)
  refreshKeys.value[route.path] = (refreshKeys.value[route.path] ?? 0) + 1
  nextTick(() => {
    restoreScrollPosition(route.fullPath)
  })
}

const userStore = useUserStore()
const isMonitorMode = computed(() => userStore.isMonitorMode())
const keepAliveNames = computed(() => {
  if (isMonitorMode.value) {
    return ['TaskChainList', 'TaskChainDetail', 'ElevatorList', 'StationQueueView', 'ExtensionResourceView', 'ExceptionDocsView']
  }
  return [
    'DashboardView',
    'TaskChainList',
    'TaskChainDetail',
    'ElevatorList',
    'StationQueueView',
  'ExtensionResourceView',
    'VendorLoadView',
    'TaskGenerateView',
    'ExceptionDocsView',
  ]
})

// 必须在 watch 之前恢复，否则 watch 的 immediate 会先执行并覆盖 storage
const activePath = tabsStore.restoreFromStorage()
if (activePath && activePath !== route.path) {
  router.replace(activePath).then(() => {
    tabsStore.enableSave()
  })
} else {
  tabsStore.enableSave()
}

watch(
  () => route.path,
  () => {
    if (route.path.startsWith('/login')) return
    tabsStore.addOrSwitch(route)
  },
  { immediate: true },
)

watch(
  () => route.fullPath,
  async (fullPath, oldFullPath) => {
    if (oldFullPath) {
      saveScrollPosition(oldFullPath)
    }
    await nextTick()
    restoreScrollPosition(fullPath)
  },
)

function getScrollContainer() {
  if (isMobile.value) return mobileMainRef.value
  const el = layoutMainRef.value
  if (!el) return null
  return el instanceof HTMLElement ? el : el.$el ?? null
}

function saveScrollPosition(path: string) {
  if (shouldSkipScrollSave(path)) return
  const container = getScrollContainer()
  if (!container) return
  scrollPositions.value[path] = container.scrollTop
}

function restoreScrollPosition(path: string) {
  const container = getScrollContainer()
  if (!container) return
  container.scrollTop = scrollPositions.value[path] ?? 0
}

function clearScrollPosition(path: string, skipNextSave = false) {
  if (skipNextSave) {
    skipNextScrollSavePaths.add(path)
  }
  Object.keys(scrollPositions.value).forEach((key) => {
    if (key === path || key.startsWith(`${path}?`)) {
      delete scrollPositions.value[key]
    }
  })
}

function keepOnlyScrollPosition(path: string) {
  Object.keys(scrollPositions.value).forEach((key) => {
    if (key !== path && !key.startsWith(`${path}?`)) {
      delete scrollPositions.value[key]
    }
  })
}

function shouldSkipScrollSave(path: string) {
  for (const skipPath of skipNextScrollSavePaths) {
    if (path === skipPath || path.startsWith(`${skipPath}?`)) {
      skipNextScrollSavePaths.delete(skipPath)
      return true
    }
  }
  return false
}

function goTab(path: string) {
  if (route.path === path) return
  router.push(path)
}

function closeTab(path: string) {
  const idx = tabsStore.tabs.findIndex((t) => t.path === path)
  if (idx < 0) return
  const next = tabsStore.tabs[idx - 1] ?? tabsStore.tabs[idx + 1]
  const nextPath = next ? next.path : '/dashboard'
  tabsStore.remove(path)
  clearScrollPosition(path, route.path === path)
  if (route.path === path) {
    router.push(nextPath)
  }
  tabsStore.saveToStorage(route.path === path ? nextPath : route.path)
}

function showTabMenu(e: MouseEvent, path: string, index: number) {
  tabMenuPath.value = path
  tabMenuIndex.value = index
  tabMenuX.value = e.clientX
  tabMenuY.value = e.clientY
  tabMenuVisible.value = true
}

function closeOthersTab() {
  const path = tabMenuPath.value
  tabMenuVisible.value = false
  if (tabsStore.tabs.length <= 1) return
  tabsStore.closeOthers(path)
  keepOnlyScrollPosition(path)
  if (route.path !== path) {
    skipNextScrollSavePaths.add(route.fullPath)
    router.push(path)
  }
  tabsStore.saveToStorage(path)
}

function closeRightTab() {
  const path = tabMenuPath.value
  const idx = tabMenuIndex.value
  tabMenuVisible.value = false
  tabMenuIndex.value = -1
  if (idx < 0 || idx >= tabsStore.tabs.length - 1) return
  const closedPaths = tabsStore.tabs.slice(idx + 1).map((t) => t.path)
  tabsStore.closeRight(idx)
  closedPaths.forEach((closedPath) => {
    clearScrollPosition(closedPath, closedPath === route.path)
  })
  const needNav = closedPaths.includes(route.path)
  if (needNav) {
    router.push(path)
  }
  tabsStore.saveToStorage(needNav ? path : route.path)
}

function hideTabMenu() {
  tabMenuVisible.value = false
}

function onTabDragStart(e: DragEvent, index: number) {
  dragFromIndex.value = index
  e.dataTransfer!.effectAllowed = 'move'
  e.dataTransfer!.setData('text/plain', String(index))
}

function onTabDragOver(e: DragEvent, index: number) {
  e.dataTransfer!.dropEffect = 'move'
  dragOverIndex.value = index
}

function onTabDragLeave() {
  dragOverIndex.value = -1
}

function onTabDragEnd() {
  dragFromIndex.value = -1
  dragOverIndex.value = -1
}

function onTabDrop(_e: DragEvent, toIndex: number) {
  const fromIndex = dragFromIndex.value
  if (fromIndex >= 0 && fromIndex !== toIndex) {
    tabsStore.reorder(fromIndex, toIndex)
    tabsStore.saveToStorage(route.path)
  }
  dragFromIndex.value = -1
  dragOverIndex.value = -1
}

onMounted(() => {
  document.addEventListener('click', hideTabMenu)
})

onUnmounted(() => {
  document.removeEventListener('click', hideTabMenu)
})
const isCollapse = ref(false)

const tabs = computed(() => [
  ...(!isMonitorMode.value ? [{ path: '/dashboard', label: '首页', icon: Monitor }] : []),
  { path: '/task/chain', label: '任务链', icon: List },
  { path: '/elevator', label: '提升机', icon: SetUp },
  { path: '/station/queue', label: '排队', icon: Tickets },
  { path: '/resource/extension', label: '资源', icon: Box },
  ...(!isMonitorMode.value ? [{ path: '/vendor/load', label: '厂商负载', icon: DataLine }] : []),
  ...(!isMonitorMode.value ? [{ path: '/generate', label: '生成', icon: Plus }] : []),
  { path: '/docs/exception', label: '异常', icon: Document },
])

const activeMenu = computed(() => {
  const path = route.path
  if (path.startsWith('/task/chain')) return '/task/chain'
  if (path.startsWith('/station/queue')) return '/station/queue'
  if (path.startsWith('/resource/extension')) return '/resource/extension'
  if (path.startsWith('/docs/exception')) return '/docs/exception'
  return path
})

const activeTab = computed(() => {
  const path = route.path
  if (path.startsWith('/task/chain')) return '/task/chain'
  if (path.startsWith('/station/queue')) return '/station/queue'
  if (path.startsWith('/resource/extension')) return '/resource/extension'
  if (path.startsWith('/docs/exception')) return '/docs/exception'
  return path
})

const currentTitle = computed(() => (route.meta.title as string) || '')
const userName = computed(() => userStore.user?.name || '管理员')

function handleCommand(command: string) {
  if (command === 'logout') {
    userStore.logout()
  }
}
</script>

<style scoped>
/* ===== PC 端 ===== */
.layout-container {
  height: 100vh;
}

.layout-aside {
  background-color: #001529;
  transition: width 0.3s;
  overflow: hidden;
}

.logo-area {
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 16px;
  border-bottom: 1px solid #ffffff1a;
}

.logo-icon {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
}

.logo-text {
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  white-space: nowrap;
}

.aside-menu {
  border-right: none;
}

.layout-header {
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  padding: 0 20px;
  z-index: 1;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 16px;
}

.collapse-btn {
  font-size: 20px;
  cursor: pointer;
  color: #606266;
}

.collapse-btn:hover {
  color: #409eff;
}

.refresh-btn {
  font-size: 18px;
  cursor: pointer;
  color: #606266;
  margin-left: 4px;
}

.refresh-btn:hover {
  color: #409eff;
}

.header-right {
  display: flex;
  align-items: center;
}

.user-info {
  display: flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  font-size: 14px;
  color: #606266;
}

.layout-tabs {
  background: #f5f7fa;
  padding: 0 16px;
  border-bottom: 1px solid #e4e7ed;
  flex-shrink: 0;
  position: relative;
}

.tab-context-menu {
  position: fixed;
  z-index: 9999;
  min-width: 120px;
  padding: 4px 0;
  background: #fff;
  border-radius: 4px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
}

.tab-menu-item {
  padding: 8px 16px;
  font-size: 13px;
  color: #606266;
  cursor: pointer;
}

.tab-menu-item:hover {
  background: #f5f7fa;
  color: #409eff;
}

.tabs-scroll {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 10px 0 8px;
}

.tab-drop-indicator {
  width: 3px;
  height: 24px;
  background: #409eff;
  border-radius: 2px;
  flex-shrink: 0;
  animation: tab-drop-pulse 0.6s ease-in-out infinite;
}

@keyframes tab-drop-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

.layout-tabs .tab-item.tab-dragging {
  opacity: 0.5;
}

.layout-tabs .tab-item {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
  padding: 10px 14px;
  border-radius: 6px 6px 0 0;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  color: #909399;
  background: #e9ecef;
  transition: all 0.2s;
  user-select: none;
}

.layout-tabs .tab-item:hover {
  color: #606266;
  background: #dee2e6;
}

.layout-tabs .tab-item.tab-active {
  background: #fff;
  color: #409eff;
  font-weight: 500;
  box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.04);
  border-bottom: 2px solid #409eff;
  margin-bottom: -1px;
}

.layout-tabs .tab-item.tab-active:hover {
  background: #fff;
  color: #409eff;
}

.layout-tabs .tab-title {
  font-size: 13px;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 20px;
  flex-shrink: 1;
  min-width: 0;
}

.layout-tabs .tab-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-left: 2px;
  font-size: 18px;
  line-height: 1;
  font-weight: 300;
  color: #c0c4cc;
  transition: color 0.2s;
  border-radius: 2px;
}

.layout-tabs .tab-item:hover .tab-close {
  color: #909399;
}

.layout-tabs .tab-close:hover {
  color: #f56c6c;
  background: rgba(245, 108, 108, 0.1);
}

.layout-main {
  background: #f0f2f5;
  padding: 20px;
}

/* ===== 移动端 ===== */
.mobile-layout {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  background: #f5f7fa;
}

.mobile-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 16px;
  height: 48px;
  background: #fff;
  border-bottom: 1px solid #ebeef5;
  flex-shrink: 0;
}

.mobile-header-title {
  font-size: 17px;
  font-weight: 600;
  color: #303133;
}

.mobile-header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.mobile-refresh-btn {
  font-size: 20px;
  color: #606266;
  cursor: pointer;
}

.mobile-refresh-btn:active {
  color: #409eff;
}

.mobile-header-avatar {
  font-size: 22px;
  color: #606266;
  cursor: pointer;
}

.mobile-main {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 12px;
}

.mobile-tab-bar {
  display: flex;
  align-items: center;
  justify-content: space-around;
  height: 56px;
  background: #fff;
  border-top: 1px solid #ebeef5;
  flex-shrink: 0;
  padding-bottom: env(safe-area-inset-bottom);
}

.tab-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  text-decoration: none;
  color: #909399;
  transition: color 0.2s;
  -webkit-tap-highlight-color: transparent;
}

.tab-active {
  color: #409eff;
}

.tab-label {
  font-size: 11px;
  line-height: 1;
}
</style>
