import { describe, it, expect } from 'vitest'
import {
  computeDelta,
  computeMetricDeltas,
  deltaReferenceIndices,
  deltaSentiment,
  MAX_SLOTS,
  type ComparisonSlot,
} from '@/lib/qaf/comparison'
import type { QAFRow } from '@/lib/qaf-parser'

function mkRow(overrides: Partial<QAFRow>): QAFRow {
  return {
    positionsnummer: '',
    teilebenennung: '',
    prozessbezeichnung: '',
    bezeichnungAnlage: '',
    standort: '',
    beschaffungswaehrung: '',
    angebotswaehrung: '',
    zykluszeit: null,
    teileProZyklus: null,
    anzahlMA: null,
    lohnkosten: null,
    lohnzuschlagssaetze: null,
    mss: null,
    ruestkosten: null,
    fek: null,
    rfgk: null,
    fk: null,
    wechselkurs: null,
    anzahlProAngebotsteil: null,
    fkAW: null,
    ausschuss: null,
    ausschusskosten: null,
    ...overrides,
  }
}

function mkSlot(i: number, fields: Partial<QAFRow>): ComparisonSlot {
  return {
    slot_index: i,
    qaf_upload_id: `f${i}`,
    file_label: `File ${i}`,
    prozess_name: 'Spritzguss',
    row: mkRow(fields),
  }
}

describe('deltaReferenceIndices', () => {
  it('returns empty for 0 slots', () => {
    expect(deltaReferenceIndices(0, 'vs-basis')).toEqual([])
  })

  it('vs-basis: every non-baseline slot points to slot 0', () => {
    expect(deltaReferenceIndices(5, 'vs-basis')).toEqual([null, 0, 0, 0, 0])
  })

  it('aufbauend: each slot points to its predecessor', () => {
    expect(deltaReferenceIndices(5, 'aufbauend')).toEqual([null, 0, 1, 2, 3])
  })

  it('always returns null at index 0 (baseline)', () => {
    expect(deltaReferenceIndices(1, 'vs-basis')[0]).toBe(null)
    expect(deltaReferenceIndices(1, 'aufbauend')[0]).toBe(null)
  })

  it('MAX_SLOTS is 5', () => {
    expect(MAX_SLOTS).toBe(5)
  })
})

describe('computeDelta', () => {
  it('returns the difference current - ref', () => {
    expect(computeDelta(65, 60)).toBe(5)
    expect(computeDelta(60, 65)).toBe(-5)
  })

  it('returns null when either side is missing', () => {
    expect(computeDelta(null, 60)).toBe(null)
    expect(computeDelta(60, null)).toBe(null)
    expect(computeDelta(null, null)).toBe(null)
  })

  it('rounds to 4 decimal places to avoid float noise', () => {
    expect(computeDelta(0.1 + 0.2, 0.3)).toBe(0)
  })
})

describe('computeMetricDeltas', () => {
  const slots: ComparisonSlot[] = [
    mkSlot(0, { zykluszeit: 60 }),
    mkSlot(1, { zykluszeit: 65 }),
    mkSlot(2, { zykluszeit: 67 }),
    mkSlot(3, { zykluszeit: null }),
  ]

  it('vs-basis: every slot delta is relative to slot 0', () => {
    expect(computeMetricDeltas(slots, 'zykluszeit', 'vs-basis')).toEqual([null, 5, 7, null])
  })

  it('aufbauend: each slot delta is relative to its predecessor', () => {
    expect(computeMetricDeltas(slots, 'zykluszeit', 'aufbauend')).toEqual([null, 5, 2, null])
  })

  it('returns null where any side of the pair is null', () => {
    const withGap: ComparisonSlot[] = [
      mkSlot(0, { zykluszeit: null }),
      mkSlot(1, { zykluszeit: 65 }),
    ]
    expect(computeMetricDeltas(withGap, 'zykluszeit', 'vs-basis')).toEqual([null, null])
  })
})

describe('deltaSentiment', () => {
  it('lowerBetter=true: negative delta = better, positive = worse', () => {
    expect(deltaSentiment(-5, true)).toBe('better')
    expect(deltaSentiment(5, true)).toBe('worse')
  })

  it('lowerBetter=false: positive delta = better, negative = worse', () => {
    expect(deltaSentiment(5, false)).toBe('better')
    expect(deltaSentiment(-5, false)).toBe('worse')
  })

  it('within epsilon of 0 = equal', () => {
    expect(deltaSentiment(0, true)).toBe('equal')
    expect(deltaSentiment(1e-9, true)).toBe('equal')
  })

  it('null delta = na', () => {
    expect(deltaSentiment(null, true)).toBe('na')
  })
})
