<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import PageCard from '@/components/PageCard.vue'
import { usePolling } from '@/composables/usePolling'
import {
  getDataProcessProgress,
  getDataProcessResults,
  getDataProcessTask,
  publishDataProcess,
  restoreDataProcessResult,
  updateDataProcessResult,
} from '@/api/modules/dataProcess'
import type {
  DataProcessDatasetSplit,
  DataProcessPublishPayload,
  DataProcessResult,
  DataProcessResultStatus,
  DataProcessTask,
  DataProcessType,
} from '@/types/dataProcess'
import { dataProcessDisplayStatus } from '@/utils/dataProcessStatus.js'

const route = useRoute()
const router = useRouter()
const taskId = computed(() => String(route.params.id || ''))
const detail = ref<DataProcessTask | null>(null)
const loading = ref(true)
const loadError = ref('')
const results = ref<DataProcessResult[]>([])
const resultTotal = ref(0)
const resultLoading = ref(false)
const resultError = ref('')
const keyword = ref('')
const statusFilter = ref('')
const currentPage = ref(1)
const pageSize = ref(10)
const editingResult = ref<DataProcessResult | null>(null)
const editDialogVisible = ref(false)
const savingResult = ref(false)
const restoringResultId = ref<string | number | null>(null)
const publishDialogVisible = ref(false)
const publishing = ref(false)
const configExpanded = ref(false)
const resultCellTooltipOptions = {
  popperClass: 'data-process-result-tooltip',
  placement: 'top',
  showAfter: 300,
} as const
let resultFilterTimer: ReturnType<typeof setTimeout> | null = null

const editForm = reactive({ instruction: '', input: '', output: '' })
const publishForm = reactive<DataProcessPublishPayload>({
  dataset_name: '',
  dataset_type: 'train',
  storage_type: 'local',
  split: { train: 80, validation: 10, test: 10 },
  format: 'alpaca_jsonl',
})

const processTypeMap: Record<DataProcessType, string> = {
  structured: '结构化数据',
  unstructured: '非结构化数据',
  external: '外来数据源拉取',
}

const configLabelMap: Record<string, string> = {
  preprocess_options: '预处理规则',
  dataset_split: '数据集划分',
  generation_model_id: '数据生成模型',
  generation_prompt: '生成提示语',
  output_type: '输出类型',
  reasoning_detail: '推理详细程度',
  temperature: '生成温度',
  max_tokens: '最大输出长度',
  json_mode: 'JSON 输出',
  quality_filter_enabled: '质量筛选',
  filter_low_quality: '过滤低质量内容',
  filter_short_content: '过滤过短内容',
  min_output_length: '最少输出字数',
  semantic_enrichment: '语义增强',
  qa_pairs_per_row: '每行生成数量',
  qa_pairs_per_chunk: '每切片生成数量',
  chunk_method: '切分方式',
  chunk_size: '切片长度',
  chunk_overlap: '重叠长度',
  min_chunk_size: '最小切片长度',
  semantic_breakpoint_percentile: '语义断点百分位',
  preserve_tables: '保留表格',
  preserve_code_blocks: '保留代码块',
  preserve_lists: '保留列表',
}

const chunkMethodLabelMap: Record<string, string> = {
  layout_hybrid: '版面结构混合切分',
  semantic: '语义切分',
  fixed: '固定 Token',
}

const preprocessOptionLabelMap: Record<string, string> = {
  clean_invalid: '清理无效数据',
  detect_structure: '嵌套结构展平',
  deduplicate: '重复记录去重',
  normalize_format: '数据格式标准化',
  filter_anomaly: '异常数据过滤',
  desensitize: '敏感信息脱敏',
  clean_invalid_content: '清理无效内容',
  detect_document_structure: '识别文档结构',
  merge_short_content: '合并过短内容',
  filter_low_quality: '过滤低质量内容',
  deduplicate_content: '重复内容去重',
  preserve_context: '保留上下文',
}

function numeric(value: unknown) {
  const parsed = typeof value === 'number' ? value : Number(value)
  return Number.isFinite(parsed) ? parsed : 0
}

function formatDateTime(value?: string | null) {
  if (!value) return '-'
  const date = new Date(value)
  return Number.isNaN(date.getTime()) ? value : date.toLocaleString('zh-CN', { hour12: false })
}

const retentionRate = computed(() => {
  const outputCount = numeric(detail.value?.output_count)
  const denominator = outputCount + numeric(detail.value?.filtered_count)
  return denominator
    ? Number(((outputCount / denominator) * 100).toFixed(1))
    : 0
})

const retentionDenominator = computed(() => (
  numeric(detail.value?.output_count) + numeric(detail.value?.filtered_count)
))
const retentionPercentage = computed(() => Math.min(100, Math.max(0, retentionRate.value)))
const progressPercentage = computed(() => (
  detail.value?.status === 'completed'
    ? 100
    : Math.min(100, Math.max(0, numeric(detail.value?.progress)))
))
const isUnstructured = computed(() => detail.value?.process_type === 'unstructured')
const sourceFileCount = computed(() => (
  numeric(detail.value?.source_file_count) || detail.value?.source_files?.length || 0
))
const previewCount = computed(() => numeric(detail.value?.preview_count))
const inputMetricLabel = computed(() => isUnstructured.value ? '输入切片' : '输入记录')
const inputMetricCount = computed(() => (
  isUnstructured.value ? previewCount.value : numeric(detail.value?.input_count)
))
const sourceDatasetName = computed(() => (
  detail.value?.source_dataset_name
  || detail.value?.source_dataset
  || detail.value?.source_files?.map((file) => file.name).join('、')
  || '源文件上传'
))
const outputDatasets = computed(() => {
  return detail.value?.output_datasets || []
})
const outputDatasetName = computed(() => (
  outputDatasets.value.map((item) => item.name).join('、')
))
const outputDatasetId = computed(() => detail.value?.output_dataset_id || null)
const hasPublishedOutputs = computed(() => outputDatasets.value.length > 0)
const hasCurrentPublishedDataset = computed(() => (
  Boolean(outputDatasetId.value)
  && outputDatasets.value.some((dataset) => String(dataset.id) === String(outputDatasetId.value))
))
const displayStatus = computed(() => dataProcessDisplayStatus({
  status: detail.value?.status || 'pending',
  output_dataset_id: hasCurrentPublishedDataset.value ? outputDatasetId.value : null,
}))
const inputMetricUnit = computed(() => isUnstructured.value ? '个' : '条')
const outputSummary = computed(() => {
  if (hasCurrentPublishedDataset.value) return outputDatasetName.value || '当前轮已发布'
  if (outputDatasetId.value) return '已发布数据集不可用，请重新发布'
  if (hasPublishedOutputs.value) {
    return `当前轮尚未发布；上次已发布 ${outputDatasets.value.length} 个数据集，仍可正常使用`
  }
  return detail.value?.status === 'completed' ? '当前轮尚未发布' : '尚未发布为数据集'
})
const resultEmptyTitle = computed(() => {
  if (detail.value?.status === 'completed') return '没有符合条件的结果'
  if (detail.value?.status === 'failed') return '任务生成失败，未生成结果明细'
  if (detail.value?.status === 'stopped') return '任务已停止，暂无生成结果'
  return '处理完成后将在这里展示结果明细'
})
const resultEmptyDescription = computed(() => (
  detail.value?.status === 'completed'
    ? '请调整搜索或筛选条件'
    : `当前任务状态：${displayStatus.value.label}`
))
const canRegenerate = computed(() => {
  const status = detail.value?.status
  return status === 'pending'
    || status === 'failed'
    || status === 'stopped'
    || (status === 'completed' && (Boolean(outputDatasetId.value) || hasPublishedOutputs.value))
})
const creatorName = computed(() => detail.value?.creator_name || detail.value?.creator || '-')
const createTime = computed(() => detail.value?.create_time || detail.value?.created_at)
const startTime = computed(() => detail.value?.start_time || detail.value?.started_at)
const completeTime = computed(() => detail.value?.complete_time || detail.value?.completed_at)

const durationText = computed(() => {
  if (detail.value?.duration) return detail.value.duration
  const providedDuration = detail.value?.duration_seconds
  let seconds = providedDuration == null ? null : numeric(providedDuration)
  if (seconds == null && startTime.value) {
    const started = new Date(startTime.value).getTime()
    const finished = completeTime.value
      ? new Date(completeTime.value).getTime()
      : detail.value?.status === 'running' ? Date.now() : Number.NaN
    if (Number.isFinite(started) && Number.isFinite(finished) && finished >= started) {
      seconds = (finished - started) / 1000
    }
  }
  if (seconds == null) return detail.value?.status === 'running' ? '处理中' : '-'
  const safeSeconds = Math.max(0, Math.round(seconds))
  const hours = Math.floor(safeSeconds / 3600)
  const minutes = Math.floor(safeSeconds / 60)
  const restSeconds = safeSeconds % 60
  if (hours) return `${hours} 小时 ${minutes % 60} 分 ${restSeconds} 秒`
  return minutes ? `${minutes} 分 ${restSeconds} 秒` : `${restSeconds} 秒`
})

const configRows = computed(() => {
  const config = detail.value?.config || {}
  return Object.entries(config)
    .filter(([key]) => (
      key !== 'generation_model_snapshot'
      && (key !== 'reasoning_detail' || config.output_type === 'reasoning')
      && !/(?:password|secret|token|api_key)/i.test(key)
    ))
    .map(([key, value]) => ({
      label: configLabelMap[key] || key.split('_').join(' '),
      value: formatConfigValue(key, value),
    }))
})

function formatConfigValue(key: string, value: unknown) {
  if (key === 'generation_model_id') {
    const snapshot = detail.value?.config?.generation_model_snapshot
    if (snapshot && typeof snapshot === 'object' && !Array.isArray(snapshot)) {
      const model = snapshot as Record<string, unknown>
      return String(model.name || model.display_name || model.model_name || value || '-')
    }
  }
  if (key === 'chunk_method' && typeof value === 'string') {
    return chunkMethodLabelMap[value] || value
  }
  if (key === 'output_type') {
    return value === 'reasoning' ? '思维链回答' : '标准回答'
  }
  if (key === 'reasoning_detail') {
    return value === 'detailed' ? '详细推理' : '普通推理'
  }
  if (key === 'dataset_split' && value && typeof value === 'object') {
    const split = value as Partial<DataProcessDatasetSplit>
    return `训练集 ${split.train ?? 0}% / 验证集 ${split.validation ?? 0}% / 测试集 ${split.test ?? 0}%`
  }
  if (Array.isArray(value)) {
    if (key === 'preprocess_options') {
      return value.length
        ? value.map((item) => preprocessOptionLabelMap[String(item)] || String(item)).join('、')
        : '-'
    }
    return value.length ? value.join('、') : '-'
  }
  if (typeof value === 'boolean') return value ? '是' : '否'
  if (value && typeof value === 'object') return JSON.stringify(value)
  return value == null || value === '' ? '-' : String(value)
}

function processTypeLabel(value?: DataProcessType) {
  return value ? processTypeMap[value] || value : '-'
}

function isActiveStatus(status?: DataProcessTask['status']) {
  return status === 'running'
}

async function loadTask(silent = false) {
  if (!silent) loading.value = true
  loadError.value = ''
  try {
    detail.value = await getDataProcessTask(taskId.value)
  } catch {
    detail.value = null
    loadError.value = '数据处理任务加载失败，任务可能已删除或当前无权访问。'
  } finally {
    if (!silent) loading.value = false
  }
}

async function loadResults() {
  resultLoading.value = true
  resultError.value = ''
  try {
    const response = await getDataProcessResults(taskId.value, {
      page: currentPage.value,
      page_size: pageSize.value,
      keyword: keyword.value.trim() || undefined,
      status: statusFilter.value || undefined,
    })
    results.value = response.items
    resultTotal.value = response.total
  } catch {
    results.value = []
    resultTotal.value = 0
    resultError.value = '结果明细加载失败，请稍后重试。'
  } finally {
    resultLoading.value = false
  }
}

async function loadPage() {
  loading.value = true
  await loadTask(true)
  if (detail.value) {
    await loadResults()
  } else {
    results.value = []
    resultTotal.value = 0
    resultError.value = ''
  }
  loading.value = false
  if (isActiveStatus(detail.value?.status)) startPolling()
  else stopPolling()
}

async function refreshRuntime() {
  if (!detail.value || !isActiveStatus(detail.value.status)) {
    stopPolling()
    return
  }

  const progress = await getDataProcessProgress(taskId.value)
  detail.value = {
    ...detail.value,
    status: progress.status,
    progress: progress.progress,
    input_count: progress.input_count ?? detail.value.input_count,
    output_count: progress.output_count ?? detail.value.output_count,
    filtered_count: progress.filtered_count ?? detail.value.filtered_count,
    duplicate_count: progress.duplicate_count ?? detail.value.duplicate_count,
    error_count: progress.error_count ?? detail.value.error_count,
    failure_reason: progress.failure_reason ?? detail.value.failure_reason,
  }

  if (!isActiveStatus(progress.status)) {
    stopPolling()
    await Promise.all([loadTask(true), loadResults()])
  }
}

const { start: startPolling, stop: stopPolling } = usePolling(refreshRuntime, 3000, {
  immediate: false,
})

function scheduleResultReload() {
  if (currentPage.value !== 1) {
    currentPage.value = 1
    return
  }
  if (resultFilterTimer) clearTimeout(resultFilterTimer)
  resultFilterTimer = setTimeout(() => void loadResults(), 300)
}

function resultStatusLabel(status: DataProcessResultStatus) {
  return status === 'valid' ? '有效' : status === 'modified' ? '已修改' : '无效'
}

function resultStatusType(status: DataProcessResultStatus) {
  return status === 'valid' ? 'success' : status === 'modified' ? 'warning' : 'danger'
}

function qualityScoreLabel(value: DataProcessResult['quality_score']) {
  if (value == null) return '-'
  const score = value.overall
  return Number.isFinite(score) ? Number(score).toFixed(1) : '-'
}

function qualityFlagsLabel(value: DataProcessResult['quality_score']) {
  return value?.flags?.length ? value.flags.join('、') : '未命中质量规则'
}

function replaceResult(updated: DataProcessResult) {
  const index = results.value.findIndex((item) => item.id === updated.id)
  if (index >= 0) results.value.splice(index, 1, updated)
}

function openResultEditor(result: DataProcessResult) {
  editingResult.value = result
  Object.assign(editForm, {
    instruction: result.instruction,
    input: result.input,
    output: result.output,
  })
  editDialogVisible.value = true
}

async function saveResult() {
  if (!editingResult.value) return
  if (!editForm.instruction.trim() || !editForm.output.trim()) {
    ElMessage.warning('Instruction 和 Output 不能为空')
    return
  }

  savingResult.value = true
  try {
    const updated = await updateDataProcessResult(taskId.value, editingResult.value.id, {
      instruction: editForm.instruction,
      input: editForm.input,
      output: editForm.output,
      expected_updated_at: editingResult.value.updated_at,
    })
    replaceResult(updated)
    await loadTask(true)
    editDialogVisible.value = false
    ElMessage.success('结果已保存')
  } catch {
    // 统一请求层已展示后端返回的失败原因。
  } finally {
    savingResult.value = false
  }
}

async function restoreResult(result: DataProcessResult) {
  try {
    await ElMessageBox.confirm('确定恢复为模型最初生成的内容吗？', '恢复生成结果', {
      type: 'warning',
      confirmButtonText: '恢复',
      cancelButtonText: '取消',
    })
  } catch {
    return
  }

  restoringResultId.value = result.id
  try {
    const restored = await restoreDataProcessResult(taskId.value, result.id)
    replaceResult(restored)
    await loadTask(true)
    ElMessage.success('已恢复生成结果')
  } catch {
    // 统一请求层已展示后端返回的失败原因。
  } finally {
    restoringResultId.value = null
  }
}

function configuredSplit(): DataProcessDatasetSplit {
  const split = detail.value?.config?.dataset_split
  if (!split || typeof split !== 'object') return { train: 80, validation: 10, test: 10 }
  const value = split as Partial<DataProcessDatasetSplit>
  return {
    train: Number(value.train) || 0,
    validation: Number(value.validation) || 0,
    test: Number(value.test) || 0,
  }
}

function openPublishDialog() {
  if (!detail.value) return
  publishForm.dataset_name = `${detail.value.name}-数据集`
  publishForm.split = configuredSplit()
  publishDialogVisible.value = true
}

async function publishDataset() {
  if (!publishForm.dataset_name.trim()) {
    ElMessage.warning('请输入数据集名称')
    return
  }

  publishing.value = true
  try {
    const published = await publishDataProcess(taskId.value, {
      ...publishForm,
      dataset_name: publishForm.dataset_name.trim(),
      split: { ...publishForm.split },
    })
    const datasets = published.datasets || published.output_datasets || []
    ElMessage.success(`已发布 ${datasets.length || 3} 个独立数据集`)
    publishDialogVisible.value = false
    await router.push({ path: '/dataset', query: { tab: 'task', task_id: taskId.value } })
  } catch {
    // 统一请求层已展示后端返回的失败原因。
  } finally {
    publishing.value = false
  }
}

function startRegeneration() {
  void router.push({ name: 'data-process-regenerate', params: { id: taskId.value } })
}

watch([currentPage, pageSize], () => void loadResults())

onMounted(loadPage)
onBeforeUnmount(() => {
  if (resultFilterTimer) clearTimeout(resultFilterTimer)
})
</script>

<template>
  <PageCard class="data-process-detail-page" v-loading="loading">
    <template v-if="detail" #header>
      <div class="detail-heading">
        <div class="heading-row">
          <h1>{{ detail.name }}</h1>
          <el-tag :type="displayStatus.type" size="small" effect="light">
            {{ displayStatus.label }}
          </el-tag>
          <el-button
            v-if="detail.status === 'completed' && !hasCurrentPublishedDataset"
            class="publish-button"
            type="primary"
            @click="openPublishDialog"
          >
            <i class="fa fa-database" style="margin-right: 4px;" />发布为三个数据集
          </el-button>
          <el-button
            v-if="canRegenerate"
            class="publish-button"
            type="primary"
            @click="startRegeneration"
          >
            <i class="fa fa-refresh" style="margin-right: 4px;" />重新生成
          </el-button>
        </div>
        <p>{{ detail.description || '暂无任务描述' }}</p>
        <dl class="heading-meta">
          <div><dt>任务 ID</dt><dd>{{ detail.id }}</dd></div>
          <div><dt>处理类型</dt><dd>{{ processTypeLabel(detail.process_type) }}</dd></div>
          <div><dt>创建人</dt><dd>{{ creatorName }}</dd></div>
        </dl>
      </div>
    </template>

    <div v-if="loadError" class="not-found-state" role="alert">
      <i class="fa fa-exclamation-circle" aria-hidden="true" />
      <h2>无法加载数据处理任务</h2>
      <p>{{ loadError }}</p>
      <div class="load-state-actions">
        <el-button @click="router.push('/data-process')">返回任务列表</el-button>
        <el-button type="primary" @click="loadPage">重新加载</el-button>
      </div>
    </div>

    <template v-else-if="detail">
      <section class="metric-grid" aria-label="处理结果概览">
        <div class="metric-card">
          <span>处理耗时</span>
          <strong>{{ durationText }}</strong>
          <small>{{ completeTime ? `完成于 ${formatDateTime(completeTime)}` : `当前进度 ${progressPercentage}%` }}</small>
        </div>
        <div class="metric-card">
          <span>{{ inputMetricLabel }}</span>
          <strong>{{ inputMetricCount.toLocaleString() }} {{ inputMetricUnit }}</strong>
          <small>{{ sourceFileCount ? `源文件 ${sourceFileCount} 个：` : '来源：' }}{{ sourceDatasetName }}</small>
        </div>
        <div class="metric-card">
          <span>输出结果</span>
          <strong>{{ numeric(detail.output_count).toLocaleString() }} 条</strong>
          <small>{{ outputSummary }}</small>
        </div>
        <div class="metric-card is-primary">
          <span>结果保留率</span>
          <strong>{{ retentionDenominator ? `${retentionRate}%` : '-' }}</strong>
          <el-progress :percentage="retentionPercentage" :show-text="false" :stroke-width="5" />
        </div>
      </section>

      <div v-if="detail.failure_reason" class="failure-alert" role="alert">
        <i class="fa fa-exclamation-triangle" aria-hidden="true" />
        <div><strong>处理任务执行失败</strong><p>{{ detail.failure_reason }}</p></div>
      </div>

      <div class="detail-grid">
        <section class="detail-section" aria-labelledby="runtime-title">
          <div class="section-heading">
            <div><h2 id="runtime-title">运行信息</h2><p>查看任务执行时间与数据流向</p></div>
          </div>
          <dl class="info-list">
            <div><dt>创建时间</dt><dd>{{ formatDateTime(createTime) }}</dd></div>
            <div><dt>开始时间</dt><dd>{{ formatDateTime(startTime) }}</dd></div>
            <div><dt>完成时间</dt><dd>{{ formatDateTime(completeTime) }}</dd></div>
            <div><dt>处理耗时</dt><dd>{{ durationText }}</dd></div>
            <div><dt>源数据集</dt><dd>{{ sourceDatasetName }}</dd></div>
            <div class="output-dataset-row">
              <dt>输出数据集</dt>
              <dd class="output-dataset-cell">
                <p
                  v-if="hasPublishedOutputs && !hasCurrentPublishedDataset"
                  class="output-dataset-hint"
                >当前轮尚未发布，以下为上次已发布数据集，仍可正常使用</p>
                <div v-if="outputDatasets.length" class="output-dataset-links">
                  <el-button
                    v-for="dataset in outputDatasets"
                    :key="String(dataset.id)"
                    type="primary"
                    link
                    @click="router.push(`/dataset/${dataset.id}/preview`)"
                  >{{ dataset.name }}（{{ dataset.count || 0 }} 条）<i class="fa fa-external-link" /></el-button>
                </div>
                <span v-else-if="outputDatasetId">已发布数据集不可用，请重新发布</span>
                <span v-else>尚未发布为数据集</span>
              </dd>
            </div>
          </dl>
        </section>

        <section class="detail-section" aria-labelledby="statistics-title">
          <div class="section-heading">
            <div><h2 id="statistics-title">处理统计</h2><p>查看数据清洗、过滤和输出情况</p></div>
          </div>
          <div class="statistics-grid">
            <div><span>源文件</span><strong>{{ sourceFileCount.toLocaleString() }} 个</strong></div>
            <div><span>{{ inputMetricLabel }}</span><strong>{{ inputMetricCount.toLocaleString() }} {{ inputMetricUnit }}</strong></div>
            <div><span>生成结果</span><strong>{{ numeric(detail.output_count).toLocaleString() }} 条</strong></div>
            <div><span>过滤数据</span><strong>{{ numeric(detail.filtered_count).toLocaleString() }} 条</strong></div>
            <div><span>重复数据</span><strong>{{ numeric(detail.duplicate_count).toLocaleString() }} 条</strong></div>
            <div><span>异常数据</span><strong>{{ numeric(detail.error_count).toLocaleString() }} 条</strong></div>
            <div><span>执行进度</span><strong>{{ progressPercentage }}%</strong></div>
          </div>
        </section>
      </div>

      <section class="detail-section config-section" aria-labelledby="config-title">
        <div class="section-heading config-heading">
          <div><h2 id="config-title">处理配置</h2><p>任务执行时使用的规则与参数</p></div>
          <div class="config-actions">
            <span>共 {{ configRows.length }} 项</span>
            <el-button
              text
              :aria-expanded="configExpanded"
              aria-controls="config-content"
              @click="configExpanded = !configExpanded"
            >
              {{ configExpanded ? '收起' : '展开' }}
              <i class="fa" :class="configExpanded ? 'fa-chevron-up' : 'fa-chevron-down'" />
            </el-button>
          </div>
        </div>
        <el-collapse-transition>
          <div v-show="configExpanded" id="config-content" class="config-content">
            <dl v-if="configRows.length" class="config-grid">
              <div v-for="item in configRows" :key="item.label"><dt>{{ item.label }}</dt><dd>{{ item.value }}</dd></div>
            </dl>
            <div v-else class="compact-empty">暂无处理配置</div>
          </div>
        </el-collapse-transition>
      </section>

      <section class="detail-section result-section" aria-labelledby="result-title" v-loading="resultLoading">
        <div class="result-toolbar">
          <div class="section-heading">
            <div>
              <h2 id="result-title">结果明细</h2>
              <p>{{ hasCurrentPublishedDataset ? '查看生成结果与质量状态，已发布数据请前往数据集详情编辑' : '查看、编辑或恢复待发布结果' }}</p>
            </div>
          </div>
          <div class="result-filters">
            <el-input
              v-model="keyword"
              clearable
              placeholder="搜索指令、输入或输出"
              @input="scheduleResultReload"
              @clear="scheduleResultReload"
            >
              <template #prefix><i class="fa fa-search" aria-hidden="true" /></template>
            </el-input>
            <el-select v-model="statusFilter" clearable placeholder="全部状态" @change="scheduleResultReload">
              <el-option label="有效" value="valid" />
              <el-option label="已修改" value="modified" />
              <el-option label="无效" value="invalid" />
            </el-select>
            <el-button :loading="resultLoading" @click="loadResults"><i class="fa fa-refresh" /></el-button>
          </div>
        </div>

        <el-table
          v-if="results.length"
          :data="results"
          :tooltip-options="resultCellTooltipOptions"
          row-key="id"
          table-layout="fixed"
        >
          <el-table-column type="index" label="#" width="56" align="center" />
          <el-table-column label="指令" min-width="190" show-overflow-tooltip prop="instruction" />
          <el-table-column label="输入" min-width="160" show-overflow-tooltip prop="input" />
          <el-table-column label="输出" min-width="230" show-overflow-tooltip prop="output" />
          <el-table-column label="质量分" width="88" align="center">
            <template #default="{ row }">
              <el-tooltip :content="qualityFlagsLabel((row as DataProcessResult).quality_score)">
                <span>{{ qualityScoreLabel((row as DataProcessResult).quality_score) }}</span>
              </el-tooltip>
            </template>
          </el-table-column>
          <el-table-column label="状态" width="90" align="center">
            <template #default="{ row }">
              <el-tag :type="resultStatusType(row.status)" size="small">{{ resultStatusLabel(row.status) }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column v-if="!hasCurrentPublishedDataset" label="操作" width="130" align="center" fixed="right">
            <template #default="{ row }">
              <el-button link type="primary" @click="openResultEditor(row as DataProcessResult)">编辑</el-button>
              <el-button
                link
                :loading="restoringResultId === row.id"
                @click="restoreResult(row as DataProcessResult)"
              >恢复</el-button>
            </template>
          </el-table-column>
        </el-table>

        <div v-else class="result-empty">
          <i class="fa" :class="resultError || detail.status === 'failed' ? 'fa-exclamation-circle' : 'fa-hourglass-half'" aria-hidden="true" />
          <strong>{{ resultError || resultEmptyTitle }}</strong>
          <span v-if="!resultError">{{ resultEmptyDescription }}</span>
          <el-button v-else type="primary" link @click="loadResults">重新加载</el-button>
        </div>

        <div v-if="resultTotal" class="result-pagination">
          <span>共 {{ resultTotal }} 条结果</span>
          <el-pagination
            v-model:current-page="currentPage"
            v-model:page-size="pageSize"
            layout="sizes, prev, pager, next"
            :page-sizes="[10, 20, 50]"
            :total="resultTotal"
          />
        </div>
      </section>
    </template>
  </PageCard>

  <el-dialog v-model="editDialogVisible" title="编辑处理结果" width="680px" destroy-on-close>
    <el-form label-position="top">
      <el-form-item label="Instruction" required>
        <el-input v-model="editForm.instruction" type="textarea" :rows="3" maxlength="4000" show-word-limit />
      </el-form-item>
      <el-form-item label="Input">
        <el-input v-model="editForm.input" type="textarea" :rows="3" maxlength="10000" show-word-limit />
      </el-form-item>
      <el-form-item label="Output" required>
        <el-input v-model="editForm.output" type="textarea" :rows="6" maxlength="20000" show-word-limit />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="editDialogVisible = false">取消</el-button>
      <el-button type="primary" :loading="savingResult" @click="saveResult">保存</el-button>
    </template>
  </el-dialog>

  <el-dialog v-model="publishDialogVisible" title="发布为三个数据集" width="560px" destroy-on-close>
    <el-form label-position="top">
      <el-form-item label="数据集名称" required>
        <el-input v-model="publishForm.dataset_name" maxlength="150" show-word-limit />
      </el-form-item>
      <div class="publish-form-grid">
        <el-form-item label="存储位置">
          <el-select v-model="publishForm.storage_type">
            <el-option label="平台本地存储" value="local" />
          </el-select>
        </el-form-item>
        <el-form-item label="输出格式">
          <el-select v-model="publishForm.format">
            <el-option label="Alpaca JSONL" value="alpaca_jsonl" />
            <el-option label="JSONL" value="jsonl" />
          </el-select>
        </el-form-item>
      </div>
      <el-alert
        type="info"
        :closable="false"
        :title="`将发布三个独立数据集：训练集 ${publishForm.split.train}% / 验证集 ${publishForm.split.validation}% / 测试集 ${publishForm.split.test}%`"
      />
    </el-form>
    <template #footer>
      <el-button @click="publishDialogVisible = false">取消</el-button>
      <el-button type="primary" :loading="publishing" @click="publishDataset">发布三个数据集</el-button>
    </template>
  </el-dialog>
</template>

<style scoped lang="scss">
.data-process-detail-page { width: 100%; min-width: 0; }

.detail-heading {
  width: 100%;

  .heading-row { display: flex; align-items: center; gap: 12px; }
  h1 { margin: 0; color: #1f2937; font-size: 20px; font-weight: 600; }
  > p { margin: 8px 0 0; color: #64748b; font-size: 13px; }
}

.publish-button { margin-left: auto; }
.load-state-actions { display: flex; gap: 10px; }
.compact-empty { padding: 28px 18px; color: #94a3b8; font-size: 13px; text-align: center; }
.publish-form-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.publish-form-grid :deep(.el-select) { width: 100%; }

.heading-meta {
  margin: 16px 0 0; display: flex; flex-wrap: wrap; gap: 10px 30px;
  div { display: flex; gap: 7px; font-size: 12px; }
  dt { color: #94a3b8; }
  dd { margin: 0; color: #475569; font-weight: 500; }
}

.metric-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
.metric-card {
  min-height: 116px; padding: 18px; border: 1px solid #e5e7eb; border-radius: 8px; background: #fff;
  display: flex; flex-direction: column;
  > span { color: #64748b; font-size: 12px; }
  > strong { margin-top: 10px; color: #1f2937; font-size: 22px; font-weight: 600; }
  > small { margin-top: auto; color: #94a3b8; font-size: 12px; }
  :deep(.el-progress) { margin-top: auto; }
  &.is-primary { border-color: var(--el-color-primary-light-7); background: var(--el-color-primary-light-9); }
  &.is-primary > strong { color: var(--primary-color); }
}

.failure-alert {
  margin-top: 16px; padding: 14px 16px; border: 1px solid #fecaca; border-radius: 8px; background: #fef2f2;
  color: #b91c1c; display: flex; gap: 12px;
  > i { margin-top: 2px; }
  strong { font-size: 13px; }
  p { margin: 4px 0 0; color: #7f1d1d; font-size: 12px; }
}

.detail-grid { margin-top: 16px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.detail-section { border: 1px solid #e5e7eb; border-radius: 8px; background: #fff; overflow: hidden; }
.section-heading {
  padding: 16px 18px; border-bottom: 1px solid #eef0f3;
  h2 { margin: 0; color: #1f2937; font-size: 15px; font-weight: 600; }
  p { margin: 4px 0 0; color: #94a3b8; font-size: 12px; }
}

.config-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.config-actions {
  display: flex;
  align-items: center;
  gap: 8px;

  > span { color: #94a3b8; font-size: 12px; }
  :deep(.el-button) { gap: 6px; }
}

.info-list { margin: 0; padding: 4px 18px 12px; }
.info-list > div {
  min-height: 44px; border-bottom: 1px solid #f1f5f9; display: grid; grid-template-columns: 100px minmax(0, 1fr); align-items: center;
  &:last-child { border-bottom: 0; }
  dt { color: #64748b; font-size: 12px; }
  dd { margin: 0; color: #334155; font-size: 13px; text-align: right; }
  :deep(.el-button) { height: auto; padding: 0; }
}

.info-list > .output-dataset-row {
  align-items: start;
  padding: 10px 0;

  dt { padding-top: 2px; line-height: 24px; }
}

.output-dataset-cell { min-width: 0; }

.output-dataset-hint {
  margin: 0 0 6px;
  color: #d97706;
  font-size: 12px;
  line-height: 1.5;
  text-align: left;
}

.output-dataset-links {
  max-width: 100%;
  min-width: 0;
  display: inline-grid;
  grid-template-columns: max-content;
  gap: 4px;
  text-align: left;

  :deep(.el-button) {
    width: 100%;
    max-width: 100%;
    margin-left: 0;
    justify-content: flex-start;
    line-height: 24px;
    white-space: normal;
  }

  :deep(.el-button > span) {
    width: 100%;
    display: flex;
    align-items: center;
    text-align: left;
  }

  :deep(.el-button i) {
    flex: none;
    margin-left: auto;
    padding-left: 12px;
  }
}

.statistics-grid { padding: 18px; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.statistics-grid > div {
  min-height: 78px; padding: 13px; border-radius: 6px; background: #f8fafc; display: flex; flex-direction: column; gap: 8px;
  span { color: #64748b; font-size: 12px; }
  strong { color: #1f2937; font-size: 18px; font-weight: 600; }
}

.config-section, .result-section { margin-top: 16px; }
.config-grid { margin: 0; padding: 8px 18px 16px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 40px; }
.config-grid > div { min-height: 48px; border-bottom: 1px solid #f1f5f9; display: flex; align-items: center; justify-content: space-between; gap: 20px; }
.config-grid dt { color: #64748b; font-size: 12px; }
.config-grid dd { max-width: 68%; margin: 0; color: #334155; font-size: 13px; overflow-wrap: anywhere; text-align: right; }

.result-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 20px; border-bottom: 1px solid #eef0f3; }
.result-toolbar .section-heading { border-bottom: 0; }
.result-filters { padding-right: 18px; display: flex; gap: 10px; }
.result-filters :deep(.el-input) { width: 250px; }
.result-filters :deep(.el-select) { width: 120px; }
.result-section :deep(.el-table) { border-radius: 0; }

:global(.data-process-result-tooltip) {
  box-sizing: border-box;
  max-width: min(520px, calc(100vw - 32px));
  max-height: 240px;
  overflow-y: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: break-word;
  line-height: 1.6;
}

.result-empty, .not-found-state {
  min-height: 220px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #94a3b8;
  > i { margin-bottom: 12px; color: var(--el-color-primary-light-5); font-size: 30px; }
  strong, h2 { margin: 0; color: #475569; font-size: 14px; font-weight: 500; }
  span, p { margin: 6px 0 16px; font-size: 12px; }
}

.result-pagination { min-height: 56px; padding: 0 18px; border-top: 1px solid #eef0f3; display: flex; align-items: center; justify-content: space-between; }
.result-pagination > span { color: #94a3b8; font-size: 12px; }
.result-pagination :deep(.el-pagination) { margin-top: 0; }

@media (max-width: 1100px) {
  .metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .detail-grid { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  .metric-grid, .config-grid { grid-template-columns: 1fr; }
  .detail-heading .heading-row { align-items: flex-start; flex-wrap: wrap; }
  .publish-button { width: 100%; margin-left: 0; }
  .publish-form-grid { grid-template-columns: 1fr; gap: 0; }
  .result-toolbar { align-items: stretch; flex-direction: column; }
  .result-filters { padding: 0 16px 16px; flex-direction: column; }
  .result-filters :deep(.el-input), .result-filters :deep(.el-select) { width: 100%; }
  .heading-meta { flex-direction: column; gap: 6px; }
}
</style>
