<script setup lang="ts">
import { ref, computed, onMounted, watch, onBeforeUnmount } from 'vue'
import Card from '@/components/ui/card.vue'
import Button from '@/components/ui/button.vue'
import Badge from '@/components/ui/badge.vue'
import Table from '@/components/ui/table.vue'
import TableBody from '@/components/ui/table-body.vue'
import TableCell from '@/components/ui/table-cell.vue'
import TableHead from '@/components/ui/table-head.vue'
import TableHeader from '@/components/ui/table-header.vue'
import TableRow from '@/components/ui/table-row.vue'
import Input from '@/components/ui/input.vue'
import Pagination from '@/components/ui/pagination.vue'
import RefreshButton from '@/components/ui/refresh-button.vue'
import Select from '@/components/ui/select.vue'
import SelectTrigger from '@/components/ui/select-trigger.vue'
import SelectContent from '@/components/ui/select-content.vue'
import SelectItem from '@/components/ui/select-item.vue'
import SelectValue from '@/components/ui/select-value.vue'
import ScatterChart from '@/components/charts/ScatterChart.vue'
import { Trash2, Eraser, Search, X, BarChart3, ChevronDown, ChevronRight, Database, ArrowRight } from 'lucide-vue-next'
import { useToast } from '@/composables/useToast'
import { useConfirm } from '@/composables/useConfirm'
import { cacheApi, modelMappingCacheApi, type CacheStats, type CacheConfig, type UserAffinity, type ModelMappingCacheStats } from '@/api/cache'
import type { TTLAnalysisUser } from '@/api/cache'
import { formatNumber, formatTokens, formatCost, formatRemainingTime } from '@/utils/format'
import {
  useTTLAnalysis,
  ANALYSIS_HOURS_OPTIONS,
  getTTLBadgeVariant,
  getFrequencyLabel,
  getFrequencyClass
} from '@/composables/useTTLAnalysis'
import { log } from '@/utils/logger'

// ==================== 缓存统计与亲和性列表 ====================

const stats = ref<CacheStats | null>(null)
const config = ref<CacheConfig | null>(null)
const loading = ref(false)
const affinityList = ref<UserAffinity[]>([])
const listLoading = ref(false)
const tableKeyword = ref('')
const matchedUserId = ref<string | null>(null)
const clearingRowAffinityKey = ref<string | null>(null)
const currentPage = ref(1)
const pageSize = ref(20)
const currentTime = ref(Math.floor(Date.now() / 1000))
const analysisHoursSelectOpen = ref(false)

// ==================== 模型映射缓存 ====================

const modelMappingStats = ref<ModelMappingCacheStats | null>(null)
const modelMappingLoading = ref(false)
const clearingModelMapping = ref(false)
const clearingModelName = ref<string | null>(null)

const { success: showSuccess, error: showError, info: showInfo } = useToast()
const { confirm: showConfirm } = useConfirm()

let searchDebounceTimer: ReturnType<typeof setTimeout> | null = null
let skipNextKeywordWatch = false
let countdownTimer: ReturnType<typeof setInterval> | null = null

// ==================== TTL 分析 (使用 composable) ====================

const {
  ttlAnalysis,
  hitAnalysis,
  ttlAnalysisLoading,
  analysisHours,
  expandedUserId,
  userTimelineData,
  userTimelineLoading,
  userTimelineChartData,
  toggleUserExpand,
  refreshAnalysis
} = useTTLAnalysis()

// ==================== 计算属性 ====================

const paginatedAffinityList = computed(() => {
  const start = (currentPage.value - 1) * pageSize.value
  const end = start + pageSize.value
  return affinityList.value.slice(start, end)
})

// ==================== 缓存统计方法 ====================

async function fetchCacheStats() {
  loading.value = true
  try {
    stats.value = await cacheApi.getStats()
  } catch (error) {
    showError('获取缓存统计失败')
    log.error('获取缓存统计失败', error)
  } finally {
    loading.value = false
  }
}

async function fetchCacheConfig() {
  try {
    config.value = await cacheApi.getConfig()
  } catch (error) {
    log.error('获取缓存配置失败', error)
  }
}

async function fetchAffinityList(keyword?: string) {
  listLoading.value = true
  try {
    const response = await cacheApi.listAffinities(keyword)
    affinityList.value = response.items
    matchedUserId.value = response.matched_user_id ?? null

    if (keyword && response.total === 0) {
      showInfo('未找到匹配的缓存记录')
    }
  } catch (error) {
    showError('获取缓存列表失败')
    log.error('获取缓存列表失败', error)
  } finally {
    listLoading.value = false
  }
}

async function resetAffinitySearch() {
  if (searchDebounceTimer) {
    clearTimeout(searchDebounceTimer)
    searchDebounceTimer = null
  }

  if (!tableKeyword.value) {
    currentPage.value = 1
    await fetchAffinityList()
    return
  }

  skipNextKeywordWatch = true
  tableKeyword.value = ''
  currentPage.value = 1
  await fetchAffinityList()
}

async function clearSingleAffinity(item: UserAffinity) {
  const affinityKey = item.affinity_key?.trim()
  const endpointId = item.endpoint_id?.trim()
  const modelId = item.global_model_id?.trim()
  const apiFormat = item.api_format?.trim()

  if (!affinityKey || !endpointId || !modelId || !apiFormat) {
    showError('缓存记录信息不完整，无法删除')
    return
  }

  const label = item.user_api_key_name || affinityKey
  const modelLabel = item.model_display_name || item.model_name || modelId
  const confirmed = await showConfirm({
    title: '确认清除',
    message: `确定要清除 ${label} 在模型 ${modelLabel} 上的缓存亲和性吗？`,
    confirmText: '确认清除',
    variant: 'destructive'
  })

  if (!confirmed) return

  clearingRowAffinityKey.value = affinityKey
  try {
    await cacheApi.clearSingleAffinity(affinityKey, endpointId, modelId, apiFormat)
    showSuccess('清除成功')
    await fetchCacheStats()
    await fetchAffinityList(tableKeyword.value.trim() || undefined)
  } catch (error) {
    showError('清除失败')
    log.error('清除单条缓存失败', error)
  } finally {
    clearingRowAffinityKey.value = null
  }
}

async function clearAllCache() {
  const firstConfirm = await showConfirm({
    title: '危险操作',
    message: '警告：此操作会清除所有用户的缓存亲和性，确定继续吗？',
    confirmText: '继续',
    variant: 'destructive'
  })
  if (!firstConfirm) return

  const secondConfirm = await showConfirm({
    title: '再次确认',
    message: '这将影响所有用户，请再次确认！',
    confirmText: '确认清除',
    variant: 'destructive'
  })
  if (!secondConfirm) return

  try {
    await cacheApi.clearAllCache()
    showSuccess('已清除所有缓存')
    await fetchCacheStats()
    await fetchAffinityList(tableKeyword.value.trim() || undefined)
  } catch (error) {
    showError('清除失败')
    log.error('清除所有缓存失败', error)
  }
}

// ==================== 工具方法 ====================

function getRemainingTime(expireAt?: number): string {
  return formatRemainingTime(expireAt, currentTime.value)
}

function formatIntervalDescription(user: TTLAnalysisUser): string {
  const p90 = user.percentiles.p90
  if (p90 === null || p90 === undefined) return '-'
  if (p90 < 1) {
    const seconds = Math.round(p90 * 60)
    return `90% 请求间隔 < ${seconds} 秒`
  }
  return `90% 请求间隔 < ${p90.toFixed(1)} 分钟`
}

function handlePageChange() {
  window.scrollTo({ top: 0, behavior: 'smooth' })
}

// ==================== 定时器管理 ====================

function startCountdown() {
  if (countdownTimer) clearInterval(countdownTimer)

  countdownTimer = setInterval(() => {
    currentTime.value = Math.floor(Date.now() / 1000)

    const beforeCount = affinityList.value.length
    affinityList.value = affinityList.value.filter(
      item => item.expire_at && item.expire_at > currentTime.value
    )

    if (beforeCount > affinityList.value.length) {
      const removedCount = beforeCount - affinityList.value.length
      showInfo(`${removedCount} 个缓存已自动过期移除`)
    }
  }, 1000)
}

function stopCountdown() {
  if (countdownTimer) {
    clearInterval(countdownTimer)
    countdownTimer = null
  }
}

// ==================== 模型映射缓存方法 ====================

async function fetchModelMappingStats() {
  modelMappingLoading.value = true
  try {
    modelMappingStats.value = await modelMappingCacheApi.getStats()
  } catch (error) {
    showError('获取模型映射缓存统计失败')
    log.error('获取模型映射缓存统计失败', error)
  } finally {
    modelMappingLoading.value = false
  }
}

async function clearAllModelMappingCache() {
  const confirmed = await showConfirm({
    title: '确认清除',
    message: '确定要清除所有模型映射缓存吗？这会影响所有模型的名称解析。',
    confirmText: '确认清除',
    variant: 'destructive'
  })

  if (!confirmed) return

  clearingModelMapping.value = true
  try {
    const result = await modelMappingCacheApi.clearAll()
    showSuccess(`已清除 ${result.deleted_count} 个缓存键`)
    await fetchModelMappingStats()
  } catch (error) {
    showError('清除模型映射缓存失败')
    log.error('清除模型映射缓存失败', error)
  } finally {
    clearingModelMapping.value = false
  }
}

async function clearModelMappingByName(modelName: string) {
  clearingModelName.value = modelName
  try {
    await modelMappingCacheApi.clearByName(modelName)
    showSuccess(`已清除 ${modelName} 的映射缓存`)
    await fetchModelMappingStats()
  } catch (error) {
    showError('清除缓存失败')
    log.error('清除模型映射缓存失败', error)
  } finally {
    clearingModelName.value = null
  }
}

async function clearProviderModelMapping(providerId: string, globalModelId: string, displayName?: string) {
  const confirmed = await showConfirm({
    title: '确认清除',
    message: `确定要清除 ${displayName || 'Provider 模型映射'} 的缓存吗？`,
    confirmText: '确认清除',
    variant: 'destructive'
  })

  if (!confirmed) return

  try {
    await modelMappingCacheApi.clearProviderModel(providerId, globalModelId)
    showSuccess('已清除 Provider 模型映射缓存')
    await fetchModelMappingStats()
  } catch (error) {
    showError('清除缓存失败')
    log.error('清除 Provider 模型映射缓存失败', error)
  }
}

function formatTTL(ttl: number | null): string {
  if (ttl === null || ttl < 0) return '-'
  if (ttl < 60) return `${ttl}s`
  const minutes = Math.floor(ttl / 60)
  const seconds = ttl % 60
  if (seconds === 0) return `${minutes}m`
  return `${minutes}m${seconds}s`
}

function getUnmappedStatusBadge(status: string): { variant: 'default' | 'secondary' | 'destructive' | 'outline', text: string } {
  switch (status) {
    case 'not_found':
      return { variant: 'secondary', text: '未找到' }
    case 'invalid':
      return { variant: 'destructive', text: '无效' }
    case 'error':
      return { variant: 'destructive', text: '错误' }
    default:
      return { variant: 'outline', text: status }
  }
}

// ==================== 刷新所有数据 ====================

async function refreshData() {
  await Promise.all([
    fetchCacheStats(),
    fetchCacheConfig(),
    fetchAffinityList(),
    fetchModelMappingStats()
  ])
}

// ==================== 生命周期 ====================

watch(tableKeyword, (value) => {
  if (skipNextKeywordWatch) {
    skipNextKeywordWatch = false
    return
  }

  if (searchDebounceTimer) clearTimeout(searchDebounceTimer)

  const keyword = value.trim()
  searchDebounceTimer = setTimeout(() => {
    fetchAffinityList(keyword || undefined)
    searchDebounceTimer = null
  }, 600)
})

onMounted(() => {
  fetchCacheStats()
  fetchCacheConfig()
  fetchAffinityList()
  fetchModelMappingStats()
  startCountdown()
  refreshAnalysis()
})

onBeforeUnmount(() => {
  if (searchDebounceTimer) clearTimeout(searchDebounceTimer)
  stopCountdown()
})
</script>

<template>
  <div class="space-y-6">
    <!-- 标题 -->
    <div>
      <h2 class="text-2xl font-bold">
        缓存监控
      </h2>
      <p class="text-sm text-muted-foreground mt-1">
        管理缓存亲和性，提高 Prompt Caching 命中率
      </p>
    </div>

    <!-- 亲和性系统状态 -->
    <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
      <Card class="p-4">
        <div class="text-xs text-muted-foreground">
          活跃亲和性
        </div>
        <div class="text-2xl font-bold mt-1">
          {{ stats?.affinity_stats?.active_affinities || 0 }}
        </div>
        <div class="text-xs text-muted-foreground mt-1">
          TTL {{ config?.cache_ttl_seconds || 300 }}s
        </div>
      </Card>

      <Card class="p-4">
        <div class="text-xs text-muted-foreground">
          Provider 切换
        </div>
        <div
          class="text-2xl font-bold mt-1"
          :class="(stats?.affinity_stats?.provider_switches || 0) > 0 ? 'text-destructive' : ''"
        >
          {{ stats?.affinity_stats?.provider_switches || 0 }}
        </div>
        <div class="text-xs text-muted-foreground mt-1">
          Key 切换 {{ stats?.affinity_stats?.key_switches || 0 }}
        </div>
      </Card>

      <Card class="p-4">
        <div class="text-xs text-muted-foreground">
          缓存失效
        </div>
        <div
          class="text-2xl font-bold mt-1"
          :class="(stats?.affinity_stats?.cache_invalidations || 0) > 0 ? 'text-warning' : ''"
        >
          {{ stats?.affinity_stats?.cache_invalidations || 0 }}
        </div>
        <div class="text-xs text-muted-foreground mt-1">
          因 Provider 不可用
        </div>
      </Card>

      <Card class="p-4">
        <div class="text-xs text-muted-foreground flex items-center gap-1">
          预留比例
          <Badge
            v-if="config?.dynamic_reservation?.enabled"
            variant="outline"
            class="text-[10px] px-1"
          >
            动态
          </Badge>
        </div>
        <div class="text-2xl font-bold mt-1">
          <template v-if="config?.dynamic_reservation?.enabled">
            {{ (config.dynamic_reservation.config.stable_min_reservation * 100).toFixed(0) }}-{{ (config.dynamic_reservation.config.stable_max_reservation * 100).toFixed(0) }}%
          </template>
          <template v-else>
            {{ config ? (config.cache_reservation_ratio * 100).toFixed(0) : '30' }}%
          </template>
        </div>
        <div class="text-xs text-muted-foreground mt-1">
          当前 {{ stats ? (stats.cache_reservation_ratio * 100).toFixed(0) : '-' }}%
        </div>
      </Card>
    </div>

    <!-- 缓存亲和性列表 -->
    <Card class="overflow-hidden">
      <div class="px-4 sm:px-6 py-3 sm:py-3.5 border-b border-border/60">
        <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
          <h3 class="text-sm sm:text-base font-semibold shrink-0">
            亲和性列表
          </h3>
          <div class="flex flex-wrap items-center gap-2">
            <div class="relative">
              <Search class="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground z-10 pointer-events-none" />
              <Input
                id="cache-affinity-search"
                v-model="tableKeyword"
                placeholder="搜索用户或 Key"
                class="w-32 sm:w-48 h-8 text-sm pl-8 pr-8"
              />
              <button
                v-if="tableKeyword"
                type="button"
                class="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground z-10"
                @click="resetAffinitySearch"
              >
                <X class="h-3.5 w-3.5" />
              </button>
            </div>
            <div class="hidden sm:block h-4 w-px bg-border" />
            <Button
              variant="ghost"
              size="icon"
              class="h-8 w-8 text-muted-foreground/70 hover:text-destructive"
              title="清除全部缓存"
              @click="clearAllCache"
            >
              <Eraser class="h-4 w-4" />
            </Button>
            <RefreshButton
              :loading="loading || listLoading"
              @click="refreshData"
            />
          </div>
        </div>
      </div>

      <Table class="hidden xl:table">
        <TableHeader>
          <TableRow>
            <TableHead class="w-36">
              用户
            </TableHead>
            <TableHead class="w-28">
              Key
            </TableHead>
            <TableHead class="w-28">
              Provider
            </TableHead>
            <TableHead class="w-40">
              模型
            </TableHead>
            <TableHead class="w-36">
              API 格式 / Key
            </TableHead>
            <TableHead class="w-20 text-center">
              剩余
            </TableHead>
            <TableHead class="w-14 text-center">
              次数
            </TableHead>
            <TableHead class="w-12 text-right">
              操作
            </TableHead>
          </TableRow>
        </TableHeader>
        <TableBody v-if="!listLoading && affinityList.length">
          <TableRow
            v-for="item in paginatedAffinityList"
            :key="`${item.affinity_key}-${item.endpoint_id}-${item.key_id}`"
          >
            <TableCell>
              <div class="flex items-center gap-1.5">
                <Badge
                  v-if="item.is_standalone"
                  variant="outline"
                  class="text-warning border-warning/30 text-[10px] px-1"
                >
                  独立
                </Badge>
                <span
                  class="text-sm font-medium truncate max-w-[120px]"
                  :title="item.username ?? undefined"
                >{{ item.username || '未知' }}</span>
              </div>
            </TableCell>
            <TableCell>
              <div class="flex items-center gap-1.5">
                <span
                  class="text-sm truncate max-w-[80px]"
                  :title="item.user_api_key_name || undefined"
                >{{ item.user_api_key_name || '未命名' }}</span>
                <Badge
                  v-if="item.rate_multiplier !== 1.0"
                  variant="outline"
                  class="text-warning border-warning/30 text-[10px] px-2"
                >
                  {{ item.rate_multiplier }}x
                </Badge>
              </div>
              <div class="text-xs text-muted-foreground font-mono">
                {{ item.user_api_key_prefix || '---' }}
              </div>
            </TableCell>
            <TableCell>
              <div
                class="text-sm truncate max-w-[100px]"
                :title="item.provider_name || undefined"
              >
                {{ item.provider_name || '未知' }}
              </div>
            </TableCell>
            <TableCell>
              <div
                class="text-sm truncate max-w-[150px]"
                :title="item.model_display_name || undefined"
              >
                {{ item.model_display_name || '---' }}
              </div>
              <div
                class="text-xs text-muted-foreground"
                :title="item.model_name || undefined"
              >
                {{ item.model_name || '---' }}
              </div>
            </TableCell>
            <TableCell>
              <div class="text-sm">
                {{ item.endpoint_api_format || '---' }}
              </div>
              <div class="text-xs text-muted-foreground font-mono">
                {{ item.key_prefix || '---' }}
              </div>
            </TableCell>
            <TableCell class="text-center">
              <span class="text-xs">{{ getRemainingTime(item.expire_at) }}</span>
            </TableCell>
            <TableCell class="text-center">
              <span class="text-sm">{{ item.request_count }}</span>
            </TableCell>
            <TableCell class="text-right">
              <Button
                size="icon"
                variant="ghost"
                class="h-7 w-7 text-muted-foreground/70 hover:text-destructive"
                :disabled="clearingRowAffinityKey === item.affinity_key"
                title="清除缓存"
                @click="clearSingleAffinity(item)"
              >
                <Trash2 class="h-3.5 w-3.5" />
              </Button>
            </TableCell>
          </TableRow>
        </TableBody>
        <TableBody v-else>
          <TableRow>
            <TableCell
              colspan="8"
              class="text-center py-6 text-sm text-muted-foreground"
            >
              {{ listLoading ? '加载中...' : '暂无缓存记录' }}
            </TableCell>
          </TableRow>
        </TableBody>
      </Table>

      <!-- 移动端卡片列表 -->
      <div
        v-if="!listLoading && affinityList.length > 0"
        class="xl:hidden divide-y divide-border/40"
      >
        <div
          v-for="item in paginatedAffinityList"
          :key="`m-${item.affinity_key}-${item.endpoint_id}-${item.key_id}`"
          class="p-4 space-y-2"
        >
          <div class="flex items-start justify-between gap-3">
            <div class="flex-1 min-w-0">
              <div class="flex items-center gap-1.5">
                <Badge
                  v-if="item.is_standalone"
                  variant="outline"
                  class="text-warning border-warning/30 text-[10px] px-1"
                >
                  独立
                </Badge>
                <span class="text-sm font-medium truncate">{{ item.username || '未知' }}</span>
              </div>
              <div class="text-xs text-muted-foreground mt-0.5">
                {{ item.user_api_key_name || '未命名' }} · {{ item.user_api_key_prefix || '---' }}
              </div>
            </div>
            <Button
              size="icon"
              variant="ghost"
              class="h-7 w-7 text-muted-foreground/70 hover:text-destructive shrink-0"
              :disabled="clearingRowAffinityKey === item.affinity_key"
              @click="clearSingleAffinity(item)"
            >
              <Trash2 class="h-3.5 w-3.5" />
            </Button>
          </div>
          <div class="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
            <span>{{ item.provider_name || '未知' }}</span>
            <span>·</span>
            <span class="truncate max-w-[100px]">{{ item.model_display_name || '---' }}</span>
          </div>
          <div class="flex items-center justify-between text-xs">
            <span class="text-muted-foreground">{{ item.endpoint_api_format || '---' }}</span>
            <span>{{ getRemainingTime(item.expire_at) }} · {{ item.request_count }}次</span>
          </div>
        </div>
      </div>
      <div
        v-else-if="!listLoading && affinityList.length === 0"
        class="xl:hidden text-center py-6 text-sm text-muted-foreground"
      >
        暂无缓存记录
      </div>

      <Pagination
        v-if="affinityList.length > 0"
        :current="currentPage"
        :total="affinityList.length"
        :page-size="pageSize"
        @update:current="currentPage = $event; handlePageChange()"
        @update:page-size="pageSize = $event"
      />
    </Card>

    <!-- 模型映射缓存管理 -->
    <Card class="overflow-hidden">
      <div class="px-4 sm:px-6 py-3 sm:py-3.5 border-b border-border/60">
        <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
          <div class="flex items-center gap-3 shrink-0">
            <Database class="h-5 w-5 text-muted-foreground hidden sm:block" />
            <h3 class="text-sm sm:text-base font-semibold">
              模型映射缓存
            </h3>
          </div>
          <div class="flex flex-wrap items-center gap-2">
            <Button
              variant="ghost"
              size="icon"
              class="h-8 w-8 text-muted-foreground/70 hover:text-destructive"
              title="清除全部映射缓存"
              :disabled="clearingModelMapping || !modelMappingStats?.available"
              @click="clearAllModelMappingCache"
            >
              <Eraser class="h-4 w-4" />
            </Button>
            <RefreshButton
              :loading="modelMappingLoading"
              @click="fetchModelMappingStats"
            />
          </div>
        </div>
      </div>

      <!-- 映射缓存表格 -->
      <Table
        v-if="modelMappingStats?.available && modelMappingStats.mappings && modelMappingStats.mappings.length > 0"
        class="hidden md:table"
      >
        <TableHeader>
          <TableRow>
            <TableHead class="w-[25%]">
              全局模型
            </TableHead>
            <TableHead class="w-8 text-center" />
            <TableHead class="w-[30%]">
              映射模型
            </TableHead>
            <TableHead class="w-[25%]">
              提供商
            </TableHead>
            <TableHead class="w-[10%] text-center">
              剩余
            </TableHead>
            <TableHead class="w-[5%] text-right">
              操作
            </TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          <TableRow
            v-for="mapping in modelMappingStats.mappings"
            :key="mapping.mapping_name"
          >
            <TableCell>
              <div v-if="mapping.global_model_name">
                <div class="text-sm font-medium">
                  {{ mapping.global_model_display_name || mapping.global_model_name }}
                </div>
                <div
                  v-if="mapping.global_model_display_name && mapping.global_model_display_name !== mapping.global_model_name"
                  class="text-xs text-muted-foreground font-mono"
                >
                  {{ mapping.global_model_name }}
                </div>
              </div>
              <span
                v-else
                class="text-sm text-muted-foreground"
              >-</span>
            </TableCell>
            <TableCell class="text-center">
              <ArrowRight class="h-4 w-4 text-muted-foreground" />
            </TableCell>
            <TableCell>
              <span class="text-sm font-mono">{{ mapping.mapping_name }}</span>
            </TableCell>
            <TableCell>
              <div
                v-if="mapping.providers && mapping.providers.length > 0"
                class="flex flex-wrap gap-1"
              >
                <Badge
                  v-for="provider in mapping.providers.slice(0, 3)"
                  :key="provider"
                  variant="outline"
                  class="text-xs"
                >
                  {{ provider }}
                </Badge>
                <Badge
                  v-if="mapping.providers.length > 3"
                  variant="outline"
                  class="text-xs"
                >
                  +{{ mapping.providers.length - 3 }}
                </Badge>
              </div>
              <span
                v-else
                class="text-sm text-muted-foreground"
              >-</span>
            </TableCell>
            <TableCell class="text-center">
              <span class="text-xs text-muted-foreground">{{ formatTTL(mapping.ttl) }}</span>
            </TableCell>
            <TableCell class="text-right">
              <Button
                size="icon"
                variant="ghost"
                class="h-6 w-6 text-muted-foreground/50 hover:text-destructive"
                :disabled="clearingModelName === mapping.mapping_name"
                title="清除缓存"
                @click="clearModelMappingByName(mapping.mapping_name)"
              >
                <X class="h-3.5 w-3.5" />
              </Button>
            </TableCell>
          </TableRow>
        </TableBody>
      </Table>

      <!-- 移动端卡片列表 -->
      <div
        v-if="modelMappingStats?.available && modelMappingStats.mappings && modelMappingStats.mappings.length > 0"
        class="md:hidden divide-y divide-border/40"
      >
        <div
          v-for="mapping in modelMappingStats.mappings"
          :key="`m-${mapping.mapping_name}`"
          class="p-4 space-y-2"
        >
          <div class="flex items-center justify-between gap-2">
            <span class="text-sm font-medium truncate">{{ mapping.global_model_display_name || mapping.global_model_name || '-' }}</span>
            <Button
              size="icon"
              variant="ghost"
              class="h-6 w-6 text-muted-foreground/50 hover:text-destructive shrink-0"
              :disabled="clearingModelName === mapping.mapping_name"
              @click="clearModelMappingByName(mapping.mapping_name)"
            >
              <X class="h-3.5 w-3.5" />
            </Button>
          </div>
          <div class="flex items-center gap-2 text-xs text-muted-foreground">
            <ArrowRight class="h-3.5 w-3.5 shrink-0" />
            <span class="font-mono">{{ mapping.mapping_name }}</span>
          </div>
          <div
            v-if="mapping.providers && mapping.providers.length > 0"
            class="flex flex-wrap gap-1"
          >
            <Badge
              v-for="provider in mapping.providers"
              :key="provider"
              variant="outline"
              class="text-xs"
            >
              {{ provider }}
            </Badge>
          </div>
        </div>
      </div>

      <!-- 未映射条目（NOT_FOUND 等） -->
      <div
        v-if="modelMappingStats?.available && modelMappingStats.unmapped && modelMappingStats.unmapped.length > 0"
        class="px-6 py-4 border-t border-border/40"
      >
        <div class="text-xs text-muted-foreground mb-2">
          未映射的缓存条目
        </div>
        <div class="flex flex-wrap gap-1.5">
          <Badge
            v-for="entry in modelMappingStats.unmapped"
            :key="entry.mapping_name"
            :variant="getUnmappedStatusBadge(entry.status).variant"
            class="text-xs font-mono cursor-pointer"
            :title="`${getUnmappedStatusBadge(entry.status).text} - 点击清除`"
            @click="clearModelMappingByName(entry.mapping_name)"
          >
            {{ entry.mapping_name }}
          </Badge>
        </div>
      </div>

      <!-- Provider 模型映射缓存 -->
      <div
        v-if="modelMappingStats?.available && modelMappingStats.provider_model_mappings && modelMappingStats.provider_model_mappings.length > 0"
        class="border-t border-border/40"
      >
        <div class="px-6 py-3 text-xs text-muted-foreground border-b border-border/30 bg-muted/20">
          Provider 模型映射缓存
        </div>
        <!-- 桌面端表格 -->
        <Table class="hidden md:table">
          <TableHeader>
            <TableRow>
              <TableHead class="w-[15%]">
                提供商
              </TableHead>
              <TableHead class="w-[25%]">
                请求名称
              </TableHead>
              <TableHead class="w-8 text-center" />
              <TableHead class="w-[25%]">
                映射模型
              </TableHead>
              <TableHead class="w-[10%] text-center">
                剩余
              </TableHead>
              <TableHead class="w-[10%] text-center">
                次数
              </TableHead>
              <TableHead class="w-[7%] text-right">
                操作
              </TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            <template
              v-for="(mapping, index) in modelMappingStats.provider_model_mappings"
              :key="index"
            >
              <TableRow
                v-for="(alias, aliasIndex) in (mapping.aliases || [])"
                :key="`${index}-${aliasIndex}`"
              >
                <TableCell>
                  <Badge
                    variant="outline"
                    class="text-xs"
                  >
                    {{ mapping.provider_name }}
                  </Badge>
                </TableCell>
                <TableCell>
                  <span class="text-sm font-mono">{{ alias }}</span>
                </TableCell>
                <TableCell class="text-center">
                  <ArrowRight class="h-4 w-4 text-muted-foreground" />
                </TableCell>
                <TableCell>
                  <span class="text-sm font-mono font-medium">{{ mapping.provider_model_name }}</span>
                </TableCell>
                <TableCell class="text-center">
                  <span class="text-xs text-muted-foreground">{{ formatTTL(mapping.ttl) }}</span>
                </TableCell>
                <TableCell class="text-center">
                  <span class="text-sm">{{ mapping.hit_count || 0 }}</span>
                </TableCell>
                <TableCell class="text-right">
                  <Button
                    size="icon"
                    variant="ghost"
                    class="h-7 w-7 text-muted-foreground/70 hover:text-destructive"
                    title="清除缓存"
                    @click="clearProviderModelMapping(mapping.provider_id, mapping.global_model_id, `${mapping.provider_name} - ${alias}`)"
                  >
                    <Trash2 class="h-3.5 w-3.5" />
                  </Button>
                </TableCell>
              </TableRow>
            </template>
          </TableBody>
        </Table>
        <!-- 移动端卡片 -->
        <div class="md:hidden divide-y divide-border/40">
          <template
            v-for="(mapping, index) in modelMappingStats.provider_model_mappings"
            :key="`m-pm-${index}`"
          >
            <div
              v-for="(alias, aliasIndex) in (mapping.aliases || [])"
              :key="`m-pm-${index}-${aliasIndex}`"
              class="p-4 space-y-2"
            >
              <div class="flex items-center justify-between">
                <Badge
                  variant="outline"
                  class="text-xs"
                >
                  {{ mapping.provider_name }}
                </Badge>
                <div class="flex items-center gap-2">
                  <span class="text-xs text-muted-foreground">{{ formatTTL(mapping.ttl) }}</span>
                  <span class="text-xs">{{ mapping.hit_count || 0 }}次</span>
                  <Button
                    size="icon"
                    variant="ghost"
                    class="h-6 w-6 text-muted-foreground/70 hover:text-destructive"
                    title="清除缓存"
                    @click="clearProviderModelMapping(mapping.provider_id, mapping.global_model_id, `${mapping.provider_name} - ${alias}`)"
                  >
                    <Trash2 class="h-3 w-3" />
                  </Button>
                </div>
              </div>
              <div class="flex items-center gap-2 text-sm">
                <span class="font-mono">{{ alias }}</span>
                <ArrowRight class="h-3.5 w-3.5 shrink-0 text-muted-foreground/60" />
                <span class="font-mono font-medium">{{ mapping.provider_model_name }}</span>
              </div>
            </div>
          </template>
        </div>
      </div>

      <!-- 无缓存状态 -->
      <div
        v-else-if="modelMappingStats?.available && (!modelMappingStats.mappings || modelMappingStats.mappings.length === 0) && (!modelMappingStats.unmapped || modelMappingStats.unmapped.length === 0) && (!modelMappingStats.provider_model_mappings || modelMappingStats.provider_model_mappings.length === 0)"
        class="px-6 py-8 text-center text-sm text-muted-foreground"
      >
        暂无模型解析缓存
      </div>

      <!-- Redis 未启用 -->
      <div
        v-else-if="modelMappingStats && !modelMappingStats.available"
        class="px-6 py-8 text-center text-sm text-muted-foreground"
      >
        {{ modelMappingStats.message || 'Redis 未启用' }}
      </div>

      <!-- 加载中 -->
      <div
        v-else-if="modelMappingLoading"
        class="px-6 py-8 text-center text-sm text-muted-foreground"
      >
        加载中...
      </div>
    </Card>

    <!-- TTL 分析区域 -->
    <Card class="overflow-hidden">
      <div class="px-4 sm:px-6 py-3 sm:py-3.5 border-b border-border/60">
        <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 sm:gap-4">
          <div class="flex items-center gap-3 shrink-0">
            <BarChart3 class="h-5 w-5 text-muted-foreground hidden sm:block" />
            <h3 class="text-sm sm:text-base font-semibold">
              TTL 分析
            </h3>
            <span class="text-xs text-muted-foreground hidden sm:inline">分析用户请求间隔，推荐合适的缓存 TTL</span>
          </div>
          <div class="flex flex-wrap items-center gap-2">
            <Select
              v-model="analysisHours"
              v-model:open="analysisHoursSelectOpen"
            >
              <SelectTrigger class="w-24 sm:w-28 h-8">
                <SelectValue placeholder="时间段" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem
                  v-for="option in ANALYSIS_HOURS_OPTIONS"
                  :key="option.value"
                  :value="option.value"
                >
                  {{ option.label }}
                </SelectItem>
              </SelectContent>
            </Select>
          </div>
        </div>
      </div>

      <!-- 缓存命中概览 -->
      <div
        v-if="hitAnalysis"
        class="px-6 py-4 border-b border-border/40 bg-muted/30"
      >
        <div class="grid grid-cols-2 md:grid-cols-5 gap-6">
          <div>
            <div class="text-xs text-muted-foreground">
              请求命中率
            </div>
            <div class="text-2xl font-bold text-success">
              {{ hitAnalysis.request_cache_hit_rate }}%
            </div>
            <div class="text-xs text-muted-foreground">
              {{ formatNumber(hitAnalysis.requests_with_cache_hit) }} / {{ formatNumber(hitAnalysis.total_requests) }} 请求
            </div>
          </div>
          <div>
            <div class="text-xs text-muted-foreground">
              Token 命中率
            </div>
            <div class="text-2xl font-bold">
              {{ hitAnalysis.token_cache_hit_rate }}%
            </div>
            <div class="text-xs text-muted-foreground">
              {{ formatTokens(hitAnalysis.total_cache_read_tokens) }} tokens 命中
            </div>
          </div>
          <div>
            <div class="text-xs text-muted-foreground">
              缓存创建费用
            </div>
            <div class="text-2xl font-bold">
              {{ formatCost(hitAnalysis.total_cache_creation_cost_usd) }}
            </div>
            <div class="text-xs text-muted-foreground">
              {{ formatTokens(hitAnalysis.total_cache_creation_tokens) }} tokens
            </div>
          </div>
          <div>
            <div class="text-xs text-muted-foreground">
              缓存读取费用
            </div>
            <div class="text-2xl font-bold">
              {{ formatCost(hitAnalysis.total_cache_read_cost_usd) }}
            </div>
            <div class="text-xs text-muted-foreground">
              {{ formatTokens(hitAnalysis.total_cache_read_tokens) }} tokens
            </div>
          </div>
          <div>
            <div class="text-xs text-muted-foreground">
              预估节省
            </div>
            <div class="text-2xl font-bold text-success">
              {{ formatCost(hitAnalysis.estimated_savings_usd) }}
            </div>
          </div>
        </div>
      </div>

      <!-- 用户 TTL 分析表格 -->
      <Table v-if="ttlAnalysis && ttlAnalysis.users.length > 0">
        <TableHeader>
          <TableRow>
            <TableHead class="w-10" />
            <TableHead class="w-[20%]">
              用户
            </TableHead>
            <TableHead class="w-[15%] text-center">
              请求数
            </TableHead>
            <TableHead class="w-[15%] text-center">
              使用频率
            </TableHead>
            <TableHead class="w-[15%] text-center">
              推荐 TTL
            </TableHead>
            <TableHead>说明</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          <template
            v-for="user in ttlAnalysis.users"
            :key="user.group_id"
          >
            <TableRow
              class="cursor-pointer hover:bg-muted/50"
              @click="toggleUserExpand(user.group_id)"
            >
              <TableCell class="p-2">
                <button class="p-1 hover:bg-muted rounded">
                  <ChevronDown
                    v-if="expandedUserId === user.group_id"
                    class="h-4 w-4 text-muted-foreground"
                  />
                  <ChevronRight
                    v-else
                    class="h-4 w-4 text-muted-foreground"
                  />
                </button>
              </TableCell>
              <TableCell>
                <span class="text-sm font-medium">{{ user.username || '未知用户' }}</span>
              </TableCell>
              <TableCell class="text-center">
                <span class="text-sm font-medium">{{ user.request_count }}</span>
              </TableCell>
              <TableCell class="text-center">
                <span
                  class="text-sm"
                  :class="getFrequencyClass(user.recommended_ttl_minutes)"
                >
                  {{ getFrequencyLabel(user.recommended_ttl_minutes) }}
                </span>
              </TableCell>
              <TableCell class="text-center">
                <Badge :variant="getTTLBadgeVariant(user.recommended_ttl_minutes)">
                  {{ user.recommended_ttl_minutes }} 分钟
                </Badge>
              </TableCell>
              <TableCell>
                <span class="text-xs text-muted-foreground">
                  {{ formatIntervalDescription(user) }}
                </span>
              </TableCell>
            </TableRow>
            <!-- 展开行：显示用户散点图 -->
            <TableRow
              v-if="expandedUserId === user.group_id"
              class="bg-muted/30"
            >
              <TableCell
                colspan="6"
                class="p-0"
              >
                <div class="px-6 py-4">
                  <div class="flex items-center justify-between mb-3">
                    <h4 class="text-sm font-medium">
                      请求间隔时间线
                    </h4>
                    <div class="flex items-center gap-3 text-xs text-muted-foreground">
                      <span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-green-500" /> 0-5分钟</span>
                      <span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-blue-500" /> 5-15分钟</span>
                      <span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-purple-500" /> 15-30分钟</span>
                      <span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-orange-500" /> 30-60分钟</span>
                      <span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-red-500" /> >60分钟</span>
                      <span
                        v-if="userTimelineData"
                        class="ml-2"
                      >共 {{ userTimelineData.total_points }} 个数据点</span>
                    </div>
                  </div>
                  <div
                    v-if="userTimelineLoading"
                    class="h-64 flex items-center justify-center"
                  >
                    <span class="text-sm text-muted-foreground">加载中...</span>
                  </div>
                  <div
                    v-else-if="userTimelineData && userTimelineData.points.length > 0"
                    class="h-64"
                  >
                    <ScatterChart :data="userTimelineChartData" />
                  </div>
                  <div
                    v-else
                    class="h-64 flex items-center justify-center"
                  >
                    <span class="text-sm text-muted-foreground">暂无数据</span>
                  </div>
                </div>
              </TableCell>
            </TableRow>
          </template>
        </TableBody>
      </Table>

      <!-- 分析完成但无数据 -->
      <div
        v-else-if="ttlAnalysis && ttlAnalysis.users.length === 0"
        class="px-6 py-12 text-center"
      >
        <BarChart3 class="h-12 w-12 text-muted-foreground/50 mx-auto mb-3" />
        <p class="text-sm text-muted-foreground">
          未找到符合条件的用户数据
        </p>
        <p class="text-xs text-muted-foreground mt-1">
          尝试增加分析天数或降低最小请求数阈值
        </p>
      </div>

      <!-- 加载中 -->
      <div
        v-else-if="ttlAnalysisLoading"
        class="px-6 py-12 text-center"
      >
        <p class="text-sm text-muted-foreground">
          正在分析用户请求数据...
        </p>
      </div>
    </Card>
  </div>
</template>
