<script setup lang="ts">
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import PageCard from '@/components/PageCard.vue'
import ModelStatusTag from '@/components/ModelStatusTag.vue'
import { useModelsStore } from '@/stores/models'
import {
  getFineTune,
} from '@/api/modules/fineTune'
import { getTrainingLogFiles, getTrainingLogContent } from '@/api/modules/log'
import { getDataset } from '@/api/modules/dataset'
import {
  TRAIN_TYPE_MAP,
  TRAIN_METHOD_MAP,
  DATASET_TYPE_MAP,
  STORAGE_MAP,
} from '@/constants'
import type { EChartsOption } from 'echarts'
import type { FineTuneTask, DatasetItem } from '@/types'

const route = useRoute()
const taskId = route.params.id as string
const modelsStore = useModelsStore()

const task = ref<FineTuneTask | null>(null)
const dataset = ref<DatasetItem | null>(null)
const logContent = ref('')
const logFiles = ref<{ file: string; name: string; pid?: number }[]>([])

/** 初始加载状态：首次数据返回前显示 loading，避免空白闪烁 */
const loading = ref(true)

// 训练指标数据（ECharts 接收 number[]，下标即 step）
const metricData = reactive({
  loss: [] as number[],
  gradNorm: [] as number[],
  lr: [] as number[],
})

const summary = reactive({
  epoch: '',
  trainLoss: '',
  runtime: '',
})

/** 训练参数折叠状态（默认收起，点击展开查看全部参数） */
const paramsExpanded = ref(false)

let timer: ReturnType<typeof setInterval> | null = null

/** 从日志解析训练指标（移植自原项目正则） */
function parseMetricsFromLog(text: string) {
  const regex = /\{'loss':\s*([\d.]+),\s*'grad_norm':\s*([\d.]+),\s*'learning_rate':\s*([\d.e+-]+),\s*'epoch':\s*([\d.]+)\}/g
  let match
  while ((match = regex.exec(text)) !== null) {
    metricData.loss.push(parseFloat(match[1]))
    metricData.gradNorm.push(parseFloat(match[2]))
    metricData.lr.push(parseFloat(match[3]))
  }
}

/** 解析训练汇总 */
function parseTrainSummary(text: string) {
  const block = text.match(/\*\*\*\*\* train metrics \*\*\*\*\*([\s\S]*?)(?:\n\n|$)/)
  if (!block) return
  const body = block[1]
  const epoch = body.match(/'epoch':\s*([\d.]+)/)
  const loss = body.match(/'train_loss':\s*([\d.]+)/)
  const runtime = body.match(/'train_runtime':\s*([\d.]+)/)
  summary.epoch = epoch ? epoch[1] : ''
  summary.trainLoss = loss ? loss[1] : ''
  summary.runtime = runtime ? runtime[1] : ''
}

/** 构建单条曲线的 ECharts 配置（渐变填充 + 十字线 tooltip + dataZoom） */
function buildLineOption(label: string, data: number[], color: string, logScale = false): EChartsOption {
  const hasData = data.length > 0
  return {
    grid: { top: 24, right: 20, bottom: 56, left: 56 },
    tooltip: {
      trigger: 'axis',
      axisPointer: { type: 'cross' },
      backgroundColor: 'rgba(15, 23, 42, 0.9)',
      borderWidth: 0,
      textStyle: { color: '#fff', fontSize: 12 },
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      name: 'Step',
      nameTextStyle: { color: '#94a3b8', fontSize: 11 },
      axisLine: { lineStyle: { color: '#e2e8f0' } },
      axisLabel: { color: '#94a3b8', fontSize: 11 },
      splitLine: { show: false },
    },
    yAxis: {
      type: logScale ? 'log' : 'value',
      name: label,
      nameTextStyle: { color: '#94a3b8', fontSize: 11 },
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: { color: '#94a3b8', fontSize: 11 },
      splitLine: { lineStyle: { color: '#f1f5f9' } },
    },
    dataZoom: hasData && data.length > 30
      ? [
          { type: 'inside', start: 0, end: 100 },
          { type: 'slider', height: 16, bottom: 8, borderColor: 'transparent', fillerColor: 'rgba(79,70,229,0.08)', handleStyle: { color: '#4f46e5' } },
        ]
      : [],
    series: [
      {
        name: label,
        type: 'line',
        data,
        smooth: true,
        symbol: 'none',
        lineStyle: { width: 2, color },
        areaStyle: {
          color: {
            type: 'linear',
            x: 0, y: 0, x2: 0, y2: 1,
            colorStops: [
              { offset: 0, color: color + '55' },
              { offset: 1, color: color + '05' },
            ],
          },
        },
      },
    ],
  }
}

/** 三个曲线的 ECharts 配置（响应式，数据变化自动重绘） */
const lossChartOption = computed(() => buildLineOption('Loss', metricData.loss, '#f56c6c'))
const gradChartOption = computed(() => buildLineOption('Grad Norm', metricData.gradNorm, '#1890ff'))
const lrChartOption = computed(() => buildLineOption('Learning Rate', metricData.lr, '#67c23a', true))

async function loadTask() {
  try {
    const t = await getFineTune(taskId)
    task.value = t
    // 基座模型解析依赖 store 中的列表
    modelsStore.load()
    // 加载训练数据集
    if (t?.train_dataset_id) {
      loadDataset(t.train_dataset_id)
    } else {
      dataset.value = null
    }
  } catch {
    // ignore
  }
}

async function loadDataset(datasetId: string | number) {
  try {
    dataset.value = (await getDataset(datasetId)) || null
  } catch {
    dataset.value = null
  }
}

/** 格式化时间戳为本地字符串 */
function formatDateTime(value?: string) {
  if (!value) return '-'
  const date = new Date(value)
  if (Number.isNaN(date.getTime())) return value
  return date.toLocaleString('zh-CN', { hour12: false })
}

/** 当前任务是否为 LoRA 系列训练方法（lora/qlora/adalora/longlora 等） */
const isLoraMethod = computed(() =>
  String(task.value?.train_method || '').toLowerCase().includes('lora'),
)

async function loadLog() {
  try {
    const files = (await getTrainingLogFiles()) || []
    logFiles.value = files
    // 按 PID 匹配日志文件
    const matched =
      files.find((f) => f.pid && task.value?.process_id && (f.file.includes(`_${f.pid}_`) || f.file.endsWith(`_${f.pid}.log`))) ||
      files.find((f) => task.value?.name && f.name.includes(task.value.name)) ||
      files[0]
    if (matched) {
      const res = await getTrainingLogContent(matched.file)
      logContent.value = res.content || ''
      // 解析指标
      metricData.loss = []
      metricData.gradNorm = []
      metricData.lr = []
      parseMetricsFromLog(logContent.value)
      parseTrainSummary(logContent.value)
    }
  } catch {
    // ignore
  }
}

async function refreshAll() {
  try {
    await Promise.all([loadTask(), loadLog()])
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  refreshAll()
  timer = setInterval(refreshAll, 5000)
})

onUnmounted(() => {
  if (timer) clearInterval(timer)
})
</script>

<template>
  <div class="training-log">
    <!-- 初始加载骨架 -->
    <div v-if="loading" v-loading="true" element-loading-text="正在加载训练详情…" class="loading-wrap" />

    <!-- 详情内容 -->
    <template v-else>
    <!-- 任务档案概览 -->
    <section class="task-overview" aria-labelledby="task-title">
      <header class="task-header-main">
        <div class="task-identity">
          <div class="task-title-row">
            <h2 id="task-title" class="task-name">{{ task?.name || '加载中…' }}</h2>
            <ModelStatusTag :status="task?.status" />
          </div>
          <p class="task-meta">
            <span>任务标识：{{ task?.name || '未配置' }}</span>
            <span class="task-meta-sep" aria-hidden="true">·</span>
            <span>类型：模型微调任务</span>
            <span class="task-meta-sep" aria-hidden="true">·</span>
            <span>创建时间：{{ formatDateTime(task?.create_time) }}</span>
          </p>
        </div>
      </header>

      <div class="overview-layout">
        <div class="overview-main">
          <section class="profile-section task-profile" aria-labelledby="task-info-title">
            <div class="profile-section-header">
              <i class="fa fa-tag" aria-hidden="true" />
              <h3 id="task-info-title">任务信息</h3>
            </div>
            <dl class="profile-list">
              <div class="profile-row">
                <dt>基座模型</dt>
                <dd>{{ task?.base_model != null ? modelsStore.getModelName(task.base_model) : '未配置' }}</dd>
              </div>
              <div class="profile-row">
                <dt>输出模型</dt>
                <dd>
                  {{ task?.output_model_name || '暂未生成' }}
                </dd>
              </div>
            </dl>
          </section>

          <section class="profile-section dataset-profile" aria-labelledby="dataset-info-title">
            <div class="profile-section-header">
              <i class="fa fa-database" aria-hidden="true" />
              <h3 id="dataset-info-title">训练数据集</h3>
            </div>

            <template v-if="!task?.train_dataset_id">
              <el-empty description="该任务未关联训练数据集" :image-size="72" />
            </template>
            <template v-else-if="!dataset">
              <el-skeleton :rows="3" animated />
            </template>
            <template v-else>
              <div class="dataset-summary">
                <div class="dataset-copy">
                  <div class="dataset-title-row">
                    <strong class="dataset-name">{{ dataset.name || '未命名数据集' }}</strong>
                    <el-tag size="small" type="primary" effect="plain">
                      {{ DATASET_TYPE_MAP[dataset.type] || dataset.type || '未配置' }}
                    </el-tag>
                  </div>
                  <p class="dataset-description">{{ dataset.description || '暂无数据集描述' }}</p>
                </div>

                <dl class="dataset-metrics">
                  <div class="dataset-metric">
                    <i class="fa fa-file-text-o" aria-hidden="true" />
                    <div>
                      <dt>数据条数</dt>
                      <dd>{{ dataset.count?.toLocaleString('zh-CN') ?? '未配置' }} 条</dd>
                    </div>
                  </div>
                  <div class="dataset-metric">
                    <i class="fa fa-database" aria-hidden="true" />
                    <div>
                      <dt>数据大小</dt>
                      <dd>{{ dataset.size || '未配置' }}</dd>
                    </div>
                  </div>
                </dl>
              </div>
            </template>
          </section>
        </div>

        <aside class="runtime-panel" aria-labelledby="runtime-title">
          <div class="profile-section-header">
            <i class="fa fa-line-chart" aria-hidden="true" />
            <h3 id="runtime-title">运行概况</h3>
          </div>
          <dl class="runtime-list">
            <div class="runtime-item">
              <i class="fa fa-calendar" aria-hidden="true" />
              <div>
                <dt>训练开始时间</dt>
                <dd>{{ formatDateTime(task?.create_time) }}</dd>
              </div>
            </div>
            <div class="runtime-item">
              <i class="fa fa-clock-o" aria-hidden="true" />
              <div>
                <dt>训练时长</dt>
                <dd>{{ task?.train_duration || '未配置' }}</dd>
              </div>
            </div>
            <div class="runtime-item">
              <i class="fa fa-hdd-o" aria-hidden="true" />
              <div>
                <dt>存储位置</dt>
                <dd>{{ STORAGE_MAP[dataset?.storage_type || ''] || dataset?.storage_type || '未配置' }}</dd>
              </div>
            </div>
            <div class="runtime-item">
              <i class="fa fa-graduation-cap" aria-hidden="true" />
              <div>
                <dt>训练方式</dt>
                <dd>{{ task?.train_type ? (TRAIN_TYPE_MAP[task.train_type] || task.train_type) : '未配置' }}</dd>
              </div>
            </div>
            <div class="runtime-item">
              <i class="fa fa-sliders" aria-hidden="true" />
              <div>
                <dt>训练方法</dt>
                <dd>{{ task?.train_method ? (TRAIN_METHOD_MAP[task.train_method] || task.train_method) : '未配置' }}</dd>
              </div>
            </div>
          </dl>
        </aside>
      </div>
    </section>

    <!-- 训练参数 & LoRA 参数（合并卡片，默认折叠） -->
    <PageCard class="parameters-card" :class="{ 'is-collapsed': !paramsExpanded }">
      <template #header>
        <button
          type="button"
          class="params-toggle-button"
          :aria-expanded="paramsExpanded"
          aria-controls="training-parameter-content"
          @click="paramsExpanded = !paramsExpanded"
        >
          <span class="params-title">
            <i class="fa fa-cog" aria-hidden="true" />
            <span class="params-title-text">训练参数</span>
            <el-tag v-if="isLoraMethod" size="small" type="primary" effect="plain" class="params-badge">LoRA</el-tag>
          </span>
          <span class="params-meta">
            <span class="params-summary">
              {{ task?.train_method ? (TRAIN_METHOD_MAP[task.train_method] || task.train_method) : '未配置' }}
              <span class="params-sep">·</span>
              Batch {{ task?.batch_size ?? '未配置' }}
              <span class="params-sep">·</span>
              LR {{ task?.learning_rate ?? '未配置' }}
              <span class="params-sep">·</span>
              {{ task?.n_epochs ?? '未配置' }} Epochs
            </span>
            <i class="fa params-toggle" :class="paramsExpanded ? 'fa-angle-up' : 'fa-angle-down'" aria-hidden="true" />
          </span>
        </button>
      </template>

      <el-collapse-transition>
        <div v-show="paramsExpanded" id="training-parameter-content" class="params-body">
          <div class="parameter-groups">
            <section class="parameter-group base-parameter-group" aria-label="基础训练参数">
              <dl class="parameter-grid">
                <div class="parameter-item">
                  <dt>训练方式</dt>
                  <dd>{{ task?.train_type ? (TRAIN_TYPE_MAP[task.train_type] || task.train_type) : '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>训练方法</dt>
                  <dd>{{ task?.train_method ? (TRAIN_METHOD_MAP[task.train_method] || task.train_method) : '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>Batch Size</dt>
                  <dd :class="{ 'is-muted': task?.batch_size == null }">{{ task?.batch_size ?? '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>Learning Rate</dt>
                  <dd :class="{ 'is-muted': task?.learning_rate == null }">{{ task?.learning_rate ?? '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>训练轮数（Epochs）</dt>
                  <dd :class="{ 'is-muted': task?.n_epochs == null }">{{ task?.n_epochs ?? '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>保存间隔（Save Steps）</dt>
                  <dd :class="{ 'is-muted': task?.save_steps == null }">{{ task?.save_steps ?? '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>学习率调度器</dt>
                  <dd :class="{ 'is-muted': !task?.lr_scheduler_type }">{{ task?.lr_scheduler_type || '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>最大序列长度</dt>
                  <dd :class="{ 'is-muted': task?.max_length == null }">{{ task?.max_length ?? '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>权重衰减（Weight Decay）</dt>
                  <dd :class="{ 'is-muted': task?.weight_decay == null }">{{ task?.weight_decay ?? '未配置' }}</dd>
                </div>
              </dl>
            </section>

            <section v-if="isLoraMethod" class="parameter-group lora-parameter-group" aria-labelledby="lora-params-title">
              <h3 id="lora-params-title" class="params-section-title">
                <i class="fa fa-clone" aria-hidden="true" />
                LoRA 参数
              </h3>
              <dl class="parameter-grid">
                <div class="parameter-item">
                  <dt>LoRA Rank</dt>
                  <dd :class="{ 'is-muted': task?.lora_rank == null }">{{ task?.lora_rank ?? '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>LoRA Alpha</dt>
                  <dd :class="{ 'is-muted': task?.lora_alpha == null }">{{ task?.lora_alpha ?? '未配置' }}</dd>
                </div>
                <div class="parameter-item">
                  <dt>LoRA Dropout</dt>
                  <dd :class="{ 'is-muted': task?.lora_dropout == null }">{{ task?.lora_dropout ?? '未配置' }}</dd>
                </div>
              </dl>
            </section>
          </div>
        </div>
      </el-collapse-transition>
    </PageCard>

    <!-- 训练曲线 -->
    <el-row :gutter="16" class="chart-row">
      <el-col :span="8">
        <el-card shadow="never" class="chart-card">
          <template #header>
            <div class="chart-card-header">
              <span class="chart-card-title"><i class="fa fa-line-chart" /> Loss 曲线</span>
            </div>
          </template>
          <VChart class="chart-canvas" :option="lossChartOption" autoresize />
        </el-card>
      </el-col>
      <el-col :span="8">
        <el-card shadow="never" class="chart-card">
          <template #header>
            <div class="chart-card-header">
              <span class="chart-card-title"><i class="fa fa-tachometer" /> 梯度范数</span>
            </div>
          </template>
          <VChart class="chart-canvas" :option="gradChartOption" autoresize />
        </el-card>
      </el-col>
      <el-col :span="8">
        <el-card shadow="never" class="chart-card">
          <template #header>
            <div class="chart-card-header">
              <span class="chart-card-title"><i class="fa fa-flask" /> 学习率（对数轴）</span>
            </div>
          </template>
          <VChart class="chart-canvas" :option="lrChartOption" autoresize />
        </el-card>
      </el-col>
    </el-row>

    <!-- 训练汇总 -->
    <el-card v-if="summary.trainLoss" shadow="never" class="summary-card">
      <template #header>
        <div class="chart-card-header">
          <span class="chart-card-title"><i class="fa fa-bar-chart" /> 训练汇总</span>
        </div>
      </template>
      <el-descriptions :column="3" border size="small">
        <el-descriptions-item label="Epoch">{{ summary.epoch }}</el-descriptions-item>
        <el-descriptions-item label="训练 Loss">{{ summary.trainLoss }}</el-descriptions-item>
        <el-descriptions-item label="训练时长">{{ summary.runtime }}s</el-descriptions-item>
      </el-descriptions>
    </el-card>

    <!-- 原始日志 -->
    <el-card shadow="never" class="log-card">
      <template #header>
        <div class="chart-card-header">
          <span class="chart-card-title"><i class="fa fa-terminal" /> 训练日志输出</span>
        </div>
      </template>
      <pre class="log-pre">{{ logContent || '暂无日志' }}</pre>
    </el-card>
    </template>
  </div>
</template>

<style scoped lang="scss">
.training-log {
  /* 企业级间距与排版 */
}

/* ===== 初始加载 ===== */
.loading-wrap {
  min-height: 320px;
  border-radius: 12px;
  background: var(--app-surface-bg);
  box-shadow: var(--card-shadow);
}

/* ===== 任务概览 ===== */
.task-overview {
  margin-bottom: 24px;
}

.task-header-main {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 18px;
}

.task-identity {
  min-width: 0;
}

.task-title-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.task-name {
  margin: 0;
  font-size: 24px;
  line-height: 1.25;
  font-weight: 700;
  color: #0f172a;
  letter-spacing: -0.02em;
}

.task-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 8px 0 0;
  color: #64748b;
  font-size: 13px;
  line-height: 1.5;
}

.task-meta-sep {
  color: #cbd5e1;
}

.overview-layout {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(280px, 0.9fr);
  gap: 24px;
  align-items: stretch;
}

.overview-main {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 16px;
}

.profile-section,
.runtime-panel {
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  background: var(--app-surface-bg);
}

.profile-section {
  padding: 20px 24px;
}

.profile-section-header {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 24px;
  margin-bottom: 14px;
  color: #0f172a;
}

.profile-section-header h3 {
  margin: 0;
  font-size: 16px;
  line-height: 1.5;
  font-weight: 600;
}

.profile-section-header > .fa {
  width: 18px;
  color: var(--el-color-primary);
  font-size: 16px;
  text-align: center;
}

.profile-list,
.runtime-list,
.dataset-metrics,
.parameter-grid {
  margin: 0;
}

.profile-row {
  display: grid;
  grid-template-columns: minmax(120px, 0.42fr) minmax(0, 1fr);
  gap: 20px;
  align-items: center;
  min-height: 56px;
  border-top: 1px solid #f1f5f9;
}

.profile-row dt,
.runtime-item dt,
.dataset-metric dt,
.parameter-item dt {
  color: #64748b;
  font-size: 13px;
  line-height: 1.45;
}

.profile-row dd,
.runtime-item dd,
.dataset-metric dd,
.parameter-item dd {
  margin: 0;
  color: #0f172a;
  font-size: 14px;
  line-height: 1.5;
  font-weight: 500;
  overflow-wrap: anywhere;
}

.is-muted {
  color: #64748b !important;
  font-weight: 400 !important;
}

.dataset-title-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.dataset-name {
  color: #0f172a;
  font-size: 16px;
  line-height: 1.5;
  font-weight: 600;
}

.dataset-description {
  margin: 6px 0 16px;
  color: #64748b;
  font-size: 13px;
  line-height: 1.6;
}

.dataset-metrics {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-radius: 8px;
  background: #f8fafc;
}

.dataset-metric {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 72px;
  padding: 14px 18px;
}

.dataset-metric + .dataset-metric {
  border-left: 1px solid #e2e8f0;
}

.dataset-metric > .fa {
  width: 28px;
  color: var(--el-color-primary);
  font-size: 21px;
  text-align: center;
}

.dataset-metric dd {
  margin-top: 3px;
  font-size: 16px;
  font-variant-numeric: tabular-nums;
}

.runtime-panel {
  padding: 20px 24px;
}

.runtime-list {
  display: flex;
  flex-direction: column;
}

.runtime-item {
  display: flex;
  align-items: flex-start;
  gap: 13px;
  min-height: 64px;
  padding: 13px 0;
  border-top: 1px solid #f1f5f9;
}

.runtime-item > .fa {
  width: 18px;
  margin-top: 3px;
  color: var(--el-color-primary);
  font-size: 16px;
  text-align: center;
}

.runtime-item dd {
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
}

/* ===== 训练参数折叠卡片 ===== */
.parameters-card {
  margin-bottom: 24px;
}

.parameters-card :deep(.page-card-header) {
  margin-bottom: 0;
}

.params-toggle-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  min-height: 44px;
  margin: -8px 0;
  padding: 8px 0;
  border: 0;
  border-radius: 8px;
  outline: none;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.params-toggle-button:hover {
  background: #f8fafc;
}

.params-toggle-button:focus-visible {
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.18);
}

.params-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.params-title > .fa {
  width: 18px;
  color: var(--el-color-primary);
  font-size: 16px;
  text-align: center;
}

.params-title-text {
  font-size: 16px;
  font-weight: 600;
  color: #0f172a;
}

.params-badge {
  font-weight: 600;
  letter-spacing: 0.3px;
}

.params-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.params-summary {
  font-size: 13px;
  color: #64748b;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.params-sep {
  margin: 0 4px;
  color: #dcdfe6;
}

.params-toggle {
  width: 18px;
  color: #64748b;
  font-size: 14px;
  text-align: center;
  flex-shrink: 0;
}

.params-body {
  padding-top: 22px;
}

.params-section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 24px;
  margin: 0 0 8px;
  color: #0f172a;
  font-size: 15px;
  font-weight: 600;
}

.params-section-title > .fa {
  width: 18px;
  color: var(--el-color-primary);
  font-size: 14px;
  text-align: center;
}

.parameter-groups {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.parameter-group + .parameter-group {
  padding-top: 22px;
  border-top: 1px solid #e2e8f0;
}

.parameter-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 48px;
}

.parameter-item {
  display: grid;
  grid-template-columns: minmax(150px, 0.72fr) minmax(0, 1fr);
  gap: 18px;
  align-items: center;
  min-height: 52px;
  border-bottom: 1px solid #f1f5f9;
}

.parameter-item dd {
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1100px) {
  .overview-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .runtime-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 32px;
  }

  .parameter-grid {
    column-gap: 28px;
  }
}

@media (max-width: 700px) {
  .task-name {
    font-size: 21px;
  }

  .task-meta-sep {
    display: none;
  }

  .task-meta span:not(.task-meta-sep) {
    width: 100%;
  }

  .profile-section,
  .runtime-panel {
    padding: 18px;
  }

  .profile-row,
  .parameter-item {
    grid-template-columns: minmax(0, 1fr);
    gap: 5px;
    align-content: center;
    padding: 12px 0;
  }

  .dataset-metrics,
  .runtime-list,
  .parameter-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .dataset-metric + .dataset-metric {
    border-top: 1px solid #e2e8f0;
    border-left: 0;
  }

  .params-toggle-button {
    align-items: flex-start;
  }

  .params-summary {
    display: none;
  }
}

/* ===== 图表卡片 ===== */
.chart-row {
  margin-bottom: 16px;
}

.chart-card {
  height: 100%;
}

.chart-card :deep(.el-card__header) {
  padding: 14px 18px;
  border-bottom: 1px solid #f1f5f9;
}

.chart-card :deep(.el-card__body) {
  padding: 16px 18px;
}

.chart-canvas {
  width: 100%;
  height: 280px;
}

.chart-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.chart-card-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: #303133;

  .fa {
    color: var(--el-color-primary);
    font-size: 13px;
  }
}

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

.summary-card :deep(.el-card__header) {
  padding: 14px 18px;
  border-bottom: 1px solid #f1f5f9;
}

/* ===== 原始日志 ===== */
.log-card :deep(.el-card__header) {
  padding: 14px 18px;
  border-bottom: 1px solid #f1f5f9;
}

.log-pre {
  margin: 0;
  padding: 16px 18px;
  background: #1e1e1e;
  color: #d4d4d4;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace;
  font-size: 12px;
  line-height: 1.65;
  max-height: 500px;
  overflow: auto;
  white-space: pre-wrap;
  word-wrap: break-word;
  border-radius: 8px;
}
</style>
