import type { EChartsOption } from 'echarts'
import type { FineTuneTask, TrainingLogFile } from '@/types'

export interface TrainingMetricData {
  loss: number[]
  gradNorm: number[]
  lr: number[]
  epoch: number[]
}

export interface TrainingSummary {
  epoch: string
  trainLoss: string
  runtime: string
}

export interface ParsedTrainingLog {
  metrics: TrainingMetricData
  summary: TrainingSummary
}

const NUMBER_SOURCE = '[-+]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][-+]?\\d+)?'

function escapeRegExp(value: string) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

function extractNumber(source: string, key: string) {
  const match = source.match(new RegExp(`['"]?${escapeRegExp(key)}['"]?\\s*:\\s*(${NUMBER_SOURCE})`, 'i'))
  return match ? Number(match[1]) : undefined
}

function extractSummaryValue(source: string, key: string) {
  const match = source.match(new RegExp(`['"]?${escapeRegExp(key)}['"]?\\s*(?:=|:)\\s*(${NUMBER_SOURCE})`, 'i'))
  return match?.[1] || ''
}

/** 根据任务精确选择日志；PID 优先，任务名仅作为明确兜底。 */
export function resolveTrainingLogFile(
  files: TrainingLogFile[],
  task: Pick<FineTuneTask, 'process_id' | 'name'>,
) {
  const processId = task.process_id
  if (processId != null) {
    const pidMatch = files.find((file) => file.pid === processId)
    if (pidMatch) return pidMatch

    const pidPattern = new RegExp(`(?:^|[^0-9])(?:pid)?${processId}(?:[^0-9]|$)`, 'i')
    const filenameMatch = files.find((file) => pidPattern.test(file.file))
    if (filenameMatch) return filenameMatch
  }

  const taskName = task.name.trim()
  if (!taskName) return undefined
  return files.find((file) => file.name.includes(taskName) || file.file.includes(taskName))
}

/** 解析日志中的逐步指标。字段顺序和常见数值格式均不受限制。 */
export function parseTrainingMetrics(text: string): TrainingMetricData {
  const metrics: TrainingMetricData = { loss: [], gradNorm: [], lr: [], epoch: [] }
  const blocks = text.match(/\{[^{}\r\n]*\}/g) || []

  for (const block of blocks) {
    const loss = extractNumber(block, 'loss')
    const gradNorm = extractNumber(block, 'grad_norm')
    const learningRate = extractNumber(block, 'learning_rate')
    const epoch = extractNumber(block, 'epoch')
    if (loss == null || gradNorm == null || learningRate == null) continue
    metrics.loss.push(loss)
    metrics.gradNorm.push(gradNorm)
    metrics.lr.push(learningRate)
    if (epoch != null) metrics.epoch.push(epoch)
  }

  return metrics
}

/** 每次都返回新对象，日志截断或切换时不会残留上一轮汇总。 */
export function parseTrainingSummary(text: string): TrainingSummary {
  const emptySummary: TrainingSummary = { epoch: '', trainLoss: '', runtime: '' }
  const startMatch = /\*{5}\s*train metrics\s*\*{5}/i.exec(text)
  if (!startMatch) return emptySummary

  const tail = text.slice(startMatch.index + startMatch[0].length)
  const endMatch = /\*{5}\s*train metrics end\s*\*{5}/i.exec(tail)
  const body = endMatch ? tail.slice(0, endMatch.index) : tail
  return {
    epoch: extractSummaryValue(body, 'epoch'),
    trainLoss: extractSummaryValue(body, 'train_loss'),
    runtime: extractSummaryValue(body, 'train_runtime'),
  }
}

export function parseTrainingLog(text: string): ParsedTrainingLog {
  return {
    metrics: parseTrainingMetrics(text),
    summary: parseTrainingSummary(text),
  }
}

/** 构建单条训练指标曲线。 */
export function buildMetricChartOption(
  label: string,
  data: number[],
  color: string,
  logScale = false,
): EChartsOption {
  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: 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` },
            ],
          },
        },
      },
    ],
  }
}
