<script setup>
// ========================================================================
//  AdminLayout —— 全站布局框架
//  ========================================================================
//  - 左侧玻璃 Sider：三分组菜单（监控 / AI 管理 / 知识库），折叠态记 localStorage
//  - 顶栏：面包屑 + 服务健康点 + 失败告警铃铛 + 命令面板按钮 + 主题切换 + 帮助 + 用户区
//  - 全局机制：/health 离线检测、失败告警轮询、JWT 静默续签、Ctrl+K、首登 Tour
// ========================================================================
import { ref, computed, onMounted, onUnmounted, h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Modal, notification } from 'ant-design-vue'
import {
    DashboardOutlined, UnorderedListOutlined, HistoryOutlined, BarChartOutlined,
    FileTextOutlined, DesktopOutlined, BugOutlined, ApiOutlined, SettingOutlined,
    DatabaseOutlined, SearchOutlined, BellOutlined, BulbOutlined, QuestionCircleOutlined,
    UserOutlined, LogoutOutlined, ThunderboltOutlined, MenuFoldOutlined, MenuUnfoldOutlined,
    CloudDownloadOutlined
} from '@ant-design/icons-vue'
import { useAppStore } from '@/stores/app'
import { useUserStore } from '@/stores/user'
import { probeHealth } from '@/api/request'
import { getStats, getRuns } from '@/api/agent'
import { usePolling } from '@/composables/usePolling'
import { fmtTime, sceneLabel } from '@/utils/format'

const app = useAppStore()
const userStore = useUserStore()
const route = useRoute()
const router = useRouter()

// ---------------- Sider 菜单（三分组） ----------------
const COLLAPSE_KEY = 'agent_admin_sider_collapsed'
const collapsed = ref(localStorage.getItem(COLLAPSE_KEY) === '1')
function toggleCollapse() {
    collapsed.value = !collapsed.value
    localStorage.setItem(COLLAPSE_KEY, collapsed.value ? '1' : '0')
}

const menuItems = [
    { type: 'group', label: '监控', children: [
        { key: '/dashboard', icon: () => h(DashboardOutlined), label: '仪表盘' },
        { key: '/runs', icon: () => h(UnorderedListOutlined), label: '运行记录' },
        { key: '/history', icon: () => h(HistoryOutlined), label: '历史记录' },
        { key: '/stats', icon: () => h(BarChartOutlined), label: '统计分析' },
        { key: '/logs', icon: () => h(FileTextOutlined), label: '实时日志' },
        { key: '/system', icon: () => h(DesktopOutlined), label: '系统状态' }
    ]},
    { type: 'group', label: 'AI 管理', children: [
        { key: '/debug', icon: () => h(BugOutlined), label: '调试工具' },
        { key: '/models', icon: () => h(ApiOutlined), label: '模型管理' },
        { key: '/config', icon: () => h(SettingOutlined), label: '配置总览' }
    ]},
    { type: 'group', label: '知识库', children: [
        { key: '/kb', icon: () => h(DatabaseOutlined), label: '知识库管理' },
        { key: '/kb-search', icon: () => h(SearchOutlined), label: '知识库检索' },
        { key: '/kb-crawl', icon: () => h(CloudDownloadOutlined), label: '药品抓取' }
    ]}
]
const selectedKeys = computed(() => ['/' + route.path.split('/')[1]])
function onMenuClick({ key }) { router.push(key) }

// ---------------- 服务健康点（离线检测） ----------------
let healthFails = 0
usePolling(async () => {
    try {
        await probeHealth()
        healthFails = 0
        if (app.offline) {
            app.offline = false
            notification.success({ message: '服务已恢复', description: 'Agent 服务重新可达' })
        }
    } catch {
        healthFails++
        // 连续 2 次失败才判离线（容忍单次网络抖动）
        if (healthFails >= 2 && !app.offline) {
            app.offline = true
            app.offlineSince = Date.now()
        }
    }
}, 10000)

// ---------------- 失败告警（铃铛 + 浏览器通知） ----------------
const bellOpen = ref(false)
const recentFails = ref([])
usePolling(async () => {
    try {
        const res = await getStats({ silent: true })
        const failed = res.data.failed + res.data.blocked
        if (app.lastFailedTotal >= 0 && failed > app.lastFailedTotal) {
            const delta = failed - app.lastFailedTotal
            app.alarmCount += delta
            if (app.notifyEnabled) {
                notification.warning({
                    message: `新增 ${delta} 条失败/拦截运行`,
                    description: res.data.last_error ? res.data.last_error.slice(0, 80) : '',
                    onClick: () => router.push('/runs?status=2')
                })
                // 浏览器系统通知（需授权；页面在后台时也能提醒）
                if ('Notification' in window && Notification.permission === 'granted') {
                    new Notification('TCM Agent 告警', { body: `新增 ${delta} 条失败运行` })
                }
            }
        }
        app.lastFailedTotal = failed
    } catch { /* 离线时静默 */ }
}, 15000)

async function openBell() {
    bellOpen.value = true
    app.clearAlarm()
    try {
        // 铃铛计数含「失败(2)+拦截(3)」，列表也要两种都展示——
        // runs 接口一次只能筛一种状态，所以拉全量后前端过滤取前 8 条
        const res = await getRuns({ limit: 50 }, { silent: true })
        recentFails.value = (res.data || []).filter(r => r.status >= 2).slice(0, 8)
    } catch { recentFails.value = [] }
}
function toggleNotifyPermission() {
    app.toggleNotify()
    if (app.notifyEnabled && 'Notification' in window && Notification.permission === 'default') {
        Notification.requestPermission()
    }
}

// ---------------- JWT 静默续签（每 10 分钟检查一次） ----------------
usePolling(() => userStore.maybeRefresh(), 600000, { immediate: true })

// ---------------- Ctrl+K 命令面板 ----------------
function onGlobalKey(e) {
    if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault()
        app.paletteOpen = !app.paletteOpen
    }
}
onMounted(() => window.addEventListener('keydown', onGlobalKey))
onUnmounted(() => window.removeEventListener('keydown', onGlobalKey))

// ---------------- 首登新手引导（antd Tour） ----------------
const TOUR_KEY = 'agent_admin_tour_done'
const refSider = ref(null), refHealth = ref(null), refBell = ref(null)
const refPalette = ref(null), refTheme = ref(null), refHelp = ref(null)
// Tour 目标解析：必须返回真实 DOM 元素，否则返回 null（antd 会安全地居中展示）。
// 组件 ref 拿到的是组件实例，直接传给 Tour 会在定位计算时抛
// "getBoundingClientRect is not a function"——遮罩渲染了但定位卡死，
// 且异常会打断 Vue 更新队列导致整个布局停止响应（已踩过坑）
function tourEl(r) {
    const el = r?.$el ?? r
    return el instanceof HTMLElement ? el : null
}
const tourSteps = [
    { title: '侧边菜单', description: '功能按「监控 / AI 管理 / 知识库」三组划分：看运行去监控，调模型去 AI 管理，管语料去知识库。', target: () => tourEl(refSider.value) },
    { title: '服务健康点', description: '绿点=Go 服务在线，红点=不可达（会弹出全局横幅并自动重连）。', target: () => tourEl(refHealth.value) },
    { title: '失败告警', description: '出现新的失败/拦截运行时这里会亮红点，可开关浏览器通知；点开可直达失败详情。', target: () => tourEl(refBell.value) },
    { title: '命令面板', description: '按 Ctrl+K 随时唤起：搜页面、执行快捷动作、输入运行 ID 直达详情。', target: () => tourEl(refPalette.value) },
    { title: '主题切换', description: '暗色暖调是默认主题，也可以切到亮色，偏好会记住。', target: () => tourEl(refTheme.value) },
    { title: '随时回看引导', description: '点这里可以重新播放本引导。先去「调试工具」发一条测试请求，再回仪表盘看数据流动起来！', target: () => tourEl(refHelp.value) }
]
function onTourFinish() {
    app.tourOpen = false
    localStorage.setItem(TOUR_KEY, '1')
}
onMounted(() => {
    // 首次登录自动启动（localStorage 无标记时）
    if (localStorage.getItem(TOUR_KEY) !== '1') {
        setTimeout(() => app.startTour(), 800)
    }
})

// ---------------- 用户区 ----------------
function doLogout() {
    Modal.confirm({
        title: '确定退出登录？',
        onOk: () => {
            userStore.logout()
            router.push('/login')
        }
    })
}

// 离线横幅重连倒计时展示
const now = ref(Date.now())
usePolling(() => { now.value = Date.now() }, 1000)
const retryCountdown = computed(() => {
    const elapsed = Math.floor((now.value - app.offlineSince) / 1000)
    return 10 - (elapsed % 10)
})
</script>

<template>
  <a-layout class="min-h-screen" style="background: transparent">
    <!-- 侧边栏 -->
    <a-layout-sider ref="refSider" v-model:collapsed="collapsed" :trigger="null" collapsible
                    :width="224" :collapsed-width="64"
                    class="!sticky top-0 h-screen glass-strong !rounded-none border-r"
                    style="border-color: var(--glass-border); z-index: 20">
      <div class="flex items-center gap-2 px-4 h-14" style="border-bottom: 1px solid var(--glass-border)">
        <span class="text-xl">🌿</span>
        <span v-if="!collapsed" class="font-semibold text-sm whitespace-nowrap" style="color: var(--primary)">TCM Agent 控制台</span>
      </div>
      <a-menu :selected-keys="selectedKeys" mode="inline" :items="menuItems"
              style="background: transparent; border-inline-end: none"
              @click="onMenuClick" />
    </a-layout-sider>

    <a-layout style="background: transparent">
      <!-- 顶栏 -->
      <a-layout-header class="!px-4 !h-14 flex items-center gap-3 sticky top-0 glass-strong !rounded-none"
                       style="z-index: 19; line-height: normal; border-bottom: 1px solid var(--glass-border); background: var(--glass-bg-strong)">
        <a-button type="text" @click="toggleCollapse">
          <MenuUnfoldOutlined v-if="collapsed" /><MenuFoldOutlined v-else />
        </a-button>
        <a-breadcrumb>
          <a-breadcrumb-item>{{ route.meta.group || 'TCM Agent' }}</a-breadcrumb-item>
          <a-breadcrumb-item>{{ route.meta.title }}</a-breadcrumb-item>
        </a-breadcrumb>

        <div class="flex-1"></div>

        <!-- 服务健康点 -->
        <a-tooltip :title="app.offline ? '服务不可达' : '服务在线（10s 轮询 /health）'">
          <span ref="refHealth" class="flex items-center gap-1.5 text-xs" style="color: var(--text-2)">
            <span class="health-dot" :class="{ down: app.offline }"></span>
            {{ app.offline ? '离线' : '在线' }}
          </span>
        </a-tooltip>

        <!-- 失败告警铃铛 -->
        <a-popover v-model:open="bellOpen" trigger="click" placement="bottomRight" @open-change="v => v && openBell()">
          <template #content>
            <div class="w-72">
              <div class="flex items-center justify-between mb-2">
                <span class="text-sm font-medium">最近失败运行</span>
                <a-switch :checked="app.notifyEnabled" size="small"
                          checked-children="通知开" un-checked-children="通知关"
                          @click="toggleNotifyPermission" />
              </div>
              <div v-if="!recentFails.length" class="text-xs text-center py-4" style="color: var(--text-3)">暂无失败记录</div>
              <div v-for="r in recentFails" :key="r.id"
                   class="py-1.5 px-2 rounded cursor-pointer text-xs glass-hover flex items-center gap-2"
                   @click="bellOpen = false; app.openRun(r.id)">
                <span class="mono" style="color: var(--text-3)">#{{ r.id }}</span>
                <span>{{ sceneLabel(r.scene) }}</span>
                <span class="ml-auto" style="color: var(--text-3)">{{ fmtTime(r.started_at) }}</span>
              </div>
              <a-button block size="small" class="mt-2" @click="bellOpen = false; router.push('/runs?status=2')">查看全部失败</a-button>
            </div>
          </template>
          <a-badge ref="refBell" :count="app.alarmCount" :offset="[-2, 4]" size="small">
            <a-button type="text"><BellOutlined /></a-button>
          </a-badge>
        </a-popover>

        <!-- 命令面板入口（多入口之一，另有 Ctrl+K） -->
        <a-tooltip title="命令面板（Ctrl+K）">
          <a-button ref="refPalette" type="text" @click="app.paletteOpen = true">
            <ThunderboltOutlined />
          </a-button>
        </a-tooltip>

        <!-- 主题切换 -->
        <a-tooltip :title="app.theme === 'dark' ? '切换到亮色' : '切换到暗色'">
          <a-button ref="refTheme" type="text" @click="app.toggleTheme()"><BulbOutlined /></a-button>
        </a-tooltip>

        <!-- 帮助（重播引导） -->
        <a-tooltip title="重播新手引导">
          <a-button ref="refHelp" type="text" @click="app.startTour()"><QuestionCircleOutlined /></a-button>
        </a-tooltip>

        <!-- 用户下拉 -->
        <a-dropdown>
          <span class="flex items-center gap-2 cursor-pointer px-2">
            <a-avatar size="small" style="background: var(--primary)"><template #icon><UserOutlined /></template></a-avatar>
            <span class="text-sm" style="color: var(--text-1)">{{ userStore.user?.nick_name || '管理员' }}</span>
          </span>
          <template #overlay>
            <a-menu>
              <a-menu-item disabled>
                <span class="text-xs">{{ userStore.user?.role_name || '面板管理员' }}</span>
              </a-menu-item>
              <a-menu-divider />
              <a-menu-item @click="doLogout"><LogoutOutlined /> 退出登录</a-menu-item>
            </a-menu>
          </template>
        </a-dropdown>
      </a-layout-header>

      <!-- 服务离线横幅 -->
      <a-alert v-if="app.offline" banner type="error" class="!rounded-none"
               :message="`Agent 服务不可达 —— 正在自动重连（${retryCountdown}s 后重试），服务恢复后本横幅自动消失`" />

      <!-- 内容区 -->
      <a-layout-content class="p-5">
        <router-view />
      </a-layout-content>
    </a-layout>

    <!-- 新手引导 -->
    <a-tour :open="app.tourOpen" :steps="tourSteps" @close="onTourFinish" @finish="onTourFinish" />
  </a-layout>
</template>

<style scoped>
/* 菜单玻璃化：菜单项 hover/选中带暖色 */
:deep(.ant-menu-item-selected) {
  background: rgba(245, 158, 11, 0.14) !important;
}
:deep(.ant-menu-item-group-title) {
  font-size: 11px;
  color: var(--text-3);
}
</style>
