import { describe, it, expect } from 'vitest'
import type { QAFRow } from '@/lib/qaf-parser'
import type { QafSummary, QafSummaryKey } from '../types'
import type { QafFileParsed } from '../compare'
import { buildBatchComparisons } from '../batch'

function summary(values: Partial<Record<QafSummaryKey, string | null>>): QafSummary {
  const keys: QafSummaryKey[] = [
    'partNumber', 'quotationDate', 'supplier', 'partName', 'variant',
    'project', 'requestVersion', 'changeIndex', 'supplierNo',
    'peakVolumeYear', 'productionStartSop', 'deliverySite', 'shiftsPerWeek', // KAR-910
    'plannedCapacity', 'lotSize', // QVS-P4
  ]
  const out = {} as QafSummary
  for (const k of keys) out[k] = { value: values[k] ?? null, cell: null }
  return out
}

function mkRow(o: Partial<QAFRow> = {}): QAFRow {
  return {
    positionsnummer: '1', teilebenennung: 'Teil', prozessbezeichnung: 'Montage',
    bezeichnungAnlage: 'Anlage', standort: 'Werk', beschaffungswaehrung: 'EUR', angebotswaehrung: 'EUR',
    zykluszeit: 10, teileProZyklus: 1, anzahlMA: 1, lohnkosten: 30, lohnzuschlagssaetze: 10, mss: 50,
    ruestkosten: 0, fek: 0, rfgk: 0, fk: 100, wechselkurs: 1, anzahlProAngebotsteil: 1, fkAW: 100,
    ausschuss: 2, ausschusskosten: 1, ...o,
  }
}

function file(id: string, pn: string | null, date: string, fk = 100): QafFileParsed {
  return {
    ref: { id, fileName: `${id}.xlsx`, quotationDate: date },
    summary: summary({ partNumber: pn, quotationDate: date }),
    steps: [mkRow({ fk })],
  }
}

describe('buildBatchComparisons', () => {
  it('groups by part number and compares oldest vs newest within each group', () => {
    const files = [
      file('a1', '7490365', '2023-01-01', 100),
      file('a2', '7490365', '2024-01-01', 150),
      file('b1', '7489861', '2023-06-01', 200),
      file('b2', '7489861', '2024-06-01', 250),
    ]
    const groups = buildBatchComparisons(files)
    const a = groups.find((g) => g.partNumber === '7490365')
    const b = groups.find((g) => g.partNumber === '7489861')

    expect(a?.comparison?.altRef.id).toBe('a1')
    expect(a?.comparison?.neuRef.id).toBe('a2')
    expect(b?.comparison?.altRef.id).toBe('b1')
    // groups are independent — A never compared against B
    expect(a?.comparison?.partNumber).toBe('7490365')
    expect(b?.comparison?.partNumber).toBe('7489861')
  })

  it('flags a single-file group as insufficient with no comparison', () => {
    const groups = buildBatchComparisons([
      file('a1', '7490365', '2023-01-01'),
      file('a2', '7490365', '2024-01-01'),
      file('solo', '5A1CEF6', '2024-01-01'),
    ])
    const solo = groups.find((g) => g.partNumber === '5A1CEF6')
    expect(solo?.fileCount).toBe(1)
    expect(solo?.baseline.status).toBe('insufficient')
    expect(solo?.comparison).toBeNull()
  })

  it('buckets files without a part number separately and does not compare them', () => {
    const groups = buildBatchComparisons([
      file('x', null, '2023-01-01'),
      file('y', null, '2024-01-01'),
    ])
    const unknown = groups.find((g) => g.partNumber === null)
    expect(unknown).toBeTruthy()
    expect(unknown?.comparison).toBeNull()
    expect(unknown?.needsReview).toBe(true)
  })
})

// ── Paarungs-Kaskade (31.07.2026) ───────────────────────────────────────────
// Anlassfall: zwei QAF-8.8-Dateien desselben Umfangs, dieselbe Anfragenummer,
// derselbe Lieferant — nur die "Aktuell"-Datei trug eine Sachnummer, die
// "Vergabe"-Datei ließ das Feld leer. Vorher: zwei Gruppen, kein Vergleich.

function fileFull(
  id: string,
  date: string,
  s: Partial<Record<QafSummaryKey, string | null>>,
  fk = 100
): QafFileParsed {
  return {
    ref: { id, fileName: `${id}.xlsx`, quotationDate: date },
    summary: summary({ quotationDate: date, ...s }),
    steps: [mkRow({ fk })],
  }
}

describe('Paarungs-Kaskade — rettet zusammengehörende Dateien', () => {
  it('paart über die Anfragenummer, wenn EINE Seite keine Sachnummer trägt', () => {
    const groups = buildBatchComparisons([
      fileFull('vergabe', '2022-07-05', { partNumber: null, requestVersion: 'REQ-8899', supplier: 'Lieferant A', partName: 'Baugruppe X' }),
      fileFull('aktuell', '2026-04-01', { partNumber: 'PN-4711', requestVersion: 'REQ-8899', supplier: 'Lieferant A', partName: 'Baugruppe X' }, 150),
    ])
    const paired = groups.filter((g) => g.comparison !== null)
    expect(paired).toHaveLength(1)
    expect(paired[0].pairedBy).toBe('requestVersion')
    expect(paired[0].comparison?.altRef.id).toBe('vergabe')
    expect(paired[0].comparison?.neuRef.id).toBe('aktuell')
    // Die vorhandene Sachnummer bleibt am Ergebnis, damit der Vergleich
    // auffindbar ist
    expect(paired[0].partNumber).toBe('PN-4711')
    // Ein über ein schwächeres Merkmal gepaartes Ergebnis geht in die Review
    expect(paired[0].needsReview).toBe(true)
  })

  it('paart über Teil + Lieferant, wenn auch die Anfragenummer fehlt', () => {
    const groups = buildBatchComparisons([
      fileFull('alt', '2022-01-01', { partNumber: null, requestVersion: null, supplier: 'Lieferant A', partName: 'Baugruppe X' }),
      fileFull('neu', '2026-01-01', { partNumber: null, requestVersion: null, supplier: 'Lieferant A', partName: 'Baugruppe X' }, 150),
    ])
    const paired = groups.filter((g) => g.comparison !== null)
    expect(paired).toHaveLength(1)
    expect(paired[0].pairedBy).toBe('partAndSupplier')
  })

  it('ignoriert Groß-/Kleinschreibung und Leerraum in den Fallback-Feldern', () => {
    const groups = buildBatchComparisons([
      fileFull('a', '2022-01-01', { partNumber: null, requestVersion: '  REQ-8899 ' }),
      fileFull('b', '2026-01-01', { partNumber: null, requestVersion: 'REQ-8899' }, 150),
    ])
    expect(groups.filter((g) => g.comparison !== null)).toHaveLength(1)
  })
})

describe('Paarungs-Kaskade — die Schutzregeln halten', () => {
  it('paart NIEMALS zwei verschiedene, jeweils gesetzte Sachnummern', () => {
    // Alles andere identisch — trotzdem sind es verschiedene Teile.
    const groups = buildBatchComparisons([
      fileFull('x', '2022-01-01', { partNumber: 'AAA111', requestVersion: 'REQ-1', supplier: 'S', partName: 'P' }),
      fileFull('y', '2026-01-01', { partNumber: 'BBB222', requestVersion: 'REQ-1', supplier: 'S', partName: 'P' }, 150),
    ])
    expect(groups.filter((g) => g.comparison !== null)).toHaveLength(0)
  })

  it('rät nicht, wenn drei Dateien denselben Fallback-Schlüssel teilen', () => {
    const groups = buildBatchComparisons([
      fileFull('a', '2022-01-01', { partNumber: null, requestVersion: 'REQ-1' }),
      fileFull('b', '2024-01-01', { partNumber: null, requestVersion: 'REQ-1' }),
      fileFull('c', '2026-01-01', { partNumber: null, requestVersion: 'REQ-1' }),
    ])
    expect(groups.filter((g) => g.comparison !== null)).toHaveLength(0)
  })

  it('paart nicht über den Lieferanten allein — verschiedene Teile bleiben getrennt', () => {
    const groups = buildBatchComparisons([
      fileFull('a', '2022-01-01', { partNumber: null, requestVersion: null, supplier: 'Lieferant A', partName: 'Baugruppe X' }),
      fileFull('b', '2026-01-01', { partNumber: null, requestVersion: null, supplier: 'Lieferant A', partName: 'Baugruppe Y' }),
    ])
    expect(groups.filter((g) => g.comparison !== null)).toHaveLength(0)
  })

  it('paart nicht über das Teil allein — verschiedene Lieferanten bleiben getrennt', () => {
    const groups = buildBatchComparisons([
      fileFull('a', '2022-01-01', { partNumber: null, requestVersion: null, supplier: 'Lieferant A', partName: 'Baugruppe X' }),
      fileFull('b', '2026-01-01', { partNumber: null, requestVersion: null, supplier: 'Lieferant B', partName: 'Baugruppe X' }),
    ])
    expect(groups.filter((g) => g.comparison !== null)).toHaveLength(0)
  })

  it('fasst eine bereits über die Sachnummer gepaarte Datei nicht erneut an', () => {
    // a1+a2 paaren über die Sachnummer. c trägt dieselbe Anfragenummer wie a1,
    // darf aber niemanden aus diesem Paar abziehen.
    const groups = buildBatchComparisons([
      fileFull('a1', '2022-01-01', { partNumber: 'PN-1', requestVersion: 'REQ-9' }),
      fileFull('a2', '2024-01-01', { partNumber: 'PN-1', requestVersion: 'REQ-9' }, 150),
      fileFull('c', '2026-01-01', { partNumber: null, requestVersion: 'REQ-9' }, 200),
    ])
    const pn1 = groups.find((g) => g.partNumber === 'PN-1')
    expect(pn1?.pairedBy).toBe('partNumber')
    expect(pn1?.comparison?.altRef.id).toBe('a1')
    expect(pn1?.comparison?.neuRef.id).toBe('a2')
    // c bleibt allein — es gibt keinen zweiten freien Kandidaten
    const withC = groups.find((g) => g.fileCount === 1 && g.partNumber === null)
    expect(withC?.comparison).toBeNull()
  })

  it('lässt reine Sachnummer-Paare unverändert und weist sie als solche aus', () => {
    const groups = buildBatchComparisons([
      fileFull('a1', '2023-01-01', { partNumber: '7490365' }),
      fileFull('a2', '2024-01-01', { partNumber: '7490365' }, 150),
    ])
    const g = groups.find((x) => x.partNumber === '7490365')
    expect(g?.pairedBy).toBe('partNumber')
    expect(g?.comparison).not.toBeNull()
    expect(g?.needsReview).toBe(false)
  })
})
