<template>
  <div class="ingest-traces-page">
    <!-- ── Filter Bar ──────────────────────────────────────── -->
    <a-card size="small" class="filter-card">
      <a-row :gutter="[12, 12]">
        <a-col :span="6">
          <a-input
            v-model:value="filters.keyword"
            placeholder="标题 / 文件名 / doc_id / trace_id"
            allow-clear
            @press-enter="handleSearch"
          />
        </a-col>
        <a-col :span="3">
          <a-select
            v-model:value="filters.status"
            placeholder="状态"
            allow-clear
            style="width: 100%"
          >
            <a-select-option value="running">运行中</a-select-option>
            <a-select-option value="completed">已完成</a-select-option>
            <a-select-option value="partial_failed">部分失败</a-select-option>
            <a-select-option value="failed">失败</a-select-option>
          </a-select>
        </a-col>
        <a-col :span="3">
          <a-select
            v-model:value="filters.file_type"
            placeholder="文件类型"
            allow-clear
            style="width: 100%"
          >
            <a-select-option value="pdf">PDF</a-select-option>
            <a-select-option value="docx">DOCX</a-select-option>
            <a-select-option value="doc">DOC</a-select-option>
            <a-select-option value="html">HTML</a-select-option>
            <a-select-option value="txt">TXT</a-select-option>
          </a-select>
        </a-col>
        <a-col :span="3">
          <a-select
            v-model:value="filters.source_type"
            placeholder="来源"
            allow-clear
            style="width: 100%"
          >
            <a-select-option value="manual">手动上传</a-select-option>
            <a-select-option value="webhook">Webhook</a-select-option>
            <a-select-option value="api">API</a-select-option>
          </a-select>
        </a-col>
        <a-col :span="5">
          <a-range-picker
            v-model:value="filters.dateRange"
            style="width: 100%"
            :placeholder="['开始时间', '结束时间']"
          />
        </a-col>
        <a-col :span="4">
          <a-space>
            <a-switch
              v-model:checked="filters.has_error"
              checked-children="仅失败"
              un-checked-children="全部"
            />
            <a-button type="primary" @click="handleSearch">查询</a-button>
            <a-button @click="resetFilters">重置</a-button>
          </a-space>
        </a-col>
      </a-row>
      <!-- Visible tag when filtering by task_id from Dashboard -->
      <div v-if="filters.task_id" style="margin-top: 8px;">
        <a-tag closable color="blue" @close="clearTaskIdFilter">
          Task ID: {{ filters.task_id.slice(0, 12) }}…
        </a-tag>
      </div>
    </a-card>

    <!-- ── Stats Cards ─────────────────────────────────────── -->
    <a-row :gutter="16" class="stats-row">
      <a-col :span="6">
        <a-card size="small">
          <a-statistic title="今日入库" :value="stats.today_total" />
        </a-card>
      </a-col>
      <a-col :span="6">
        <a-card size="small">
          <a-statistic title="运行中" :value="stats.running" :value-style="{ color: '#1890ff' }" />
        </a-card>
      </a-col>
      <a-col :span="6">
        <a-card size="small">
          <a-statistic title="失败" :value="stats.failed" :value-style="{ color: '#ff4d4f' }" />
        </a-card>
      </a-col>
      <a-col :span="6">
        <a-card size="small">
          <a-statistic
            title="平均耗时"
            :value="stats.avg_duration_ms ? (stats.avg_duration_ms / 1000).toFixed(1) : 0"
            suffix="s"
          />
        </a-card>
      </a-col>
    </a-row>

    <!-- ── Trace Table ─────────────────────────────────────── -->
    <a-card size="small">
      <a-table
        :columns="columns"
        :data-source="traces"
        :loading="loading"
        :pagination="pagination"
        row-key="trace_id"
        size="small"
        :scroll="{ x: 1400 }"
        @change="handleTableChange"
      >
        <template #bodyCell="{ column, record }">
          <template v-if="column.dataIndex === 'title'">
            <a @click="openDrawer(record)">
              {{ record.original_filename || record.doc_id?.slice(0, 12) }}
            </a>
          </template>

          <template v-else-if="column.dataIndex === 'trace_id'">
            <a-typography-text copyable :content="record.trace_id" style="font-size: 12px;">
              {{ record.trace_id?.slice(0, 8) }}…
            </a-typography-text>
          </template>

          <template v-else-if="column.dataIndex === 'status'">
            <a-tag :color="statusColor(record.status)">
              {{ statusLabel(record.status) }}
            </a-tag>
          </template>

          <template v-else-if="column.dataIndex === 'current_stage'">
            <span>{{ record.current_stage || '—' }}</span>
          </template>

          <template v-else-if="column.dataIndex === 'duration_ms'">
            {{ record.duration_ms != null ? (record.duration_ms / 1000).toFixed(1) + 's' : '—' }}
          </template>

          <template v-else-if="column.dataIndex === 'started_at'">
            {{ formatDate(record.started_at) }}
          </template>

          <template v-else-if="column.dataIndex === 'error'">
            <a-tooltip v-if="record.error_message" :title="record.error_message">
              <a-tag color="red">{{ record.error_code || 'ERROR' }}</a-tag>
            </a-tooltip>
            <span v-else>—</span>
          </template>

          <template v-else-if="column.dataIndex === 'action'">
            <a @click="openDrawer(record)">详情</a>
          </template>
        </template>
      </a-table>
    </a-card>

    <!-- ── Detail Drawer ───────────────────────────────────── -->
    <a-drawer
      v-model:open="drawerVisible"
      :title="currentTrace?.original_filename || '追踪详情'"
      width="780"
      :destroy-on-close="true"
      :body-style="{ padding: '16px 24px', display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }"
      @close="closeDrawer"
    >
      <template v-if="currentTrace">
        <!-- Summary header -->
        <a-descriptions :column="2" size="small" bordered class="trace-summary">
          <a-descriptions-item label="文件名">{{ currentTrace.original_filename }}</a-descriptions-item>
          <a-descriptions-item label="状态">
            <a-tag :color="statusColor(currentTrace.status)">{{ statusLabel(currentTrace.status) }}</a-tag>
            <a-tag v-if="currentTrace.status === 'running'" color="processing" style="margin-left: 4px;">
              轮询中
            </a-tag>
          </a-descriptions-item>
          <a-descriptions-item label="doc_id">
            <a-typography-text copyable :content="currentTrace.doc_id" style="font-size: 12px;">
              {{ currentTrace.doc_id }}
            </a-typography-text>
          </a-descriptions-item>
          <a-descriptions-item label="trace_id">
            <a-typography-text copyable :content="currentTrace.trace_id" style="font-size: 12px;">
              {{ currentTrace.trace_id }}
            </a-typography-text>
          </a-descriptions-item>
          <a-descriptions-item label="task_id">
            <a-typography-text copyable :content="currentTrace.task_id" style="font-size: 12px;">
              {{ currentTrace.task_id }}
            </a-typography-text>
          </a-descriptions-item>
          <a-descriptions-item label="当前阶段">
            <a-tag size="small">{{ currentTrace.current_stage }}</a-tag>
          </a-descriptions-item>
          <a-descriptions-item label="时间">
            {{ formatDate(currentTrace.started_at) }}
            <template v-if="currentTrace.finished_at">
              <span style="color: #999; margin: 0 4px;">→</span>
              {{ formatDate(currentTrace.finished_at) }}
            </template>
          </a-descriptions-item>
          <a-descriptions-item label="总耗时">
            {{ currentTrace.duration_ms != null ? (currentTrace.duration_ms / 1000).toFixed(2) + 's' : '—' }}
          </a-descriptions-item>
          <a-descriptions-item label="文件类型">
            <a-tag size="small">{{ currentTrace.file_type?.toUpperCase() }}</a-tag>
          </a-descriptions-item>
          <a-descriptions-item label="内容哈希">
            <a-typography-text
              v-if="currentTrace.content_hash"
              copyable
              :content="currentTrace.content_hash"
              style="font-size: 11px; font-family: monospace;"
            >
              {{ currentTrace.content_hash?.slice(0, 16) }}…
            </a-typography-text>
            <span v-else style="color: #ccc;">—</span>
          </a-descriptions-item>
          <a-descriptions-item v-if="currentTrace.error_code" label="错误" :span="2">
            <a-tag color="red" size="small">{{ currentTrace.error_code }}</a-tag>
            <span style="font-size: 12px; color: #ff4d4f; margin-left: 4px;">
              {{ currentTrace.error_message?.slice(0, 200) }}
            </span>
          </a-descriptions-item>
        </a-descriptions>

        <!-- Tabs: Timeline / Stages / Artifacts / JSON -->
        <a-tabs v-model:activeKey="drawerTab" class="drawer-tabs">
          <!-- 阶段视图：将 started/completed 配对合并为阶段卡片 -->
          <a-tab-pane key="stages" tab="阶段概览">
            <a-spin :spinning="eventsLoading">
              <div v-if="stageList.length" class="stage-list">
                <div
                  v-for="stage in stageList"
                  :key="stage.name"
                  class="stage-card"
                  :class="{ 'stage-card--active': selectedStage === stage.name }"
                  @click="selectedStage = selectedStage === stage.name ? null : stage.name"
                >
                  <div class="stage-card-header">
                    <span class="stage-card-dot" :style="{ background: stageStatusDotColor(stage.status) }"></span>
                    <span class="stage-card-name">{{ stage.name }}</span>
                    <a-tag v-if="stage.service && stage.service !== 'worker'" size="small" style="margin-left: auto; font-size: 10px;">
                      {{ stage.service }}
                    </a-tag>
                    <span class="stage-card-duration" v-if="stage.durationMs">
                      {{ (stage.durationMs / 1000).toFixed(2) }}s
                    </span>
                    <a-tag :color="stageStatusDotColor(stage.status)" size="small" style="margin-left: 4px;">
                      {{ stage.status }}
                    </a-tag>
                  </div>
                  <div class="stage-card-summary" v-if="stage.summary">{{ stage.summary }}</div>
                  <div class="stage-card-time">{{ formatDate(stage.startedAt) }}</div>
                  <!-- Expanded details -->
                  <template v-if="selectedStage === stage.name">
                    <div v-if="stage.errorCode" class="stage-card-error">
                      <a-tag color="red" size="small">{{ stage.errorCode }}</a-tag>
                      <span>{{ stage.errorMessage?.slice(0, 300) }}</span>
                    </div>
                    <div v-if="stage.details && Object.keys(stage.details).length" class="stage-card-details">
                      <div v-for="(val, key) in stage.details" :key="key" class="stage-detail-row">
                        <span class="stage-detail-key">{{ key }}</span>
                        <span class="stage-detail-val">{{ typeof val === 'object' ? JSON.stringify(val) : val }}</span>
                      </div>
                    </div>
                  </template>
                </div>
              </div>
              <a-empty v-else description="暂无阶段数据" />
            </a-spin>
          </a-tab-pane>

          <a-tab-pane key="timeline" tab="事件时间线">
            <a-spin :spinning="eventsLoading">
              <a-timeline v-if="events.length" mode="left" style="margin-top: 12px;">
                <a-timeline-item
                  v-for="evt in events"
                  :key="evt.event_id"
                  :color="eventColor(evt.status)"
                >
                  <div class="event-node" @click="selectedEvent = (selectedEvent?.event_id === evt.event_id ? null : evt)">
                    <div class="event-header">
                      <a-tag :color="eventColor(evt.status)" size="small">{{ evt.stage }}</a-tag>
                      <span class="event-type">{{ evt.event_type }}</span>
                      <span v-if="evt.duration_ms" class="event-duration" :class="durationClass(evt.duration_ms)">
                        {{ (evt.duration_ms / 1000).toFixed(2) }}s
                      </span>
                      <span class="event-seq">#{{ evt.seq }}</span>
                    </div>
                    <div class="event-summary">{{ evt.summary }}</div>
                    <div class="event-meta">
                      <span class="event-time">{{ formatDate(evt.timestamp) }}</span>
                      <a-tag v-if="evt.error_code" color="red" size="small" style="margin-left: 6px;">
                        {{ evt.error_code }}
                      </a-tag>
                    </div>
                  </div>
                </a-timeline-item>
              </a-timeline>
              <a-empty v-else description="暂无事件" />
            </a-spin>
          </a-tab-pane>

          <a-tab-pane key="artifacts" tab="工件" v-if="artifacts.length > 0">
            <a-table
              :columns="artifactColumns"
              :data-source="artifacts"
              row-key="artifact_id"
              size="small"
              :pagination="false"
            >
              <template #bodyCell="{ column, record: art }">
                <template v-if="column.dataIndex === 'payload_text'">
                  <a-typography-paragraph
                    :ellipsis="{ rows: 2, expandable: true }"
                    :content="art.payload_text || '—'"
                    style="font-size: 12px; margin: 0;"
                  />
                </template>
              </template>
            </a-table>
          </a-tab-pane>

          <a-tab-pane key="json" tab="原始 JSON">
            <a-button size="small" type="link" @click="copyJson" style="margin-bottom: 8px;">复制 JSON</a-button>
            <pre class="json-block">{{ JSON.stringify(currentTrace, null, 2) }}</pre>
          </a-tab-pane>
        </a-tabs>

        <!-- Sticky bottom: event detail panel -->
        <div v-if="selectedEvent && drawerTab === 'timeline'" class="event-detail-sticky">
          <div class="event-detail-sticky-header">
            <span class="event-detail-sticky-title">
              <a-tag :color="eventColor(selectedEvent.status)" size="small">{{ selectedEvent.stage }}</a-tag>
              {{ selectedEvent.event_type }}
              <span v-if="selectedEvent.duration_ms" style="color: #999; font-size: 12px; margin-left: 6px;">
                {{ (selectedEvent.duration_ms / 1000).toFixed(2) }}s
              </span>
            </span>
            <a-button size="small" type="text" @click="selectedEvent = null">
              <UpOutlined />
            </a-button>
          </div>
          <pre class="json-block json-block--sticky">{{ JSON.stringify(selectedEvent.details || {}, null, 2) }}</pre>
        </div>
      </template>
    </a-drawer>
  </div>
</template>

<script setup lang="ts">
/**
 * 入库追踪视图
 * 提供入库任务的全链路追踪：筛选查询、统计概览、追踪列表、
 * 详情抽屉（时间线事件、中间工件、原始 JSON）。
 * 对于运行中的任务支持自动轮询刷新。
 */
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import type { Dayjs } from 'dayjs'
import request from '@/api/request'
// 使用共享的格式化工具函数
// Use shared formatting utilities
import { UpOutlined } from '@ant-design/icons-vue'
import { statusColor, statusLabel, formatDate } from '@/utils/format'

const route = useRoute()
const router = useRouter()
import type {
  IngestTrace,
  IngestEvent,
  IngestArtifact,
  TraceStats,
} from '@/types/ingest-trace'

// ── State ─────────────────────────────────────────────────

const loading = ref(false)
const eventsLoading = ref(false)
const traces = ref<IngestTrace[]>([])
const events = ref<IngestEvent[]>([])
const artifacts = ref<IngestArtifact[]>([])
const stats = reactive<TraceStats>({
  today_total: 0,
  running: 0,
  completed: 0,
  failed: 0,
  avg_duration_ms: 0,
})

const filters = reactive<{
  keyword: string
  status: string | undefined
  file_type: string | undefined
  source_type: string | undefined
  has_error: boolean
  dateRange: [Dayjs, Dayjs] | null
  task_id: string | undefined
}>({
  keyword: '',
  status: undefined,
  file_type: undefined,
  source_type: undefined,
  has_error: false,
  dateRange: null,
  task_id: (route.query.task_id as string) || undefined,
})

const pagination = reactive({
  current: 1,
  pageSize: 20,
  total: 0,
  showSizeChanger: true,
  showTotal: (total: number) => `共 ${total} 条`,
})

// Drawer
const drawerVisible = ref(false)
const drawerTab = ref('stages')
const currentTrace = ref<IngestTrace | null>(null)
const selectedEvent = ref<IngestEvent | null>(null)
const selectedStage = ref<string | null>(null)
let pollTimer: ReturnType<typeof setInterval> | null = null

// ── Table columns ─────────────────────────────────────────

const columns = [
  { title: '文件名', dataIndex: 'title', width: 300,  ellipsis: true },
  { title: 'Trace ID', dataIndex: 'trace_id', width: 70 },
  { title: '文件类型', dataIndex: 'file_type', width: 30 },
  { title: '当前阶段', dataIndex: 'current_stage', width: 100 },
  { title: '状态', dataIndex: 'status', width: 50 },
  { title: '耗时', dataIndex: 'duration_ms', width: 50 },
  { title: '开始时间', dataIndex: 'started_at', width: 100 },
  { title: '错误', dataIndex: 'error', width: 120 },
  { title: '操作', dataIndex: 'action', width: 60, align:'center', fixed: 'right' as const },
]

const artifactColumns = [
  { title: '类型', dataIndex: 'artifact_type', width: 120 },
  { title: '阶段', dataIndex: 'stage', width: 120 },
  { title: '标签', dataIndex: 'label', width: 140 },
  { title: '大小', dataIndex: 'payload_size', width: 80 },
  { title: '内容预览', dataIndex: 'payload_text' },
]

// ── Helpers ───────────────────────────────────────────────

function eventColor(s: string) {
  if (s === 'completed') return 'green'
  if (s === 'started') return 'blue'
  if (s === 'failed') return 'red'
  return 'gray'
}

function durationClass(ms: number) {
  if (ms >= 60_000) return 'duration--danger'
  if (ms >= 30_000) return 'duration--warning'
  if (ms >= 10_000) return 'duration--slow'
  return ''
}

function stageStatusDotColor(s: string) {
  if (s === 'completed') return '#52c41a'
  if (s === 'started' || s === 'running') return '#1890ff'
  if (s === 'failed') return '#ff4d4f'
  return '#d9d9d9'
}

interface StageInfo {
  name: string
  status: string
  summary: string
  service: string
  startedAt: string
  finishedAt: string
  durationMs: number | null
  errorCode: string
  errorMessage: string
  details: Record<string, unknown> | null
}

/** Merge paired started/completed events into stage cards */
const stageList = computed<StageInfo[]>(() => {
  const map = new Map<string, { started?: IngestEvent; completed?: IngestEvent; failed?: IngestEvent }>()
  for (const evt of events.value) {
    const stage = evt.stage
    if (!stage) continue
    if (!map.has(stage)) map.set(stage, {})
    const entry = map.get(stage)!
    if (evt.event_type === 'stage_started') entry.started = evt
    else if (evt.event_type === 'stage_completed') entry.completed = evt
    else if (evt.event_type === 'stage_failed') entry.failed = evt
    // For non-stage events (task_started etc.), use event_type as key
    else {
      const key = evt.event_type || stage
      if (!map.has(key)) map.set(key, {})
      const e = map.get(key)!
      if (!e.completed && !e.failed) e.completed = evt
      else if (!e.started) e.started = evt
    }
  }

  const result: StageInfo[] = []
  for (const [name, entry] of map) {
    const end = entry.failed || entry.completed
    const start = entry.started
    const ref = end || start
    if (!ref) continue
    result.push({
      name,
      status: entry.failed ? 'failed' : entry.completed ? 'completed' : 'running',
      summary: (end?.summary || start?.summary) ?? '',
      service: (ref as IngestEvent).service || '',
      startedAt: start?.timestamp || ref.timestamp || '',
      finishedAt: end?.timestamp || '',
      durationMs: end?.duration_ms ?? null,
      errorCode: (entry.failed as IngestEvent)?.error_code || '',
      errorMessage: (entry.failed as IngestEvent)?.error_message || '',
      details: end?.details || start?.details || null,
    })
  }
  return result
})

// ── Data loading ──────────────────────────────────────────

async function loadTraces() {
  loading.value = true
  try {
    const params: Record<string, unknown> = {
      page: pagination.current,
      page_size: pagination.pageSize,
    }
    if (filters.keyword) params.keyword = filters.keyword
    if (filters.task_id) params.task_id = filters.task_id
    if (filters.status) params.status = filters.status
    if (filters.file_type) params.file_type = filters.file_type
    if (filters.source_type) params.source_type = filters.source_type
    if (filters.has_error) params.has_error = true
    if (filters.dateRange && filters.dateRange[0]) {
      params.start_time = filters.dateRange[0].toISOString()
      params.end_time = filters.dateRange[1].toISOString()
    }
    const { data } = await request.get('/api/ai/v1/admin/ingest-traces', { params })
    traces.value = data.records || []
    pagination.total = data.total || 0
  } catch {
    // handled by interceptor
  } finally {
    loading.value = false
  }
}

async function loadStats() {
  try {
    const { data } = await request.get('/api/ai/v1/admin/ingest-traces/stats')
    Object.assign(stats, data)
  } catch {
    // non-critical
  }
}

async function loadEvents(traceId: string) {
  eventsLoading.value = true
  try {
    const { data } = await request.get(`/api/ai/v1/admin/ingest-traces/${traceId}/events`, {
      params: { page_size: 500 },
    })
    events.value = data.records || []
  } catch {
    events.value = []
  } finally {
    eventsLoading.value = false
  }
}

async function loadArtifacts(traceId: string) {
  try {
    const { data } = await request.get(`/api/ai/v1/admin/ingest-traces/${traceId}/artifacts`, {
      params: { page_size: 200 },
    })
    artifacts.value = data.records || []
  } catch {
    artifacts.value = []
  }
}

async function refreshTraceDetail() {
  if (!currentTrace.value) return
  const traceId = currentTrace.value.trace_id
  try {
    const { data } = await request.get(`/api/ai/v1/admin/ingest-traces/${traceId}`)
    currentTrace.value = data
    // 当追踪状态变为终态时自动停止轮询，避免无意义的持续请求
    // Auto-stop polling when trace reaches terminal state to avoid unnecessary requests
    const terminalStates = ['completed', 'failed', 'partial_failed']
    if (terminalStates.includes(data.status)) {
      stopPoll()
    }
  } catch {
    // keep existing data
  }
  await loadEvents(traceId)
}

// ── Actions ───────────────────────────────────────────────

function handleSearch() {
  pagination.current = 1
  loadTraces()
  loadStats()
}

function resetFilters() {
  filters.keyword = ''
  filters.status = undefined
  filters.file_type = undefined
  filters.source_type = undefined
  filters.has_error = false
  filters.dateRange = null
  filters.task_id = undefined
  // Clean query params from URL
  if (route.query.task_id) {
    router.replace({ query: {} })
  }
  handleSearch()
}

function clearTaskIdFilter() {
  filters.task_id = undefined
  if (route.query.task_id) {
    router.replace({ query: {} })
  }
  handleSearch()
}

function handleTableChange(pag: { current?: number; pageSize?: number }) {
  pagination.current = pag.current || 1
  pagination.pageSize = pag.pageSize || 20
  loadTraces()
}

/** 打开追踪详情抽屉，加载事件时间线和工件列表；运行中的任务启动轮询 */
function openDrawer(trace: IngestTrace) {
  currentTrace.value = trace
  selectedEvent.value = null
  drawerTab.value = 'stages'
  drawerVisible.value = true
  loadEvents(trace.trace_id)
  loadArtifacts(trace.trace_id)

  // Poll for running traces
  stopPoll()
  if (trace.status === 'running') {
    pollTimer = setInterval(refreshTraceDetail, 5000)
  }
}

function closeDrawer() {
  drawerVisible.value = false
  currentTrace.value = null
  events.value = []
  artifacts.value = []
  selectedEvent.value = null
  stopPoll()
}

function stopPoll() {
  if (pollTimer) {
    clearInterval(pollTimer)
    pollTimer = null
  }
}

function copyJson() {
  if (!currentTrace.value) return
  navigator.clipboard.writeText(JSON.stringify(currentTrace.value, null, 2))
  message.success('已复制')
}

// ── Lifecycle ─────────────────────────────────────────────

onMounted(() => {
  loadTraces()
  loadStats()
})

// Re-sync filters when the route query changes (e.g. navigating from Dashboard
// with a different task_id while the page is already open)
watch(
  () => route.query.task_id,
  (newVal) => {
    const tid = (newVal as string) || undefined
    if (tid !== filters.task_id) {
      filters.task_id = tid
      pagination.current = 1
      loadTraces()
    }
  },
)

onUnmounted(() => {
  stopPoll()
})
</script>

<style scoped lang="less">
.ingest-traces-page {
  padding: 24px;
}

.filter-card {
  margin-bottom: 16px;
}

.stats-row {
  margin-bottom: 16px;
}

.trace-summary {
  margin-bottom: 8px;
}

.event-node {
  cursor: pointer;
  padding: 4px 0;

  &:hover {
    background: #fafafa;
  }
}

.event-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

.event-type {
  font-weight: 500;
  font-size: 13px;
}

.event-duration {
  color: #999;
  font-size: 12px;
  font-weight: 500;

  &.duration--slow {
    color: #d4b106;
    font-weight: 600;
  }
  &.duration--warning {
    color: #d46b08;
    font-weight: 600;
  }
  &.duration--danger {
    color: #cf1322;
    font-weight: 600;
  }
}

.event-summary {
  color: #666;
  font-size: 12px;
  margin-top: 2px;
}

.event-time {
  color: #aaa;
  font-size: 11px;
  margin-top: 2px;
}

.event-detail-card {
  margin-top: 16px;
}

.json-block {
  background: #f6f6f6;
  padding: 12px;
  border-radius: 4px;
  font-size: 12px;
  max-height: 500px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-all;
}

// ── Stage overview ──────────────────────────────────────
.stage-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.stage-card {
  border: 1px solid #f0f0f0;
  border-radius: 6px;
  padding: 10px 12px;
  cursor: pointer;
  transition: all 0.2s;

  &:hover {
    border-color: #d9d9d9;
    background: #fafafa;
  }

  &--active {
    border-color: #1890ff;
    background: #e6f7ff;
  }
}

.stage-card-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

.stage-card-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.stage-card-name {
  font-weight: 500;
  font-size: 13px;
}

.stage-card-duration {
  color: #999;
  font-size: 12px;
  margin-left: auto;
}

.stage-card-summary {
  color: #666;
  font-size: 12px;
  margin-top: 4px;
  padding-left: 16px;
}

.stage-card-time {
  color: #aaa;
  font-size: 11px;
  margin-top: 2px;
  padding-left: 16px;
}

.stage-card-error {
  margin-top: 6px;
  padding-left: 16px;
  font-size: 12px;
  color: #ff4d4f;
}

.stage-card-details {
  margin-top: 6px;
  padding-left: 16px;
}

.stage-detail-row {
  display: flex;
  gap: 8px;
  font-size: 12px;
  line-height: 1.8;
}

.stage-detail-key {
  color: #999;
  min-width: 100px;
  flex-shrink: 0;

  &::after {
    content: ':';
  }
}

.stage-detail-val {
  color: #333;
  word-break: break-all;
}

// ── Sticky event detail ─────────────────────────────────
.event-detail-sticky {
  border-top: 1px solid #f0f0f0;
  padding-top: 8px;
  flex-shrink: 0;
  max-height: 35%;
  display: flex;
  flex-direction: column;
}

.event-detail-sticky-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}

.event-detail-sticky-title {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 500;
  font-size: 13px;
}

.json-block--sticky {
  max-height: none;
  flex: 1;
  min-height: 0;
  overflow: auto;
}

// ── Timeline enhancements ───────────────────────────────
.event-seq {
  color: #bbb;
  font-size: 11px;
  font-family: monospace;
}

.event-meta {
  display: flex;
  align-items: center;
  margin-top: 2px;
}

// ── Drawer tabs fill height ─────────────────────────────
.drawer-tabs {
  margin-top: 12px;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;

  :deep(.ant-tabs-content-holder) {
    flex: 1;
    min-height: 0;
    overflow: hidden;
  }

  :deep(.ant-tabs-content) {
    height: 100%;
  }

  :deep(.ant-tabs-tabpane) {
    height: 100%;
    overflow: auto;
  }
}
</style>
