import { describe, it, expect } from 'vitest'
import { COMPARISON_MODE_RULES, comparisonModeRule, normalizeComparisonMode, type QafComparisonModeKey } from '../comparison-mode'

// KAR-943 adversarial-review F2/F4 — regression coverage for the central
// comparison-mode dispatch registry. Pure, DB-free module — no
// supabase/auth mocking needed.
//
// Before this fix (F2): app/qaf-differences/actions.ts's
// replaceComparisonFile file-kind guard had no case for
// 'multi_qaf_variant_vs_standard' at all — every role for that mode fell
// into the generic "needs kind: summary" branch, so a valid Multi-QAF
// container upload into the container role (role 'neu') was rejected AND
// deleted. Before F4: exportQafComparisonXlsx only guarded
// 'g60'/'multi_qaf' — a 'multi_qaf_variant_vs_standard' comparison fell
// through into the Summary export path and would have produced a
// misleadingly empty XLSX (no qaf_manufacturing_step rows exist for this
// mode either).

const ALL_MODES: readonly QafComparisonModeKey[] = [
  'g60',
  'multi_qaf',
  'multi_qaf_variant_vs_standard',
  'summary',
  'supplier_benchmark',
]

describe('normalizeComparisonMode', () => {
  it('passes through the three known literal modes unchanged', () => {
    expect(normalizeComparisonMode('g60')).toBe('g60')
    expect(normalizeComparisonMode('multi_qaf')).toBe('multi_qaf')
    expect(normalizeComparisonMode('multi_qaf_variant_vs_standard')).toBe('multi_qaf_variant_vs_standard')
  })

  it('collapses null/undefined/any other string to "summary" — the historical implicit else', () => {
    expect(normalizeComparisonMode(null)).toBe('summary')
    expect(normalizeComparisonMode(undefined)).toBe('summary')
    expect(normalizeComparisonMode('')).toBe('summary')
    expect(normalizeComparisonMode('something-unrecognized')).toBe('summary')
  })
})

describe('COMPARISON_MODE_RULES — registry shape', () => {
  it('has exactly one entry per QafComparisonModeKey, no more, no less', () => {
    expect(Object.keys(COMPARISON_MODE_RULES).sort()).toEqual([...ALL_MODES].sort())
  })

  it('every wrongKindError/pinsNotSupportedError/swapNotSupportedError/exportNotSupportedError is a non-empty string exactly when its *Supported flag is false', () => {
    for (const mode of ALL_MODES) {
      const rule = COMPARISON_MODE_RULES[mode]
      expect(rule.alt.wrongKindError.length).toBeGreaterThan(0)
      expect(rule.neu.wrongKindError.length).toBeGreaterThan(0)
      if (!rule.exportSupported) expect(rule.exportNotSupportedError?.length ?? 0).toBeGreaterThan(0)
      if (!rule.pinsSupported) expect(rule.pinsNotSupportedError?.length ?? 0).toBeGreaterThan(0)
      if (!rule.swapSupported) expect(rule.swapNotSupportedError?.length ?? 0).toBeGreaterThan(0)
    }
  })
})

describe('comparisonModeRule — F2 regression: multi_qaf_variant_vs_standard needs DIFFERENT kinds per role', () => {
  it('the standard role (alt / baseline_file_id) expects a Summary-QAF, NOT a Multi-QAF container', () => {
    const rule = comparisonModeRule('multi_qaf_variant_vs_standard')
    expect(rule.alt.expectedKinds).toEqual(['summary'])
  })

  it('the container role (neu / comparison_file_id) expects a Multi-QAF file, NOT a Summary-QAF — this was the F2 bug: both roles used to require "summary", rejecting-and-deleting a valid container replacement', () => {
    const rule = comparisonModeRule('multi_qaf_variant_vs_standard')
    expect(rule.neu.expectedKinds).toEqual(['multi_qaf'])
  })

  it('g60 and multi_qaf require the SAME kind on both roles (symmetric pairing, unlike variant_vs_standard)', () => {
    expect(comparisonModeRule('g60').alt.expectedKinds).toEqual(['g60'])
    expect(comparisonModeRule('g60').neu.expectedKinds).toEqual(['g60'])
    expect(comparisonModeRule('multi_qaf').alt.expectedKinds).toEqual(['multi_qaf'])
    expect(comparisonModeRule('multi_qaf').neu.expectedKinds).toEqual(['multi_qaf'])
  })

  it('a null/unrecognized comparison_mode resolves to the summary rule (both roles expect kind summary)', () => {
    const rule = comparisonModeRule(null)
    expect(rule).toBe(COMPARISON_MODE_RULES.summary)
    expect(rule.alt.expectedKinds).toEqual(['summary'])
    expect(rule.neu.expectedKinds).toEqual(['summary'])
  })
})

describe('comparisonModeRule — F4 regression: export support is explicit per mode, never a silent fallthrough', () => {
  it('g60/multi_qaf/multi_qaf_variant_vs_standard all have exportSupported: false with a non-empty error message', () => {
    for (const mode of ['g60', 'multi_qaf', 'multi_qaf_variant_vs_standard'] as const) {
      const rule = comparisonModeRule(mode)
      expect(rule.exportSupported).toBe(false)
      expect(rule.exportNotSupportedError?.length ?? 0).toBeGreaterThan(0)
    }
  })

  it('the two Multi-QAF-family export errors (multi_qaf/multi_qaf_variant_vs_standard) are bilingual (DE / EN), matching this package\'s existing bilingual-message discipline', () => {
    expect(comparisonModeRule('multi_qaf').exportNotSupportedError).toContain('/')
    expect(comparisonModeRule('multi_qaf_variant_vs_standard').exportNotSupportedError).toContain('/')
  })

  it('only the plain Summary mode supports the XLSX export today', () => {
    expect(comparisonModeRule('summary').exportSupported).toBe(true)
    expect(comparisonModeRule(null).exportSupported).toBe(true)
  })
})

describe('comparisonModeRule — pins/swap applicability', () => {
  it('pins (Summary-only step-matching) are only supported for the plain summary mode', () => {
    expect(comparisonModeRule('g60').pinsSupported).toBe(false)
    expect(comparisonModeRule('multi_qaf').pinsSupported).toBe(false)
    expect(comparisonModeRule('multi_qaf_variant_vs_standard').pinsSupported).toBe(false)
    expect(comparisonModeRule('summary').pinsSupported).toBe(true)
  })

  it('swap is supported everywhere EXCEPT multi_qaf_variant_vs_standard (asymmetric container+variant vs. single standard file pairing)', () => {
    expect(comparisonModeRule('g60').swapSupported).toBe(true)
    expect(comparisonModeRule('multi_qaf').swapSupported).toBe(true)
    expect(comparisonModeRule('summary').swapSupported).toBe(true)
    expect(comparisonModeRule('multi_qaf_variant_vs_standard').swapSupported).toBe(false)
  })
})

describe('supplier_benchmark (KAR-993) — der einzige format-agnostische Modus', () => {
  it('nimmt auf BEIDEN Seiten Multi-QAF ODER Summary an', () => {
    const rule = comparisonModeRule('supplier_benchmark')
    expect(rule.alt.expectedKinds).toEqual(['multi_qaf', 'summary'])
    expect(rule.neu.expectedKinds).toEqual(['multi_qaf', 'summary'])
  })

  it('nimmt KEIN G60-Detail-QAF an — dort gibt es kein Summary-Blatt, aus dem sich Anteile bilden ließen', () => {
    const rule = comparisonModeRule('supplier_benchmark')
    expect(rule.alt.expectedKinds).not.toContain('g60')
    expect(rule.neu.expectedKinds).not.toContain('g60')
  })

  it('fordert KEINE Sachnummern-Gleichheit — verschiedene Teile sind der Zweck des Modus', () => {
    const rule = comparisonModeRule('supplier_benchmark')
    expect(rule.alt.partNumberMustMatch).toBe(false)
    expect(rule.neu.partNumberMustMatch).toBe(false)
  })

  it('unterstützt kein Pinning (es wird nichts zugeordnet), aber Rollentausch (reine Anzeige-Reihenfolge)', () => {
    const rule = comparisonModeRule('supplier_benchmark')
    expect(rule.pinsSupported).toBe(false)
    expect(rule.pinsNotSupportedError).toContain('/')
    expect(rule.swapSupported).toBe(true)
  })

  it('nutzt einen eigenen Export, nicht den Summary-Export-Pfad', () => {
    const rule = comparisonModeRule('supplier_benchmark')
    expect(rule.exportSupported).toBe(false)
    expect(rule.exportNotSupportedError).toContain('/')
  })

  it('wird aus dem persistierten String erkannt und fällt nicht auf summary zurück', () => {
    expect(normalizeComparisonMode('supplier_benchmark')).toBe('supplier_benchmark')
    expect(comparisonModeRule('supplier_benchmark')).toBe(COMPARISON_MODE_RULES.supplier_benchmark)
  })
})

describe('partNumberMustMatch — Werte der Vorgänger-Modi unverändert (Verhaltensgleichheit)', () => {
  it('entspricht genau der alten Bedingung expectedKind === summary', () => {
    // Vor KAR-993 galt die Sachnummern-Prüfung dort, wo die Rolle 'summary'
    // erwartete. Diese Erwartung friert das für die vier Vorgänger-Modi ein,
    // damit die Umstellung auf das explizite Flag nichts verschiebt.
    expect(COMPARISON_MODE_RULES.g60.alt.partNumberMustMatch).toBe(false)
    expect(COMPARISON_MODE_RULES.g60.neu.partNumberMustMatch).toBe(false)
    expect(COMPARISON_MODE_RULES.multi_qaf.alt.partNumberMustMatch).toBe(false)
    expect(COMPARISON_MODE_RULES.multi_qaf.neu.partNumberMustMatch).toBe(false)
    expect(COMPARISON_MODE_RULES.multi_qaf_variant_vs_standard.alt.partNumberMustMatch).toBe(true)
    expect(COMPARISON_MODE_RULES.multi_qaf_variant_vs_standard.neu.partNumberMustMatch).toBe(false)
    expect(COMPARISON_MODE_RULES.summary.alt.partNumberMustMatch).toBe(true)
    expect(COMPARISON_MODE_RULES.summary.neu.partNumberMustMatch).toBe(true)
  })

  it('ist genau dann true, wenn die Rolle AUSSCHLIESSLICH summary erwartet', () => {
    // Die Regel, die die alte Ableitung implizit hatte — hier explizit als
    // Invariante festgehalten. supplier_benchmark ist die Ausnahme, die die
    // Ableitung gebrochen hätte: es akzeptiert summary, will aber kein Matching.
    for (const mode of ALL_MODES) {
      const rule = COMPARISON_MODE_RULES[mode]
      for (const role of ['alt', 'neu'] as const) {
        const r = rule[role]
        const onlySummary = r.expectedKinds.length === 1 && r.expectedKinds[0] === 'summary'
        if (mode === 'supplier_benchmark') {
          expect(r.partNumberMustMatch).toBe(false)
        } else {
          expect(r.partNumberMustMatch).toBe(onlySummary)
        }
      }
    }
  })
})
