import { describe, it, expect } from 'vitest'
import {
  avgCycleTimeSec,
  calcLscWorkshopStats,
  calcBottleneck,
  calcTaktAchievement,
  calcLscMeasureStatus,
  type ProcessStepWithMeasurements,
} from '../aggregations'

// ─── avgCycleTimeSec ─────────────────────────────────────────────────────────

describe('avgCycleTimeSec', () => {
  it('returns null for an empty list', () => {
    expect(avgCycleTimeSec([])).toBeNull()
  })

  it('returns null when every measurement is an outlier', () => {
    expect(
      avgCycleTimeSec([
        { cycle_time_sec: 10, is_outlier: true },
        { cycle_time_sec: 99, is_outlier: true },
      ]),
    ).toBeNull()
  })

  it('averages only the non-outlier measurements', () => {
    expect(
      avgCycleTimeSec([
        { cycle_time_sec: 10, is_outlier: false },
        { cycle_time_sec: 20, is_outlier: false },
        { cycle_time_sec: 1000, is_outlier: true },
      ]),
    ).toBe(15)
  })

  it('treats missing/undefined is_outlier as valid', () => {
    expect(avgCycleTimeSec([{ cycle_time_sec: 8 }, { cycle_time_sec: 12 }])).toBe(10)
  })
})

// ─── calcLscWorkshopStats ────────────────────────────────────────────────────

describe('calcLscWorkshopStats', () => {
  it('returns zeros and null for empty steps', () => {
    expect(calcLscWorkshopStats([])).toEqual({
      totalStations: 0,
      measuredStations: 0,
      avgCycleTimeSec: null,
    })
  })

  it('counts only steps with ≥1 non-outlier measurement as measured', () => {
    const steps: ProcessStepWithMeasurements[] = [
      {
        id: 'a',
        project_id: 'p',
        station_name: 'S1',
        cycle_measurements: [
          { cycle_time_sec: 10, is_outlier: false },
          { cycle_time_sec: 12, is_outlier: false },
        ],
      },
      {
        id: 'b',
        project_id: 'p',
        station_name: 'S2',
        cycle_measurements: [{ cycle_time_sec: 99, is_outlier: true }],
      },
      {
        id: 'c',
        project_id: 'p',
        station_name: 'S3',
        cycle_measurements: [],
      },
    ]
    const result = calcLscWorkshopStats(steps)
    expect(result.totalStations).toBe(3)
    expect(result.measuredStations).toBe(1)
    expect(result.avgCycleTimeSec).toBeCloseTo(11)
  })

  it('excludes outliers from the CT average', () => {
    const steps: ProcessStepWithMeasurements[] = [
      {
        id: 'x',
        project_id: 'p',
        station_name: 'Station X',
        cycle_measurements: [
          { cycle_time_sec: 10, is_outlier: false },
          { cycle_time_sec: 200, is_outlier: true },
          { cycle_time_sec: 20, is_outlier: false },
        ],
      },
    ]
    expect(calcLscWorkshopStats(steps).avgCycleTimeSec).toBeCloseTo(15)
  })
})

// ─── calcBottleneck ──────────────────────────────────────────────────────────

describe('calcBottleneck', () => {
  it('returns null when no steps have measurements', () => {
    const steps: ProcessStepWithMeasurements[] = [
      {
        id: 'a',
        project_id: 'p',
        station_name: 'S1',
        cycle_measurements: [],
      },
    ]
    expect(calcBottleneck(steps, 30)).toBeNull()
  })

  it('marks isOverTakt=true when worst station CT > customer takt (F-019)', () => {
    const steps: ProcessStepWithMeasurements[] = [
      {
        id: 'a',
        project_id: 'p',
        station_name: 'SlowStation',
        cycle_measurements: [{ cycle_time_sec: 40, is_outlier: false }],
      },
      {
        id: 'b',
        project_id: 'p',
        station_name: 'FastStation',
        cycle_measurements: [{ cycle_time_sec: 20, is_outlier: false }],
      },
    ]
    const result = calcBottleneck(steps, 35)
    expect(result).not.toBeNull()
    expect(result!.stationName).toBe('SlowStation')
    expect(result!.isOverTakt).toBe(true)
    expect(result!.taktRatio).toBeCloseTo(40 / 35)
  })

  it('marks isOverTakt=false when worst station CT < customer takt', () => {
    const steps: ProcessStepWithMeasurements[] = [
      {
        id: 'a',
        project_id: 'p',
        station_name: 'Station',
        cycle_measurements: [{ cycle_time_sec: 25, is_outlier: false }],
      },
    ]
    const result = calcBottleneck(steps, 30)
    expect(result!.isOverTakt).toBe(false)
    expect(result!.taktRatio).toBeCloseTo(25 / 30)
  })

  it('returns null taktRatio when customerTaktTimeSec is null', () => {
    const steps: ProcessStepWithMeasurements[] = [
      {
        id: 'a',
        project_id: 'p',
        station_name: 'Station',
        cycle_measurements: [{ cycle_time_sec: 25, is_outlier: false }],
      },
    ]
    const result = calcBottleneck(steps, null)
    expect(result!.taktRatio).toBeNull()
    expect(result!.isOverTakt).toBe(false)
  })
})

// ─── calcTaktAchievement ─────────────────────────────────────────────────────

describe('calcTaktAchievement', () => {
  it('returns null ratios when avgCT is null', () => {
    expect(calcTaktAchievement(null, 30, 25)).toEqual({
      ctVsCustomerTakt: null,
      ctVsTargetCt: null,
    })
  })

  it('computes correct ratios', () => {
    const result = calcTaktAchievement(30, 40, 25)
    expect(result.ctVsCustomerTakt).toBeCloseTo(30 / 40)
    expect(result.ctVsTargetCt).toBeCloseTo(30 / 25)
  })

  it('returns null when takt is zero to avoid division by zero', () => {
    const result = calcTaktAchievement(30, 0, 0)
    expect(result.ctVsCustomerTakt).toBeNull()
    expect(result.ctVsTargetCt).toBeNull()
  })
})

// ─── calcLscMeasureStatus ────────────────────────────────────────────────────

describe('calcLscMeasureStatus', () => {
  it('handles German DB status codes', () => {
    const measures = [
      { status: 'offen' },
      { status: 'offen' },
      { status: 'in_arbeit' },
      { status: 'erledigt' },
    ]
    expect(calcLscMeasureStatus(measures)).toEqual({
      open: 2,
      in_progress: 1,
      done: 1,
    })
  })

  it('handles English status codes (future-proofing F-03 migration)', () => {
    const measures = [
      { status: 'open' },
      { status: 'in_progress' },
      { status: 'done' },
    ]
    expect(calcLscMeasureStatus(measures)).toEqual({
      open: 1,
      in_progress: 1,
      done: 1,
    })
  })

  it('ignores unknown status codes gracefully', () => {
    const measures = [{ status: 'unknown' }, { status: 'offen' }]
    expect(calcLscMeasureStatus(measures)).toEqual({
      open: 1,
      in_progress: 0,
      done: 0,
    })
  })

  it('returns zeros for empty array', () => {
    expect(calcLscMeasureStatus([])).toEqual({
      open: 0,
      in_progress: 0,
      done: 0,
    })
  })
})
