import { describe, it, expect } from 'vitest'
import {
  buildPerProcessSeries,
  buildProcessAverages,
  formatSeconds,
  type EvalStep,
  type EvalMeasurement,
} from '@/lib/stopwatch/evaluation'

const steps: EvalStep[] = [
  { id: 'a', station_name: 'Prozess A', sort_order: 1 },
  { id: 'b', station_name: 'Prozess B', sort_order: 2 },
]

function m(
  process_step_id: string,
  cycle_number: number,
  cycle_time_sec: number | null,
  is_outlier = false,
): EvalMeasurement {
  return { process_step_id, cycle_number, cycle_time_sec, is_outlier }
}

describe('buildPerProcessSeries', () => {
  it('produces one bar per measurement, labelled #1..#n in cycle order', () => {
    const measurements = [
      m('a', 3, 12),
      m('a', 1, 10),
      m('a', 2, 11),
    ]
    const series = buildPerProcessSeries(steps, measurements)
    const a = series.find((s) => s.stepId === 'a')!
    expect(a.bars.map((b) => b.label)).toEqual(['#1', '#2', '#3'])
    // order preserved by cycle_number → times come out sorted by cycle
    expect(a.bars.map((b) => b.timeSec)).toEqual([10, 11, 12])
    expect(a.bars.map((b) => b.cycleNumber)).toEqual([1, 2, 3])
    expect(a.barCount).toBe(3)
  })

  it('returns one series per station, including empty ones', () => {
    const series = buildPerProcessSeries(steps, [m('a', 1, 10)])
    expect(series.map((s) => s.stepId)).toEqual(['a', 'b'])
    const b = series.find((s) => s.stepId === 'b')!
    expect(b.bars).toEqual([])
    expect(b.avgSec).toBeNull()
    expect(b.barCount).toBe(0)
  })

  it('keeps outliers as bars but excludes them from the average', () => {
    const measurements = [
      m('a', 1, 10),
      m('a', 2, 12),
      m('a', 3, 100, true), // outlier
    ]
    const a = buildPerProcessSeries(steps, measurements).find((s) => s.stepId === 'a')!
    expect(a.barCount).toBe(3)
    expect(a.bars[2].isOutlier).toBe(true)
    expect(a.validCount).toBe(2)
    expect(a.avgSec).toBe(11) // (10 + 12) / 2, outlier dropped
  })

  it('drops invalid/missing values (null, NaN, ≤ 0) from bars and average', () => {
    const measurements = [
      m('a', 1, 10),
      m('a', 2, null),
      m('a', 3, NaN),
      m('a', 4, 0),
      m('a', 5, -5),
      m('a', 6, 14),
    ]
    const a = buildPerProcessSeries(steps, measurements).find((s) => s.stepId === 'a')!
    expect(a.barCount).toBe(2) // only 10 and 14 are plottable
    expect(a.bars.map((b) => b.label)).toEqual(['#1', '#2'])
    expect(a.avgSec).toBe(12) // (10 + 14) / 2
  })

  it('respects station display order (sort_order)', () => {
    const reordered: EvalStep[] = [
      { id: 'b', station_name: 'Prozess B', sort_order: 2 },
      { id: 'a', station_name: 'Prozess A', sort_order: 1 },
    ]
    const series = buildPerProcessSeries(reordered, [])
    expect(series.map((s) => s.stepId)).toEqual(['a', 'b'])
  })
})

describe('buildProcessAverages', () => {
  it('one bar per process with the correct mean, outliers excluded', () => {
    const measurements = [
      m('a', 1, 10),
      m('a', 2, 20),
      m('b', 1, 5),
      m('b', 2, 7),
      m('b', 3, 999, true),
    ]
    const rows = buildProcessAverages(steps, measurements)
    expect(rows).toEqual([
      { stepId: 'a', stationName: 'Prozess A', avgSec: 15, count: 2 },
      { stepId: 'b', stationName: 'Prozess B', avgSec: 6, count: 2 },
    ])
  })

  it('omits processes without any valid measurement', () => {
    const measurements = [
      m('a', 1, 10),
      m('b', 1, null),
      m('b', 2, 50, true), // only an outlier → no average
    ]
    const rows = buildProcessAverages(steps, measurements)
    expect(rows.map((r) => r.stepId)).toEqual(['a'])
  })

  it('returns empty when there are no measurements at all', () => {
    expect(buildProcessAverages(steps, [])).toEqual([])
  })
})

describe('formatSeconds', () => {
  it('formats with German decimal comma and a unit', () => {
    expect(formatSeconds(12.34)).toBe('12,34 s')
    expect(formatSeconds(6, 1)).toBe('6,0 s')
  })
})
