<template>
  <!-- 可拖动悬浮入口：位置持久化，点击打开抽屉 -->
  <button
    v-show="!open"
    class="ai-fab"
    type="button"
    :class="{ 'is-dragging': fabDragging }"
    :style="fabStyle"
    title="AI 分析助手（可拖动调整位置）"
    @pointerdown="onFabPointerDown"
    @pointermove="onFabPointerMove"
    @pointerup="onFabPointerUp"
    @pointercancel="onFabPointerUp"
  >
    <span class="ai-fab-glyph">*</span>
  </button>

  <section
    class="ai-drawer"
    :class="{ 'is-open': open, 'has-side': showSide }"
    :style="{ width: drawerWidth + 'px' }"
    role="complementary"
    aria-label="AI 分析助手"
  >
    <div
      class="ai-resizer"
      title="拖拽调整宽度"
      @pointerdown="onResizeStart"
      @pointermove="onResizeMove"
      @pointerup="onResizeEnd"
      @pointercancel="onResizeEnd"
    />

    <div class="ai-shell">
      <!-- 主对话区 -->
      <div class="ai-main">
        <header class="ai-head">
          <div class="ai-head-title">
            <span class="ai-head-glyph">*</span>
            <div class="ai-head-text">
              <div class="ai-head-main">AI 分析助手</div>
              <div class="ai-head-sub">故障 / 日志 / 数据分析 · 操作答疑</div>
            </div>
          </div>
          <div class="ai-head-actions">
            <button class="ai-textbtn" title="新建对话" @click="onNew">
              <el-icon><Plus /></el-icon>
              <span>新建对话</span>
            </button>
            <button class="ai-iconbtn" :class="{ active: showSessions }" title="历史会话" @click="toggleSessions">
              <el-icon><ChatLineSquare /></el-icon>
            </button>
            <button class="ai-iconbtn" :class="{ active: showSide }" title="知识与工具" @click="showSide = !showSide">
              <el-icon><Grid /></el-icon>
            </button>
            <button class="ai-iconbtn" title="收起" @click="toggle(false)">
              <el-icon><Close /></el-icon>
            </button>
          </div>

          <div v-if="showSessions" class="ai-sessions">
            <div class="ai-sessions-head">
              <span>历史会话</span>
              <button class="ai-link" @click="onNew">+ 新建</button>
            </div>
            <div v-if="sessions.length === 0" class="ai-sessions-empty">暂无历史会话</div>
            <ul v-else class="ai-sessions-list">
              <li
                v-for="s in sessions"
                :key="s.id"
                class="ai-session-item"
                :class="{ active: s.id === currentSessionId }"
                @click="onPickSession(s.id)"
              >
                <div class="ai-session-main">
                  <div class="ai-session-title">{{ s.title || '未命名会话' }}</div>
                  <div class="ai-session-meta">{{ s.turns }} 轮 · {{ formatTime(s.updated) }}</div>
                </div>
                <button class="ai-session-del" title="删除" @click.stop="onDeleteSession(s.id)">
                  <el-icon><Delete /></el-icon>
                </button>
              </li>
            </ul>
          </div>
        </header>

        <div ref="listRef" class="ai-body" @click="showSessions = false">
          <div v-if="messages.length === 0" class="ai-empty">
            <div class="ai-empty-glyph">*</div>
            <div class="ai-empty-title">我帮你做故障 / 日志 / 数据分析</div>
            <div class="ai-empty-tip">试着这样问，或从右侧知识与工具切入：</div>
            <button v-for="(ex, i) in examples" :key="i" class="ai-example" @click="useExample(ex)">
              {{ ex }}
            </button>
          </div>

          <div v-for="m in messages" :key="m.id" class="ai-msg" :class="`ai-msg--${m.role}`">
            <div v-if="m.role === 'assistant'" class="ai-avatar ai-avatar--bot" aria-hidden="true">*</div>
            <div class="ai-bubble">
              <div v-if="m.role === 'user'" class="ai-user-text">{{ m.content }}</div>

              <template v-else>
                <div v-if="m.tools.length" class="ai-tools">
                  <details v-for="(t, ti) in m.tools" :key="ti" class="ai-tool" :class="`is-${t.status}`">
                    <summary class="ai-tool-sum">
                      <span class="ai-tool-ic">
                        <el-icon v-if="t.status === 'running'" class="is-loading"><Loading /></el-icon>
                        <el-icon v-else-if="t.status === 'ok'"><Select /></el-icon>
                        <el-icon v-else><WarningFilled /></el-icon>
                      </span>
                      <span class="ai-tool-name">{{ t.name }}</span>
                      <span class="ai-tool-badge" :class="t.isWrite ? 'write' : 'read'">{{ t.isWrite ? '写' : '读' }}</span>
                    </summary>
                    <div class="ai-tool-body">
                      <div v-if="t.args !== undefined" class="ai-tool-kv">
                        <span class="ai-tool-k">参数</span>
                        <pre class="ai-tool-pre">{{ toJson(t.args) }}</pre>
                      </div>
                      <div v-if="t.result !== undefined" class="ai-tool-kv">
                        <span class="ai-tool-k">结果</span>
                        <pre class="ai-tool-pre">{{ toJson(t.result) }}</pre>
                      </div>
                    </div>
                  </details>
                </div>

                <div v-if="m.content" class="ai-md" v-html="render(m.content)" />
                <span v-if="m.streaming && m.content" class="ai-caret" />
                <div v-if="m.streaming && !m.content && m.tools.length === 0" class="ai-thinking">
                  <el-icon class="is-loading"><Loading /></el-icon><span>正在思考...</span>
                </div>

                <div v-if="m.error" class="ai-error">
                  <el-icon><WarningFilled /></el-icon>
                  <span>{{ m.error }}</span>
                </div>

                <div v-if="!m.streaming && m.content" class="ai-msg-actions">
                  <button type="button" class="ai-act" title="复制" @click="copyText(m.content)">
                    <el-icon><DocumentCopy /></el-icon>
                  </button>
                </div>
              </template>
            </div>
            <div v-if="m.role === 'user'" class="ai-avatar ai-avatar--user" aria-hidden="true">U</div>
          </div>
        </div>

        <footer class="ai-foot">
          <el-input
            v-model="draft"
            type="textarea"
            :rows="2"
            :autosize="{ minRows: 2, maxRows: 6 }"
            resize="none"
            placeholder="描述你想分析的问题，例如：分析当前报警车辆的原因（Enter 发送，Shift+Enter 换行）"
            @keydown="onKeydown"
          />
          <div class="ai-foot-row">
            <div class="ai-chips">
              <button type="button" class="ai-chip" @click="useExample('现在有哪些车辆在报警？帮我分析原因和处理建议')">工具</button>
              <button type="button" class="ai-chip" @click="useExample('结合车队健康数据，给出本周运维重点')">知识库</button>
              <button type="button" class="ai-chip" @click="goPath('/admin/alarms')"># 报警</button>
              <button type="button" class="ai-chip" @click="goPath('/admin/tasks')"># 任务</button>
            </div>
            <div class="ai-foot-actions">
              <el-button v-if="isStreaming" type="danger" plain size="small" @click="onStop">停止</el-button>
              <button
                v-else
                type="button"
                class="ai-send"
                :disabled="!draft.trim()"
                title="发送"
                @click="onSend"
              >
                <el-icon><Promotion /></el-icon>
              </button>
            </div>
          </div>
        </footer>
      </div>

      <!-- 右侧：知识与工具 -->
      <aside v-show="showSide" class="ai-side">
        <div class="ai-side-head">
          <span>知识与工具</span>
          <button type="button" class="ai-iconbtn" title="收起侧栏" @click="showSide = false">
            <el-icon><Close /></el-icon>
          </button>
        </div>

        <div class="ai-side-scroll">
          <section class="ai-card">
            <div class="ai-card-head">
              <span>快捷工具</span>
            </div>
            <div class="ai-tool-grid">
              <button
                v-for="t in quickTools"
                :key="t.id"
                type="button"
                class="ai-tool-tile"
                @click="onQuickTool(t)"
              >
                <span class="ai-tool-tile-ic">{{ t.glyph }}</span>
                <span class="ai-tool-tile-lb">{{ t.label }}</span>
              </button>
            </div>
          </section>

          <section class="ai-card">
            <div class="ai-card-head">
              <span>关键数据</span>
              <button type="button" class="ai-link" @click="refreshSideData">刷新</button>
            </div>
            <div class="ai-kpi">
              <div class="ai-kpi-main">
                <div class="ai-kpi-label">车队在线</div>
                <div class="ai-kpi-value">
                  {{ healthSummary.reachable }}
                  <span class="ai-kpi-unit">/ {{ healthSummary.total }}</span>
                </div>
              </div>
              <div class="ai-spark" aria-hidden="true">
                <span
                  v-for="(h, i) in healthSummary.bars"
                  :key="i"
                  class="ai-spark-bar"
                  :style="{ height: h + '%' }"
                />
              </div>
            </div>
            <div class="ai-kpi-sub">
              活跃报警 <b>{{ healthSummary.alarms }}</b>
              · 平均延迟 <b>{{ healthSummary.avgLatency }} ms</b>
            </div>
          </section>

          <section class="ai-card">
            <div class="ai-card-head">
              <span>设备健康</span>
              <button type="button" class="ai-link" @click="goPath('/admin/config/vehicle-hub')">更多</button>
            </div>
            <div class="ai-health">
              <div class="ai-ring" :style="{ '--p': healthSummary.score + '%' }">
                <span class="ai-ring-num">{{ healthSummary.score }}</span>
              </div>
              <ul class="ai-health-legend">
                <li><i class="dot ok" />正常 <b>{{ healthSummary.ok }}</b></li>
                <li><i class="dot warn" />告警 <b>{{ healthSummary.warn }}</b></li>
                <li><i class="dot bad" />离线 <b>{{ healthSummary.offline }}</b></li>
              </ul>
            </div>
          </section>

          <section class="ai-card">
            <div class="ai-card-head">
              <span>分析模板</span>
            </div>
            <ul class="ai-kb-list">
              <li v-for="k in knowledgeTips" :key="k.id">
                <button type="button" class="ai-kb-item" @click="useExample(k.prompt)">
                  <span class="ai-kb-title">{{ k.title }}</span>
                  <span class="ai-kb-meta">
                    <em>{{ k.tag }}</em>
                    <span>{{ k.kind }}</span>
                  </span>
                </button>
              </li>
            </ul>
          </section>

          <section class="ai-card">
            <div class="ai-card-head">
              <span>最近报警</span>
              <button type="button" class="ai-link" @click="goPath('/admin/alarms')">更多</button>
            </div>
            <ul v-if="recentAlarms.length" class="ai-wo-list">
              <li v-for="a in recentAlarms" :key="a.id" class="ai-wo-item">
                <div class="ai-wo-main">
                  <div class="ai-wo-id">{{ a.title }}</div>
                  <div class="ai-wo-meta">{{ a.meta }}</div>
                </div>
                <span class="ai-wo-badge" :class="a.level">{{ a.badge }}</span>
              </li>
            </ul>
            <div v-else class="ai-side-empty">暂无报警记录</div>
          </section>
        </div>
      </aside>
    </div>
  </section>
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import {
  Plus,
  Close,
  Delete,
  Loading,
  Select,
  WarningFilled,
  ChatLineSquare,
  Grid,
  DocumentCopy,
  Promotion
} from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { renderMarkdown } from '@/utils/miniMarkdown'
import { useAssistantChat } from '@/composables/useAssistantChat'
import { fetchFleetHealth } from '@/api/fleetHealth'
import { fetchAlarmFeed } from '@/api/alarm'

const OPEN_KEY = 'assistant.drawer.open'
const WIDTH_KEY = 'assistant.drawer.width'
const SIDE_KEY = 'assistant.drawer.side'
const FAB_POS_KEY = 'assistant.fab.pos'
const MIN_W = 380
const MAX_W = 1100
const SIDE_W = 300
const FAB_SIZE = 52

const router = useRouter()
const { messages, sessions, currentSessionId, isStreaming, ensureInit, loadSession, newSession, removeSession, send, stop } =
  useAssistantChat()

const open = ref(localStorage.getItem(OPEN_KEY) === '1')
const chatWidth = ref(clampChatWidth(Number(localStorage.getItem(WIDTH_KEY)) || 440))
const showSide = ref(localStorage.getItem(SIDE_KEY) !== '0')
const draft = ref('')
const showSessions = ref(false)
const listRef = ref<HTMLElement | null>(null)

const fabPos = reactive(loadFabPos())
const fabDragging = ref(false)
let fabPointerId: number | null = null
let fabMoved = false
let fabStart = { x: 0, y: 0, left: 0, top: 0 }

const healthSummary = reactive({
  total: 0,
  reachable: 0,
  alarms: 0,
  avgLatency: 0,
  score: 0,
  ok: 0,
  warn: 0,
  offline: 0,
  bars: [36, 48, 42, 58, 52, 64, 60, 72] as number[]
})

interface RecentAlarmRow {
  id: string
  title: string
  meta: string
  badge: string
  level: 'ok' | 'warn' | 'bad'
}

const recentAlarms = ref<RecentAlarmRow[]>([])

const examples = [
  '现在有哪些车辆在报警？帮我分析原因和处理建议',
  '汇总最近的诊断日志，有哪些异常？',
  '分析一下当前车队的健康状况',
  '1 号车为什么停了？帮我排查'
]

const quickTools = [
  { id: 'alarms', label: '报警查询', glyph: '!', path: '/admin/alarms', prompt: '列出当前活跃报警并给出处理优先级' },
  { id: 'monitor', label: '地图监控', glyph: 'M', path: '/admin/map-monitor', prompt: null as string | null },
  { id: 'health', label: '车队健康', glyph: '+', path: '/admin/config/vehicle-hub', prompt: '分析当前车队健康状况与风险车辆' },
  { id: 'tasks', label: '任务管理', glyph: 'T', path: '/admin/tasks', prompt: null as string | null }
]

const knowledgeTips = [
  {
    id: 'k1',
    title: '报警分级与升级处理指南',
    tag: '最佳实践',
    kind: '指南',
    prompt: '按严重程度说明当前报警该如何分级处理和升级'
  },
  {
    id: 'k2',
    title: '车辆离线排查清单',
    tag: '排障',
    kind: '清单',
    prompt: '按清单帮我排查离线车辆：网络、WatchDog、车载服务'
  },
  {
    id: 'k3',
    title: '车队能耗与负载观察要点',
    tag: '运维',
    kind: '要点',
    prompt: '结合车队健康数据，给出本周运维观察重点'
  }
]

const drawerWidth = computed(() => chatWidth.value + (showSide.value ? SIDE_W : 0))

const fabStyle = computed(() => ({
  left: `${fabPos.left}px`,
  top: `${fabPos.top}px`,
  right: 'auto',
  bottom: 'auto'
}))

watch(showSide, (v) => {
  try { localStorage.setItem(SIDE_KEY, v ? '1' : '0') } catch { /* ignore */ }
})

function clampChatWidth(w: number): number {
  return Math.min(MAX_W - SIDE_W, Math.max(MIN_W, w))
}

function loadFabPos(): { left: number; top: number } {
  try {
    const raw = localStorage.getItem(FAB_POS_KEY)
    if (raw) {
      const p = JSON.parse(raw) as { left?: number; top?: number }
      if (typeof p.left === 'number' && typeof p.top === 'number') {
        return clampFab(p.left, p.top)
      }
    }
  } catch { /* ignore */ }
  const left = Math.max(16, window.innerWidth - FAB_SIZE - 22)
  const top = Math.max(16, window.innerHeight - FAB_SIZE - 28)
  return { left, top }
}

function clampFab(left: number, top: number): { left: number; top: number } {
  const maxL = Math.max(8, window.innerWidth - FAB_SIZE - 8)
  const maxT = Math.max(8, window.innerHeight - FAB_SIZE - 8)
  return {
    left: Math.min(maxL, Math.max(8, left)),
    top: Math.min(maxT, Math.max(8, top))
  }
}

function persistFab(): void {
  try {
    localStorage.setItem(FAB_POS_KEY, JSON.stringify({ left: fabPos.left, top: fabPos.top }))
  } catch { /* ignore */ }
}

function onFabPointerDown(e: PointerEvent): void {
  if (e.button !== 0) return
  fabPointerId = e.pointerId
  fabMoved = false
  fabDragging.value = true
  fabStart = { x: e.clientX, y: e.clientY, left: fabPos.left, top: fabPos.top }
  ;(e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId)
  e.preventDefault()
}

function onFabPointerMove(e: PointerEvent): void {
  if (fabPointerId !== e.pointerId || !fabDragging.value) return
  const dx = e.clientX - fabStart.x
  const dy = e.clientY - fabStart.y
  if (Math.abs(dx) > 4 || Math.abs(dy) > 4) fabMoved = true
  const next = clampFab(fabStart.left + dx, fabStart.top + dy)
  fabPos.left = next.left
  fabPos.top = next.top
}

function onFabPointerUp(e: PointerEvent): void {
  if (fabPointerId !== e.pointerId) return
  fabDragging.value = false
  fabPointerId = null
  ;(e.currentTarget as HTMLElement).releasePointerCapture?.(e.pointerId)
  persistFab()
  if (!fabMoved) toggle(true)
}

function onViewportResize(): void {
  const next = clampFab(fabPos.left, fabPos.top)
  fabPos.left = next.left
  fabPos.top = next.top
}

function toggle(v: boolean): void {
  open.value = v
  try { localStorage.setItem(OPEN_KEY, v ? '1' : '0') } catch { /* ignore */ }
  if (v) {
    void ensureInit()
    void refreshSideData()
  }
}

function toggleSessions(): void {
  showSessions.value = !showSessions.value
}

function onNew(): void {
  newSession()
  showSessions.value = false
  draft.value = ''
}

async function onPickSession(id: string): Promise<void> {
  showSessions.value = false
  if (id === currentSessionId.value) return
  await loadSession(id)
  void scrollToBottom()
}

async function onDeleteSession(id: string): Promise<void> {
  try {
    await ElMessageBox.confirm('确定删除该会话？', '删除会话', {
      type: 'warning',
      confirmButtonText: '删除',
      cancelButtonText: '取消'
    })
  } catch {
    return
  }
  await removeSession(id)
}

function useExample(ex: string): void {
  draft.value = ex
}

function goPath(path: string): void {
  void router.push(path)
}

function onQuickTool(t: (typeof quickTools)[number]): void {
  if (t.prompt) useExample(t.prompt)
  else goPath(t.path)
}

function onKeydown(e: Event | KeyboardEvent): void {
  if (!(e instanceof KeyboardEvent)) return
  if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) {
    e.preventDefault()
    void onSend()
  }
}

async function onSend(): Promise<void> {
  const text = draft.value.trim()
  if (!text || isStreaming.value) return
  draft.value = ''
  void scrollToBottom()
  await send(text)
}

function onStop(): void {
  stop()
}

async function copyText(text: string): Promise<void> {
  try {
    await navigator.clipboard.writeText(text)
    ElMessage.success('已复制')
  } catch {
    ElMessage.warning('复制失败')
  }
}

function toJson(v: unknown): string {
  try {
    const s = JSON.stringify(v, null, 2)
    return s.length > 4000 ? s.slice(0, 4000) + '\n...(已截断)' : s
  } catch {
    return String(v)
  }
}

function render(s: string): string {
  return renderMarkdown(s)
}

function formatTime(iso: string): string {
  const d = new Date(iso)
  if (Number.isNaN(d.getTime())) return ''
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}

async function scrollToBottom(): Promise<void> {
  await nextTick()
  const el = listRef.value
  if (el) el.scrollTop = el.scrollHeight
}

async function refreshSideData(): Promise<void> {
  try {
    const rows = await fetchFleetHealth()
    const total = rows.length
    const reachable = rows.filter((r) => r.reachable).length
    const alarms = rows.filter((r) => r.isAlarmActive).length
    const offline = total - reachable
    const warn = Math.max(0, alarms)
    const ok = Math.max(0, reachable - warn)
    const avgLatency = total
      ? Math.round(rows.reduce((s, r) => s + (r.latencyMs || 0), 0) / total)
      : 0
    const score = total ? Math.round((ok / total) * 100) : 0
    healthSummary.total = total
    healthSummary.reachable = reachable
    healthSummary.alarms = alarms
    healthSummary.avgLatency = avgLatency
    healthSummary.score = score
    healthSummary.ok = ok
    healthSummary.warn = warn
    healthSummary.offline = offline
    if (total) {
      healthSummary.bars = rows.slice(0, 8).map((r) => {
        const base = r.reachable ? 55 : 18
        const bump = Math.min(40, Math.round((100 - Math.min(r.latencyMs || 0, 2000) / 50)))
        return Math.min(92, Math.max(14, base + bump / 4))
      })
      while (healthSummary.bars.length < 8) healthSummary.bars.push(30)
    }
  } catch {
    /* 侧栏失败不阻塞对话 */
  }

  try {
    const feed = await fetchAlarmFeed(8)
    recentAlarms.value = (feed.alarms ?? []).slice(0, 5).map((a) => {
      const level: RecentAlarmRow['level'] =
        a.level >= 3 ? 'bad' : a.status === 'active' ? 'warn' : 'ok'
      return {
        id: a.id,
        title: `${a.carName || `车${a.carId}`} · ${String(a.info || '报警').slice(0, 28)}`,
        meta: a.lastAt ? formatTime(a.lastAt) : (a.firstAt ? formatTime(a.firstAt) : '最近'),
        badge: a.status === 'active' ? (level === 'bad' ? '严重' : '处理中') : '已清除',
        level
      }
    })
  } catch {
    recentAlarms.value = []
  }
}

watch(messages, () => { void scrollToBottom() }, { deep: true })

let resizing = false
let startX = 0
let startW = 0
function onResizeStart(e: PointerEvent): void {
  resizing = true
  startX = e.clientX
  startW = chatWidth.value
  ;(e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId)
  e.preventDefault()
}
function onResizeMove(e: PointerEvent): void {
  if (!resizing) return
  chatWidth.value = clampChatWidth(startW + (startX - e.clientX))
}
function onResizeEnd(e: PointerEvent): void {
  if (!resizing) return
  resizing = false
  ;(e.currentTarget as HTMLElement).releasePointerCapture?.(e.pointerId)
  try { localStorage.setItem(WIDTH_KEY, String(chatWidth.value)) } catch { /* ignore */ }
}

onMounted(() => {
  window.addEventListener('resize', onViewportResize)
  if (open.value) {
    void ensureInit()
    void refreshSideData()
  }
})

onUnmounted(() => {
  window.removeEventListener('resize', onViewportResize)
})
</script>

<style scoped>
.ai-fab {
  position: fixed;
  z-index: 1800;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: none;
  background: var(--ai-fab-bg);
  color: var(--ai-fab-fg);
  cursor: grab;
  box-shadow: var(--ai-fab-shadow), var(--ai-fab-glow);
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: none;
  user-select: none;
  transition: transform 0.18s ease, box-shadow 0.18s ease, filter 0.15s ease;
}
.ai-fab:hover { filter: brightness(1.06); transform: scale(1.04); }
.ai-fab.is-dragging {
  cursor: grabbing;
  transform: scale(1.08);
  box-shadow: 0 12px 28px color-mix(in srgb, var(--mg-primary) 36%, transparent);
}
.ai-fab-glyph { font-size: 22px; line-height: 1; pointer-events: none; }

.ai-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 1900;
  display: flex;
  flex-direction: column;
  max-width: 96vw;
  background: var(--ai-panel-bg);
  border-left: 1px solid var(--ai-panel-border);
  box-shadow: var(--ai-panel-shadow);
  color: var(--ai-bot-fg);
  transform: translateX(100%);
  opacity: 0;
  visibility: hidden;
  transition: transform 0.28s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.28s ease, visibility 0.28s, width 0.2s ease;
}
.ai-drawer.is-open {
  transform: translateX(0);
  opacity: 1;
  visibility: visible;
}

.ai-resizer {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 6px;
  cursor: ew-resize;
  z-index: 5;
  touch-action: none;
}
.ai-resizer:hover { background: var(--ai-accent-soft); }

.ai-shell {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: row;
}

.ai-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.ai-side {
  flex: none;
  width: 300px;
  border-left: 1px solid var(--ai-panel-border);
  background: var(--ai-side-bg);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.ai-side-head {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 14px 10px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ai-title);
  border-bottom: 1px solid var(--ai-head-border);
}
.ai-side-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  scrollbar-width: thin;
  scrollbar-color: var(--ai-scrollbar) transparent;
}

.ai-card {
  border: 1px solid var(--ai-bot-border);
  border-radius: 12px;
  background: var(--ai-bot-bg);
  padding: 12px;
}
.ai-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
  font-size: 12px;
  font-weight: 700;
  color: var(--ai-title);
}

.ai-tool-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.ai-tool-tile {
  appearance: none;
  border: 1px solid var(--ai-example-border);
  background: var(--ai-example-bg);
  color: var(--ai-example-fg);
  border-radius: 10px;
  padding: 12px 8px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.ai-tool-tile:hover {
  border-color: var(--ai-accent);
  transform: translateY(-1px);
}
.ai-tool-tile-ic { font-size: 18px; line-height: 1; }
.ai-tool-tile-lb { font-size: 11.5px; font-weight: 600; }

.ai-kpi { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ai-kpi-label { font-size: 11px; color: var(--ai-muted); }
.ai-kpi-value {
  font-size: 26px;
  font-weight: 750;
  color: var(--ai-title);
  line-height: 1.1;
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}
.ai-kpi-unit { font-size: 13px; font-weight: 600; color: var(--ai-muted); }
.ai-kpi-sub {
  margin-top: 8px;
  font-size: 11.5px;
  color: var(--ai-muted);
}
.ai-kpi-sub b { color: var(--ai-title); font-weight: 700; }

.ai-spark {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 36px;
  padding-bottom: 2px;
}
.ai-spark-bar {
  width: 5px;
  border-radius: 2px 2px 0 0;
  background: linear-gradient(180deg, var(--ai-accent), color-mix(in srgb, var(--ai-accent) 40%, transparent));
  opacity: 0.85;
}

.ai-health { display: flex; align-items: center; gap: 14px; }
.ai-ring {
  --p: 80%;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at center, var(--ai-bot-bg) 58%, transparent 59%),
    conic-gradient(var(--ai-accent) var(--p), color-mix(in srgb, var(--ai-muted) 28%, transparent) 0);
}
.ai-ring-num {
  font-size: 20px;
  font-weight: 750;
  color: var(--ai-title);
  font-variant-numeric: tabular-nums;
}
.ai-health-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12px;
  color: var(--ai-muted);
}
.ai-health-legend b { color: var(--ai-title); margin-left: 4px; }
.ai-health-legend .dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: 6px;
}
.ai-health-legend .dot.ok { background: #10b981; }
.ai-health-legend .dot.warn { background: #f59e0b; }
.ai-health-legend .dot.bad { background: #ef4444; }

.ai-kb-list, .ai-wo-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ai-kb-item {
  appearance: none;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  border-radius: 8px;
  padding: 8px;
  cursor: pointer;
  transition: background 0.15s ease;
}
.ai-kb-item:hover { background: var(--ai-accent-soft); }
.ai-kb-title {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ai-title);
  line-height: 1.35;
}
.ai-kb-meta {
  display: flex;
  gap: 8px;
  margin-top: 4px;
  font-size: 10.5px;
  color: var(--ai-muted);
}
.ai-kb-meta em {
  font-style: normal;
  color: var(--ai-accent);
  font-weight: 650;
}

.ai-wo-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px;
  border-radius: 8px;
  background: var(--ai-thinking-bg);
}
.ai-wo-main { min-width: 0; flex: 1; }
.ai-wo-id {
  font-size: 12px;
  font-weight: 600;
  color: var(--ai-title);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ai-wo-meta { font-size: 11px; color: var(--ai-muted); margin-top: 2px; }
.ai-wo-badge {
  flex: none;
  font-size: 10px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 999px;
}
.ai-wo-badge.warn {
  background: rgba(245, 158, 11, 0.14);
  color: #d97706;
  border: 1px solid rgba(245, 158, 11, 0.28);
}
.ai-wo-badge.bad {
  background: rgba(239, 68, 68, 0.12);
  color: #dc2626;
  border: 1px solid rgba(239, 68, 68, 0.28);
}
.ai-wo-badge.ok {
  background: rgba(16, 185, 129, 0.12);
  color: #059669;
  border: 1px solid rgba(16, 185, 129, 0.28);
}
.ai-side-empty {
  font-size: 12px;
  color: var(--ai-muted);
  text-align: center;
  padding: 12px 0;
}

.ai-head {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 14px 16px;
  background: var(--ai-head-bg);
  border-bottom: 1px solid var(--ai-head-border);
}
.ai-head-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
.ai-head-glyph {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  flex: none;
  color: var(--ai-avatar-bot-fg);
  background: var(--ai-avatar-bot-bg);
  border: 1px solid var(--ai-avatar-bot-border);
}
.ai-head-text { min-width: 0; }
.ai-head-main { font-size: 15px; font-weight: 700; color: var(--ai-title); line-height: 1.2; }
.ai-head-sub { font-size: 11px; color: var(--ai-muted); margin-top: 2px; white-space: nowrap; }
.ai-head-actions { display: flex; align-items: center; gap: 6px; flex: none; }

.ai-textbtn {
  appearance: none;
  border: 1px solid var(--ai-accent-soft-border);
  background: transparent;
  color: var(--ai-accent);
  height: 30px;
  padding: 0 10px;
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 650;
  transition: background 0.15s ease;
}
.ai-textbtn:hover { background: var(--ai-accent-soft); }

.ai-iconbtn {
  appearance: none;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ai-muted);
  width: 30px;
  height: 30px;
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.ai-iconbtn:hover,
.ai-iconbtn.active {
  background: var(--ai-close-hover-bg);
  color: var(--ai-close-hover-fg);
  border-color: var(--ai-accent-soft-border);
}

.ai-sessions {
  position: absolute;
  top: 60px;
  right: 12px;
  width: 280px;
  max-height: 360px;
  overflow: auto;
  background: var(--ai-panel-bg);
  border: 1px solid var(--ai-panel-border);
  border-radius: 12px;
  box-shadow: 0 16px 36px rgba(15, 23, 42, 0.12);
  z-index: 20;
  padding: 8px;
  color: var(--ai-bot-fg);
}
.ai-sessions-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  color: var(--ai-muted);
  padding: 4px 6px 8px;
}
.ai-link {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--ai-accent);
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
}
.ai-sessions-empty { padding: 16px; text-align: center; color: var(--ai-muted); font-size: 12px; }
.ai-sessions-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.ai-session-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border-radius: 8px;
  cursor: pointer;
}
.ai-session-item:hover,
.ai-session-item.active { background: var(--ai-accent-soft); }
.ai-session-main { min-width: 0; flex: 1; }
.ai-session-title {
  font-size: 13px;
  color: var(--ai-title);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ai-session-meta { font-size: 11px; color: var(--ai-muted); margin-top: 2px; }
.ai-session-del {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--ai-muted);
  cursor: pointer;
  width: 24px;
  height: 24px;
  border-radius: 6px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ai-session-del:hover { background: rgba(239, 68, 68, 0.12); color: #ef4444; }

.ai-body {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  scrollbar-width: thin;
  scrollbar-color: var(--ai-scrollbar) transparent;
}
.ai-body::-webkit-scrollbar { width: 6px; }
.ai-body::-webkit-scrollbar-thumb { background: var(--ai-scrollbar); border-radius: 3px; }

.ai-empty {
  text-align: center;
  padding: 28px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.ai-empty-glyph {
  width: 48px;
  height: 48px;
  border-radius: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: var(--ai-avatar-bot-fg);
  background: var(--ai-avatar-bot-bg);
  border: 1px solid var(--ai-avatar-bot-border);
  margin-bottom: 4px;
}
.ai-empty-title { font-size: 15px; font-weight: 650; color: var(--ai-title); }
.ai-empty-tip { font-size: 12px; color: var(--ai-muted); margin: 2px 0 4px; }
.ai-example {
  appearance: none;
  width: 100%;
  text-align: left;
  border: 1px solid var(--ai-example-border);
  background: var(--ai-example-bg);
  color: var(--ai-example-fg);
  border-radius: 10px;
  padding: 10px 12px;
  font-size: 12.5px;
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.ai-example:hover {
  border-color: var(--ai-accent);
  transform: translateY(-1px);
}

.ai-msg { display: flex; align-items: flex-end; gap: 8px; }
.ai-msg--user { justify-content: flex-end; }
.ai-msg--assistant { justify-content: flex-start; }
.ai-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
}
.ai-avatar--bot {
  color: var(--ai-avatar-bot-fg);
  background: var(--ai-avatar-bot-bg);
  border: 1px solid var(--ai-avatar-bot-border);
}
.ai-avatar--user {
  color: var(--ai-avatar-user-fg);
  background: var(--ai-avatar-user-bg);
}
.ai-bubble {
  max-width: min(90%, 520px);
  padding: 10px 13px;
  border-radius: 14px;
  font-size: 13.5px;
  line-height: 1.6;
}
.ai-msg--user .ai-bubble {
  background: var(--ai-user-bg);
  color: var(--ai-user-fg);
  border-bottom-right-radius: 4px;
}
.ai-msg--assistant .ai-bubble {
  background: var(--ai-bot-bg);
  border: 1px solid var(--ai-bot-border);
  border-left: 3px solid var(--ai-accent);
  color: var(--ai-bot-fg);
  border-bottom-left-radius: 4px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.ai-user-text { white-space: pre-wrap; word-break: break-word; }
.ai-msg-actions {
  display: flex;
  gap: 4px;
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid var(--ai-bot-border);
}
.ai-act {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--ai-muted);
  width: 26px;
  height: 26px;
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ai-act:hover { background: var(--ai-accent-soft); color: var(--ai-accent); }

.ai-thinking {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 2px;
  color: var(--ai-thinking-fg);
  font-size: 13px;
}
.ai-caret {
  display: inline-block;
  width: 7px;
  height: 15px;
  margin-left: 2px;
  vertical-align: text-bottom;
  background: var(--ai-dot);
  animation: ai-blink 1s steps(2) infinite;
}
@keyframes ai-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }

.ai-error {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-top: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  background: rgba(239, 68, 68, 0.08);
  border: 1px solid rgba(239, 68, 68, 0.28);
  color: #dc2626;
  font-size: 12.5px;
}

.ai-tools { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.ai-tool {
  border: 1px solid var(--ai-bot-border);
  border-radius: 9px;
  background: var(--ai-thinking-bg);
  overflow: hidden;
}
.ai-tool.is-running { border-color: var(--ai-accent-soft-border); }
.ai-tool.is-ok { border-color: rgba(16, 185, 129, 0.4); }
.ai-tool.is-error { border-color: rgba(239, 68, 68, 0.45); }
.ai-tool-sum {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  cursor: pointer;
  list-style: none;
  font-size: 12.5px;
}
.ai-tool-sum::-webkit-details-marker { display: none; }
.ai-tool-ic { display: inline-flex; align-items: center; color: var(--ai-muted); }
.ai-tool.is-ok .ai-tool-ic { color: #10b981; }
.ai-tool.is-error .ai-tool-ic { color: #ef4444; }
.ai-tool-name {
  font-family: var(--mg-font-mono, monospace);
  color: var(--ai-title);
  font-weight: 600;
  flex: 1;
  min-width: 0;
}
.ai-tool-badge {
  flex: none;
  font-size: 10px;
  padding: 1px 7px;
  border-radius: 999px;
  font-weight: 600;
}
.ai-tool-badge.read {
  background: rgba(59, 130, 246, 0.12);
  color: #2563eb;
  border: 1px solid rgba(59, 130, 246, 0.28);
}
.ai-tool-badge.write {
  background: rgba(245, 158, 11, 0.12);
  color: #d97706;
  border: 1px solid rgba(245, 158, 11, 0.3);
}
.ai-tool-body { padding: 0 10px 8px; }
.ai-tool-kv { margin-top: 6px; }
.ai-tool-k { font-size: 11px; color: var(--ai-muted); }
.ai-tool-pre {
  margin: 3px 0 0;
  padding: 7px 9px;
  background: var(--ai-md-pre-bg);
  border: 1px solid var(--ai-md-pre-border);
  border-radius: 7px;
  font-size: 11.5px;
  font-family: var(--mg-font-mono, monospace);
  color: var(--ai-bot-fg);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 220px;
  overflow: auto;
}

.ai-md :deep(p) { margin: 0 0 8px; }
.ai-md :deep(p:last-child) { margin-bottom: 0; }
.ai-md :deep(.mmd-h) { margin: 10px 0 6px; font-weight: 700; color: var(--ai-title); line-height: 1.3; }
.ai-md :deep(h3.mmd-h) { font-size: 15px; }
.ai-md :deep(h4.mmd-h) { font-size: 14px; }
.ai-md :deep(h5.mmd-h) { font-size: 13px; }
.ai-md :deep(.mmd-ul), .ai-md :deep(.mmd-ol) { margin: 4px 0 8px; padding-left: 20px; }
.ai-md :deep(li) { margin: 2px 0; }
.ai-md :deep(.mmd-code) {
  font-family: var(--mg-font-mono, monospace);
  font-size: 12px;
  background: var(--ai-md-code-bg);
  color: var(--ai-md-code-fg);
  border: 1px solid var(--ai-example-border);
  border-radius: 5px;
  padding: 1px 5px;
}
.ai-md :deep(.mmd-pre) {
  margin: 6px 0;
  padding: 10px 12px;
  background: var(--ai-md-pre-bg);
  border: 1px solid var(--ai-md-pre-border);
  border-radius: 9px;
  overflow: auto;
  max-height: 320px;
}
.ai-md :deep(.mmd-pre code) {
  font-family: var(--mg-font-mono, monospace);
  font-size: 12px;
  color: var(--ai-bot-fg);
  white-space: pre;
}
.ai-md :deep(.mmd-link) { color: var(--ai-md-link); text-decoration: underline; }
.ai-md :deep(.mmd-quote) {
  margin: 6px 0;
  padding: 4px 12px;
  border-left: 3px solid var(--ai-md-blockquote-border);
  color: var(--ai-md-blockquote-fg);
}
.ai-md :deep(strong) { color: var(--ai-title); }

.ai-foot {
  flex: none;
  padding: 12px 14px 14px;
  border-top: 1px solid var(--ai-composer-border);
  background: var(--ai-composer-bg);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ai-foot :deep(.el-textarea__inner) {
  background: var(--ai-input-bg) !important;
  border: 1px solid var(--ai-input-border) !important;
  color: var(--ai-input-fg) !important;
  box-shadow: none !important;
  border-radius: 10px;
}
.ai-foot :deep(.el-textarea__inner::placeholder) {
  color: var(--ai-input-placeholder) !important;
}
.ai-foot :deep(.el-textarea__inner:focus) {
  border-color: var(--ai-accent) !important;
}
.ai-foot-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.ai-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
}
.ai-chip {
  appearance: none;
  border: 1px solid var(--ai-example-border);
  background: var(--ai-example-bg);
  color: var(--ai-example-fg);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.ai-chip:hover { border-color: var(--ai-accent); }
.ai-foot-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.ai-send {
  appearance: none;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: var(--ai-send-bg);
  color: var(--ai-send-fg);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  transition: filter 0.15s ease, transform 0.15s ease;
}
.ai-send:hover:not(:disabled) {
  filter: brightness(1.06);
  transform: scale(1.04);
}
.ai-send:disabled {
  background: var(--ai-send-disabled);
  cursor: not-allowed;
  opacity: 0.75;
}

@media (max-width: 900px) {
  .ai-side { display: none !important; }
  .ai-drawer { width: min(100vw, 480px) !important; }
}
@media (max-width: 560px) {
  .ai-drawer { width: 100vw !important; }
  .ai-textbtn span { display: none; }
}
</style>
