// Unit tests for container-differ.ts (KAR-937 / Multi-QAF-Programm P2.3).
//
// FIXTURE-DATEN-REGEL (same discipline as synthetic-fixtures.ts's own header
// comment, KAR-929 adversarial review F4 — merge-blocker class): every code,
// number, label, and cell reference below is FREE INVENTION, not lifted from
// any real analyzed workbook.
//
// Synthetic, one scenario per Master-Prompt §14 finding group this module
// closes, plus determinism and review-required propagation. Real-file
// regression lives in container-differ.real-files.test.ts (env-gated).

import { describe, it, expect } from 'vitest'
import { buildCompositeCanonicalKey, makeDimensionValue } from '../identity'
import type {
  MultiQafContainer,
  MultiQafWarning,
  SharedCostProfile,
  VariantDefinition,
  VariantDimensions,
  VariantMatrixRow,
  VariantProfileBinding,
} from '../types'
import type { VariantMatchResult } from '../variant-matcher'
import {
  allMultiQafContainerVariants,
  detailLinkageStatus,
  diffContainers,
} from '../container-differ'

// ── Minimal fixture builders ────────────────────────────────────────────────

function dims(entries: Record<string, string>): VariantDimensions {
  const out: Record<string, ReturnType<typeof makeDimensionValue>> = {}
  for (const [k, v] of Object.entries(entries)) out[k] = makeDimensionValue(v)
  return out
}

function variant(id: string, overrides: Partial<VariantDefinition> = {}): VariantDefinition {
  const dimensions = overrides.dimensions ?? dims({ region: 'EU' })
  const labels = overrides.originalLabels ?? [`Label ${id}`]
  return {
    stableInternalId: id,
    originalColumn: overrides.originalColumn ?? 'Q',
    originalColumnIndex: overrides.originalColumnIndex ?? 17,
    originalVariantNumber: overrides.originalVariantNumber ?? '1',
    originalLabels: labels,
    normalizedLabels: overrides.normalizedLabels ?? labels.map((l) => l.toLowerCase()),
    compositeCanonicalKey: overrides.compositeCanonicalKey ?? buildCompositeCanonicalKey(dimensions, id),
    dimensions,
    annualVolume: overrides.annualVolume ?? 1000,
    peakVolume: overrides.peakVolume ?? null,
    lifetimeVolume: overrides.lifetimeVolume ?? null,
    currency: overrides.currency ?? 'EUR',
    activeState: overrides.activeState ?? 'active',
    sourceReferences: overrides.sourceReferences ?? [
      { sheet: 'Zusammenfassung', cell: `${overrides.originalColumn ?? 'Q'}2`, row: 1, column: overrides.originalColumnIndex ?? 17 },
    ],
    confidence: overrides.confidence ?? 0.9,
  }
}

function emptyContainer(overrides: Partial<MultiQafContainer> = {}): MultiQafContainer {
  return {
    sourceWorkbook: { fileName: null, fileHash: null, sheetNames: [], hiddenSheetNames: [] },
    detectedTemplateFamily: 'unknown',
    multiQafVersion: null,
    underlyingQafVersion: null,
    language: 'unknown',
    currencies: [],
    sharedMetadata: {},
    variantDimensions: [],
    activeVariants: [],
    inactiveVariants: [],
    auxiliaryScenarios: [],
    helperColumns: [],
    sharedMaterialMaster: [],
    sharedManufacturingProfiles: [],
    sharedToolingData: [],
    setupCostProfiles: [],
    variantProfileBindings: [],
    summaryAggregation: { perVariant: [], formulaLineageNotes: [] },
    templateFingerprint: { family: 'unknown', structuralHash: null, variantColumnCount: 0, headerRowCount: 0, knownProfile: null, classification: null },
    warnings: [],
    confidence: 0.5,
    summaryMoneyRowsByVariant: {},
    ...overrides,
  }
}

function matched(leftId: string, rightId: string): VariantMatchResult {
  return {
    kind: 'matched',
    leftIndex: 0,
    leftId,
    rightIndex: 0,
    rightId,
    stage: 'raw_exact',
    confidence: 1,
    evidence: [],
    explanation: 'test fixture',
    matchedDimensionCount: 1,
  }
}

function unmatchedLeft(leftId: string): VariantMatchResult {
  return { kind: 'unmatched_left', leftIndex: 0, leftId, reason: 'removed' }
}

function unmatchedRight(rightId: string): VariantMatchResult {
  return { kind: 'unmatched_right', rightIndex: 0, rightId, reason: 'added' }
}

function ambiguous(leftIds: string[], rightIds: string[]): VariantMatchResult {
  return {
    kind: 'ambiguous',
    leftIndices: leftIds.map((_, i) => i),
    leftIds,
    rightIndices: rightIds.map((_, i) => i),
    rightIds,
    candidates: [],
    reviewRelevant: true,
    explanation: 'test fixture',
  }
}

function materialRow(overrides: Partial<VariantMatrixRow> = {}): VariantMatrixRow {
  return {
    canonicalComponentIdentity: overrides.canonicalComponentIdentity ?? 'pos-1',
    sourceRow: overrides.sourceRow ?? 10,
    unitCost: overrides.unitCost ?? { value: 1, currency: 'EUR' },
    procurementCurrency: overrides.procurementCurrency ?? 'EUR',
    offerCurrency: overrides.offerCurrency ?? 'EUR',
    exchangeRate: overrides.exchangeRate ?? null,
    logisticsOrDuty: overrides.logisticsOrDuty ?? null,
    materialOverhead: overrides.materialOverhead ?? null,
    quantityFactorByVariant: overrides.quantityFactorByVariant ?? {},
    effectiveCostByVariant: overrides.effectiveCostByVariant ?? {},
    formulaAndCachedValue: overrides.formulaAndCachedValue ?? null,
    sourceCells: overrides.sourceCells ?? [],
    validationStatus: overrides.validationStatus ?? 'ok',
  }
}

function profile(overrides: Partial<SharedCostProfile> = {}): SharedCostProfile {
  return {
    profileId: overrides.profileId ?? 'Fertigungskosten!U10',
    kind: overrides.kind ?? 'manufacturing',
    label: overrides.label ?? null,
    sheet: overrides.sheet ?? 'Fertigungskosten',
    values: overrides.values ?? {},
    formulaAndCachedValue: overrides.formulaAndCachedValue ?? null,
    sourceReferences: overrides.sourceReferences ?? [],
    confidence: overrides.confidence ?? 0.8,
  }
}

function binding(variantId: string, profileId: string): VariantProfileBinding {
  return { variantId, profileId, bindingEvidence: { kind: 'cellReference', source: null, detail: 'test fixture' } }
}

function warning(overrides: Partial<MultiQafWarning> = {}): MultiQafWarning {
  return {
    code: overrides.code ?? 'test_code',
    severity: overrides.severity ?? 'info',
    message: overrides.message ?? 'test warning',
    sourceReferences: overrides.sourceReferences ?? [],
    ...(overrides.variantIds !== undefined ? { variantIds: overrides.variantIds } : {}),
    ...(overrides.reviewRelevant !== undefined ? { reviewRelevant: overrides.reviewRelevant } : {}),
  }
}

// ── added / removed ──────────────────────────────────────────────────────

describe('diffContainers — added / removed variants', () => {
  it('reports a right-only variant as added, a left-only variant as removed', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1'), variant('A2')] })
    const neu = emptyContainer({ activeVariants: [variant('A1'), variant('B1')] })
    const matchResult: VariantMatchResult[] = [matched('A1', 'A1'), unmatchedLeft('A2'), unmatchedRight('B1')]

    const diff = diffContainers(alt, neu, matchResult)

    expect(diff.variants.added).toHaveLength(1)
    expect(diff.variants.added[0]!.neu.variantId).toBe('B1')
    expect(diff.variants.removed).toHaveLength(1)
    expect(diff.variants.removed[0]!.alt.variantId).toBe('A2')
  })
})

// ── renamed ──────────────────────────────────────────────────────────────

describe('diffContainers — renamed variants', () => {
  it('flags a matched pair with different labels and a changed dimension value, naming the changed dimension', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1', { originalLabels: ['Old Label'], dimensions: dims({ region: 'EU', steeringSide: 'links' }) })] })
    const neu = emptyContainer({ activeVariants: [variant('A1', { originalLabels: ['New Label'], dimensions: dims({ region: 'EU', steeringSide: 'rechts' }) })] })

    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])

    expect(diff.variants.renamed).toHaveLength(1)
    const finding = diff.variants.renamed[0]!
    expect(finding.labelsChanged).toBe(true)
    expect(finding.dimensionChanges).toHaveLength(1)
    expect(finding.dimensionChanges[0]!.key).toBe('steeringSide')
    expect(finding.dimensionChanges[0]!.altRaw).toBe('links')
    expect(finding.dimensionChanges[0]!.neuRaw).toBe('rechts')
  })

  it('does not flag a matched pair with identical labels and dimensions', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1')] })
    const neu = emptyContainer({ activeVariants: [variant('A1')] })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])
    expect(diff.variants.renamed).toHaveLength(0)
  })
})

// ── reordered ────────────────────────────────────────────────────────────

describe('diffContainers — reordered variants', () => {
  it('flags a matched pair whose column/slot moved, independent of label/dimension changes', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1', { originalColumn: 'Q', originalColumnIndex: 17, originalVariantNumber: '1' })] })
    const neu = emptyContainer({ activeVariants: [variant('A1', { originalColumn: 'AF', originalColumnIndex: 32, originalVariantNumber: '5' })] })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])

    expect(diff.variants.reordered).toHaveLength(1)
    expect(diff.variants.reordered[0]!.alt.originalColumn).toBe('Q')
    expect(diff.variants.reordered[0]!.neu.originalColumn).toBe('AF')
    expect(diff.variants.renamed).toHaveLength(0)
  })
})

// ── active/inactive/reserved transitions ────────────────────────────────

describe('diffContainers — active state transitions', () => {
  it('flags a matched pair moving from active to inactive', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1', { activeState: 'active' })] })
    const neu = emptyContainer({ inactiveVariants: [variant('A1', { activeState: 'inactive' })] })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])

    expect(diff.variants.activeStateChanges).toHaveLength(1)
    expect(diff.variants.activeStateChanges[0]!.altState).toBe('active')
    expect(diff.variants.activeStateChanges[0]!.neuState).toBe('inactive')
  })

  it('does not flag a matched pair with the same active state', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1', { activeState: 'active' })] })
    const neu = emptyContainer({ activeVariants: [variant('A1', { activeState: 'active' })] })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])
    expect(diff.variants.activeStateChanges).toHaveLength(0)
  })
})

// ── uncertain matches pass-through, no double-reporting ─────────────────

describe('diffContainers — uncertain matches', () => {
  it('passes an ambiguous finding through unchanged and does not ALSO report those variants as added/removed', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1'), variant('A2')] })
    const neu = emptyContainer({ activeVariants: [variant('B1'), variant('B2')] })
    const matchResult = [ambiguous(['A1', 'A2'], ['B1', 'B2'])]

    const diff = diffContainers(alt, neu, matchResult)

    expect(diff.variants.uncertainMatches).toHaveLength(1)
    expect(diff.variants.uncertainMatches[0]!.kind).toBe('ambiguous')
    expect(diff.variants.added).toHaveLength(0)
    expect(diff.variants.removed).toHaveLength(0)
    expect(diff.reviewRequired).toBe(true)
    expect(diff.reviewRequiredReasons).toContain('uncertain_variant_matches_present')
  })
})

// ── detail linkage status (unit-level) ──────────────────────────────────

describe('detailLinkageStatus', () => {
  it('is not_applicable for reserved variants regardless of matrix content', () => {
    const c = emptyContainer({ sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { A1: 1 } })] })
    expect(detailLinkageStatus(c, 'A1', 'reserved')).toBe('not_applicable')
  })

  it('is unknown when the shared material matrix has zero rows', () => {
    const c = emptyContainer({ sharedMaterialMaster: [] })
    expect(detailLinkageStatus(c, 'A1', 'active')).toBe('unknown')
  })

  it('is linked when a row references the variant id', () => {
    const c = emptyContainer({ sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { A1: 1 } })] })
    expect(detailLinkageStatus(c, 'A1', 'active')).toBe('linked')
  })

  it('is orphaned when the matrix is populated but no row references the variant id', () => {
    const c = emptyContainer({ sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { A2: 1 } })] })
    expect(detailLinkageStatus(c, 'A1', 'active')).toBe('orphaned')
  })
})

// ── detail linkage transitions + summary/detail presence ────────────────

describe('diffContainers — detail linkage', () => {
  it('flags a matched pair losing its material-matrix linkage', () => {
    const alt = emptyContainer({
      activeVariants: [variant('A1')],
      sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { A1: 1 } })],
    })
    const neu = emptyContainer({
      activeVariants: [variant('A1')],
      sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { OTHER: 1 } })],
      warnings: [warning({ code: 'material_matrix_variant_never_referenced', variantIds: ['A1'], message: 'no row references A1' })],
    })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])

    expect(diff.variants.detailLinkageTransitions).toHaveLength(1)
    const t = diff.variants.detailLinkageTransitions[0]!
    expect(t.altStatus).toBe('linked')
    expect(t.neuStatus).toBe('orphaned')
    expect(t.direction).toBe('lost_detail_link')
    expect(t.neuEvidence).toHaveLength(1)
  })

  it('flags a matched pair gaining material-matrix linkage', () => {
    const alt = emptyContainer({
      activeVariants: [variant('A1')],
      sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { OTHER: 1 } })],
    })
    const neu = emptyContainer({
      activeVariants: [variant('A1')],
      sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { A1: 1 } })],
    })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])

    expect(diff.variants.detailLinkageTransitions).toHaveLength(1)
    expect(diff.variants.detailLinkageTransitions[0]!.direction).toBe('gained_detail_link')
  })

  it('does not flag a transition when either side is unknown (matrix never parsed)', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1')], sharedMaterialMaster: [] })
    const neu = emptyContainer({
      activeVariants: [variant('A1')],
      sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { OTHER: 1 } })],
    })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])
    expect(diff.variants.detailLinkageTransitions).toHaveLength(0)
  })

  it('diffs detail-without-summary evidence as a message-keyed set (added/removed), not a raw per-side listing', () => {
    const alt = emptyContainer({
      warnings: [
        warning({ code: 'material_variant_unmatched_to_summary', message: 'shared column, unchanged' }),
        warning({ code: 'material_variant_unmatched_to_summary', message: 'alt-only orphan column' }),
      ],
    })
    const neu = emptyContainer({
      warnings: [
        warning({ code: 'material_variant_unmatched_to_summary', message: 'shared column, unchanged' }),
        warning({ code: 'material_variant_unmatched_to_summary', message: 'neu-only orphan column' }),
      ],
    })
    const diff = diffContainers(alt, neu, [])

    expect(diff.variants.detailWithoutSummary.added.map((e) => e.message)).toEqual(['neu-only orphan column'])
    expect(diff.variants.detailWithoutSummary.removed.map((e) => e.message)).toEqual(['alt-only orphan column'])
  })

  // KAR-937 adversarial review F2: two genuinely DIFFERENT columns that
  // happen to render an identical warning message (a fixed column and an
  // independently newly-broken column landing on the same rendered column
  // letter with the same recurring boilerplate label, after a column shift)
  // must never cancel each other out of the diff. Keying by the producer's
  // own structural `variantIds[0]` instead of message text is what this
  // regression locks in.
  it('does not let two structurally-different columns with an identical message text cancel out (F2 collision case)', () => {
    const alt = emptyContainer({
      warnings: [
        warning({
          code: 'material_variant_unmatched_to_summary',
          message: 'Material-Sheet-Spalte "F" (Option A) konnte keiner Summary-Varianten-Identität zugeordnet werden.',
          variantIds: ['mat-col-old-F'],
        }),
      ],
    })
    const neu = emptyContainer({
      warnings: [
        warning({
          // Same rendered message text as ALT's (now-fixed) entry above —
          // a DIFFERENT physical column shifted into the same letter/label
          // — but a different structural id.
          code: 'material_variant_unmatched_to_summary',
          message: 'Material-Sheet-Spalte "F" (Option A) konnte keiner Summary-Varianten-Identität zugeordnet werden.',
          variantIds: ['mat-col-new-F'],
        }),
      ],
    })
    const diff = diffContainers(alt, neu, [])

    // Both the fix (alt-only structural id gone) and the regression
    // (neu-only structural id new) must surface — neither is lost.
    expect(diff.variants.detailWithoutSummary.removed).toHaveLength(1)
    expect(diff.variants.detailWithoutSummary.added).toHaveLength(1)
  })

  it('is empty for a self-diff even when the container has unattributed detail columns', () => {
    const c = emptyContainer({ warnings: [warning({ code: 'material_variant_unmatched_to_summary', message: 'orphan column' })] })
    const diff = diffContainers(c, c, [])
    expect(diff.variants.detailWithoutSummary.added).toEqual([])
    expect(diff.variants.detailWithoutSummary.removed).toEqual([])
  })

  it('lists summary-without-detail for an added/removed variant that is also orphaned on its own side', () => {
    const alt = emptyContainer({
      activeVariants: [variant('A1')],
      sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { OTHER: 1 } })],
    })
    const neu = emptyContainer({ activeVariants: [variant('B1')], sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { OTHER: 1 } })] })
    const diff = diffContainers(alt, neu, [unmatchedLeft('A1'), unmatchedRight('B1')])

    expect(diff.variants.summaryWithoutDetail).toHaveLength(2)
    const altEntry = diff.variants.summaryWithoutDetail.find((f) => f.side === 'alt')!
    expect(altEntry.variant.variantId).toBe('A1')
  })

  it('does not list summary-without-detail for a MATCHED variant that is orphaned unchanged on both sides (nothing differs, no finding)', () => {
    const alt = emptyContainer({
      activeVariants: [variant('A1')],
      sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { OTHER: 1 } })],
    })
    const neu = emptyContainer({
      activeVariants: [variant('A1')],
      sharedMaterialMaster: [materialRow({ quantityFactorByVariant: { OTHER: 1 } })],
    })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])

    expect(diff.variants.summaryWithoutDetail).toEqual([])
    expect(diff.variants.detailLinkageTransitions).toEqual([])
  })
})

// ── dimensions ───────────────────────────────────────────────────────────

describe('diffContainers — variant dimensions (container-wide)', () => {
  it('reports added/removed dimension descriptor keys', () => {
    const alt = emptyContainer({ variantDimensions: [{ key: 'region', headerRow: 1, label: 'Region', sheet: 'Zusammenfassung' }] })
    const neu = emptyContainer({
      variantDimensions: [
        { key: 'region', headerRow: 1, label: 'Region', sheet: 'Zusammenfassung' },
        { key: 'driveType', headerRow: 2, label: 'Antrieb', sheet: 'Zusammenfassung' },
      ],
    })
    const diff = diffContainers(alt, neu, [])
    expect(diff.dimensions.added).toEqual(['driveType'])
    expect(diff.dimensions.removed).toEqual([])
  })

  // KAR-937 adversarial review F4: a container-wide dimension-descriptor-KEY
  // rename ('Farbe' -> 'Farbe_Code', same value carried over) used to fire a
  // spurious VariantRenamedFinding for EVERY matched variant instead of
  // being reported once at the container level.
  it('correlates a container-wide dimension-key rename into ONE dimensions.renamedKeys finding and suppresses the per-variant renamed flood', () => {
    const ids = ['A1', 'A2', 'A3', 'A4', 'A5']
    const alt = emptyContainer({
      activeVariants: ids.map((id) => variant(id, { dimensions: dims({ Farbe: 'Rot' }) })),
      variantDimensions: [{ key: 'Farbe', headerRow: 1, label: 'Farbe', sheet: 'Zusammenfassung' }],
    })
    const neu = emptyContainer({
      activeVariants: ids.map((id) => variant(id, { dimensions: dims({ Farbe_Code: 'Rot' }) })),
      variantDimensions: [{ key: 'Farbe_Code', headerRow: 1, label: 'Farbe Code', sheet: 'Zusammenfassung' }],
    })
    const matchResult = ids.map((id) => matched(id, id))
    const diff = diffContainers(alt, neu, matchResult)

    expect(diff.dimensions.renamedKeys).toHaveLength(1)
    expect(diff.dimensions.renamedKeys[0]).toMatchObject({ oldKey: 'Farbe', newKey: 'Farbe_Code', correlatedVariantCount: 5, correlatedVariantShare: 1 })
    expect(diff.dimensions.added).toEqual(['Farbe_Code'])
    expect(diff.dimensions.removed).toEqual(['Farbe'])
    // The per-variant flood is gone — none of the 5 matched variants are
    // reported as renamed (nothing about any single variant's own identity
    // changed).
    expect(diff.variants.renamed).toEqual([])
  })

  it('does not suppress a genuine single-variant dimension key swap that is NOT container-wide (below the 80% correlation threshold)', () => {
    const ids = ['A1', 'A2', 'A3', 'A4', 'A5']
    const alt = emptyContainer({ activeVariants: ids.map((id) => variant(id, { dimensions: dims({ region: 'EU' }) })) })
    const neu = emptyContainer({
      // Only A1 has 'region' swapped to 'zone' with the identical value — a
      // one-off quirk, not a container-wide rename. The other 4 keep
      // 'region' unchanged.
      activeVariants: [
        variant('A1', { dimensions: dims({ zone: 'EU' }) }),
        ...ids.slice(1).map((id) => variant(id, { dimensions: dims({ region: 'EU' }) })),
      ],
    })
    const matchResult = ids.map((id) => matched(id, id))
    const diff = diffContainers(alt, neu, matchResult)

    expect(diff.dimensions.renamedKeys).toEqual([])
    expect(diff.variants.renamed).toHaveLength(1)
    expect(diff.variants.renamed[0]!.alt.variantId).toBe('A1')
    expect(diff.variants.renamed[0]!.dimensionChanges).toEqual([
      { key: 'region', altRaw: 'EU', neuRaw: null },
      { key: 'zone', altRaw: null, neuRaw: 'EU' },
    ])
  })
})

// ── shared material (identity/structure only) ────────────────────────────

describe('diffContainers — shared material (structural)', () => {
  it('reports row count / column variant-id-set structure, added/removed rows by canonicalComponentIdentity, and per-row structural changes', () => {
    const alt = emptyContainer({
      sharedMaterialMaster: [
        materialRow({ canonicalComponentIdentity: 'pos-1', quantityFactorByVariant: { A1: 1 }, validationStatus: 'ok' }),
        materialRow({ canonicalComponentIdentity: 'pos-2', quantityFactorByVariant: { A1: 1 } }),
      ],
    })
    const neu = emptyContainer({
      sharedMaterialMaster: [
        materialRow({ canonicalComponentIdentity: 'pos-1', quantityFactorByVariant: { A1: 1, A2: 1 }, validationStatus: 'needs_review' }),
        materialRow({ canonicalComponentIdentity: 'pos-3', quantityFactorByVariant: { A2: 1 } }),
      ],
    })
    const diff = diffContainers(alt, neu, [])

    expect(diff.sharedMaterial.structure.altRowCount).toBe(2)
    expect(diff.sharedMaterial.structure.neuRowCount).toBe(2)
    expect(diff.sharedMaterial.structure.addedColumnVariantIds).toEqual(['A2'])
    expect(diff.sharedMaterial.addedRows.map((r) => r.canonicalComponentIdentity)).toEqual(['pos-3'])
    expect(diff.sharedMaterial.removedRows.map((r) => r.canonicalComponentIdentity)).toEqual(['pos-2'])
    expect(diff.sharedMaterial.changedRows).toHaveLength(1)
    const changed = diff.sharedMaterial.changedRows[0]!
    expect(changed.canonicalComponentIdentity).toBe('pos-1')
    expect(changed.validationStatusChanged).toBe(true)
    expect(changed.referencedVariantIdsAdded).toEqual(['A2'])
  })
})

// ── profiles ────────────────────────────────────────────────────────────

describe('diffContainers — shared profiles', () => {
  it('reports added/removed profiles by structural identity (kind + label), not raw profileId', () => {
    const alt = emptyContainer({ sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', label: 'EU Manufacturing' })] })
    const neu = emptyContainer({ sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U20', label: 'Region B Manufacturing' })] })
    const diff = diffContainers(alt, neu, [])
    expect(diff.profiles.removed.map((p) => p.profileId)).toEqual(['Fertigungskosten!U10'])
    expect(diff.profiles.added.map((p) => p.profileId)).toEqual(['Fertigungskosten!U20'])
  })

  // KAR-937 adversarial review F5: profileId is a literal `${sheet}!${cell}`
  // reference (profile-parser.ts), NOT row-shift-invariant. A row inserted
  // above a manufacturing-profile block shifts every profile's own Total
  // cell — and therefore its profileId — even though nothing about the
  // profile itself (kind, label, its position among same-kind profiles)
  // changed. This must NOT flood added/removed with the entire profile set.
  it('does not report added/removed for a pure profileId change (row shift) — same kind/label/ordinal', () => {
    const alt = emptyContainer({ sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U34', label: 'EU Manufacturing' })] })
    const neu = emptyContainer({ sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U35', label: 'EU Manufacturing' })] })
    const diff = diffContainers(alt, neu, [])
    expect(diff.profiles.added).toEqual([])
    expect(diff.profiles.removed).toEqual([])
  })

  it('disambiguates two same-kind, identically-labeled profiles by ordinal instead of colliding them', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [
        profile({ profileId: 'Fertigungskosten!U10', label: 'Manufacturing' }),
        profile({ profileId: 'Fertigungskosten!U20', label: 'Manufacturing' }),
      ],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U11', label: 'Manufacturing' })],
    })
    const diff = diffContainers(alt, neu, [])
    // First-of-kind ('Manufacturing', ordinal 0) survives (mere row shift,
    // U10 -> U11); the second ('Manufacturing', ordinal 1) is genuinely gone.
    expect(diff.profiles.added).toEqual([])
    expect(diff.profiles.removed.map((p) => p.profileId)).toEqual(['Fertigungskosten!U20'])
  })

  it('reports volumeBand threshold metadata changes for a matched profileId, and nothing else', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!B5', kind: 'volumeBand', values: { threshold: 50000 } })],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!B5', kind: 'volumeBand', values: { threshold: 60000 } })],
    })
    const diff = diffContainers(alt, neu, [])
    expect(diff.profiles.added).toHaveLength(0)
    expect(diff.profiles.removed).toHaveLength(0)
    expect(diff.profiles.volumeBandThresholdChanges).toHaveLength(1)
    expect(diff.profiles.volumeBandThresholdChanges[0]!.changes).toEqual([{ key: 'threshold', altValue: 50000, neuValue: 60000 }])
  })

  // KAR-937 adversarial review F3: `values` also carries raw cost-component
  // literals (profile-parser.ts componentValuesOnRow folds every
  // neighbouring numeric-literal cell into `values`, for every profile kind
  // including volumeBand) — this module promises VALUE comparisons are out
  // of scope (P2.5) and must never leak them through this finding group.
  it('never leaks a non-threshold VALUE key change through volumeBandThresholdChanges (F3)', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!B5', kind: 'volumeBand', values: { threshold: 50000, U: 12.5 } })],
    })
    const neu = emptyContainer({
      // threshold unchanged, but the raw cost-component literal in column U
      // changed — a plain manufacturing-cost edit, unrelated to the band
      // threshold itself.
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!B5', kind: 'volumeBand', values: { threshold: 50000, U: 99.9 } })],
    })
    const diff = diffContainers(alt, neu, [])
    expect(diff.profiles.volumeBandThresholdChanges).toEqual([])
  })

  it('still reports a threshold change alongside an unrelated VALUE-key change, filtering out only the VALUE key (F3)', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!B5', kind: 'volumeBand', values: { threshold: 50000, U: 12.5 } })],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!B5', kind: 'volumeBand', values: { threshold: 60000, U: 99.9 } })],
    })
    const diff = diffContainers(alt, neu, [])
    expect(diff.profiles.volumeBandThresholdChanges).toHaveLength(1)
    expect(diff.profiles.volumeBandThresholdChanges[0]!.changes).toEqual([{ key: 'threshold', altValue: 50000, neuValue: 60000 }])
  })

  it('reports lookup-table-shaped volumeBand structural key changes (band_N_upperBound / band_N_lotCount)', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!B5:C5', kind: 'volumeBand', values: { band_1_upperBound: 20000, band_1_lotCount: 1 } })],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!B5:C5', kind: 'volumeBand', values: { band_1_upperBound: 25000, band_1_lotCount: 1 } })],
    })
    const diff = diffContainers(alt, neu, [])
    expect(diff.profiles.volumeBandThresholdChanges).toHaveLength(1)
    expect(diff.profiles.volumeBandThresholdChanges[0]!.changes).toEqual([{ key: 'band_1_upperBound', altValue: 20000, neuValue: 25000 }])
  })

  it('reports a binding change (rebound) for a matched variant pair', () => {
    const alt = emptyContainer({
      activeVariants: [variant('A1')],
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', kind: 'manufacturing' })],
      variantProfileBindings: [binding('A1', 'Fertigungskosten!U10')],
    })
    const neu = emptyContainer({
      activeVariants: [variant('A1')],
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U20', kind: 'manufacturing' })],
      variantProfileBindings: [binding('A1', 'Fertigungskosten!U20')],
    })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])

    expect(diff.profiles.bindingChanges).toHaveLength(1)
    const b = diff.profiles.bindingChanges[0]!
    expect(b.kind).toBe('manufacturing')
    expect(b.direction).toBe('rebound')
    expect(b.altProfileId).toBe('Fertigungskosten!U10')
    expect(b.neuProfileId).toBe('Fertigungskosten!U20')
  })

  it('reports a binding change (bound) when a matched variant gains a binding it did not have before', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1')] })
    const neu = emptyContainer({
      activeVariants: [variant('A1')],
      setupCostProfiles: [profile({ profileId: 'Ruestkosten!U10', kind: 'setupCost' })],
      variantProfileBindings: [binding('A1', 'Ruestkosten!U10')],
    })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])

    expect(diff.profiles.bindingChanges).toHaveLength(1)
    expect(diff.profiles.bindingChanges[0]!.direction).toBe('bound')
    expect(diff.profiles.bindingChanges[0]!.kind).toBe('setupCost')
  })

  // KAR-937 adversarial review F6 (PLAUSIBLE, currently unreachable via the
  // sole real producer): a duplicate same-(variantId,kind) binding must fail
  // loudly instead of a silent Map last-write-wins collapse that could hide
  // a real removed-binding finding.
  it('throws instead of silently collapsing two same-kind bindings for the same variant (F6 defensive invariant)', () => {
    const alt = emptyContainer({
      activeVariants: [variant('A1')],
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', kind: 'manufacturing' }), profile({ profileId: 'Fertigungskosten!U20', kind: 'manufacturing' })],
      variantProfileBindings: [binding('A1', 'Fertigungskosten!U10'), binding('A1', 'Fertigungskosten!U20')],
    })
    const neu = emptyContainer({
      activeVariants: [variant('A1')],
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', kind: 'manufacturing' })],
      variantProfileBindings: [binding('A1', 'Fertigungskosten!U10')],
    })
    expect(() => diffContainers(alt, neu, [matched('A1', 'A1')])).toThrow(/more than one profile binding/)
  })
})

// ── template ────────────────────────────────────────────────────────────

describe('diffContainers — template', () => {
  it('reports family/classification/structural-hash changes', () => {
    const alt = emptyContainer({
      templateFingerprint: { family: 'm_qaf_1_0', structuralHash: 'hash-a', variantColumnCount: 5, headerRowCount: 1, knownProfile: null, classification: 'known' },
    })
    const neu = emptyContainer({
      templateFingerprint: { family: 'm_qaf_2_0', structuralHash: 'hash-b', variantColumnCount: 5, headerRowCount: 1, knownProfile: null, classification: 'modified' },
    })
    const diff = diffContainers(alt, neu, [])
    expect(diff.template.family.changed).toBe(true)
    expect(diff.template.family.altFamily).toBe('m_qaf_1_0')
    expect(diff.template.family.neuFamily).toBe('m_qaf_2_0')
    expect(diff.template.family.structuralHashChanged).toBe(true)
  })

  it('reports added/removed sheets and hidden-status transitions for sheets present on both sides', () => {
    const alt = emptyContainer({ sourceWorkbook: { fileName: null, fileHash: null, sheetNames: ['Zusammenfassung', 'Material', 'Setup'], hiddenSheetNames: ['Setup'] } })
    const neu = emptyContainer({ sourceWorkbook: { fileName: null, fileHash: null, sheetNames: ['Zusammenfassung', 'Material', 'Extra'], hiddenSheetNames: ['Material'] } })
    const diff = diffContainers(alt, neu, [])

    expect(diff.template.sheetSet.added).toEqual(['Extra'])
    expect(diff.template.sheetSet.removed).toEqual(['Setup'])
    // "Setup" is removed entirely (not a hidden-transition), so it must not
    // ALSO appear in becameVisible.
    expect(diff.template.sheetSet.becameVisible).toEqual([])
    expect(diff.template.sheetSet.becameHidden).toEqual(['Material'])
  })

  it('always reports externalLinks as not_tracked (the container carries no external-link inventory)', () => {
    const diff = diffContainers(emptyContainer(), emptyContainer(), [])
    expect(diff.template.externalLinks.tracked).toBe(false)
  })
})

// ── reviewRequired propagation ────────────────────────────────────────────

describe('diffContainers — reviewRequired propagation', () => {
  it('is true when either source container itself is review_required', () => {
    const alt = emptyContainer({ warnings: [warning({ severity: 'critical' })] })
    const neu = emptyContainer()
    const diff = diffContainers(alt, neu, [])
    expect(diff.reviewRequired).toBe(true)
    expect(diff.reviewRequiredReasons).toContain('alt_container_review_required')
  })

  it('is false when neither container nor the match result carries any review-relevant signal', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1')] })
    const neu = emptyContainer({ activeVariants: [variant('A1')] })
    const diff = diffContainers(alt, neu, [matched('A1', 'A1')])
    expect(diff.reviewRequired).toBe(false)
    expect(diff.reviewRequiredReasons).toEqual([])
  })

  it('honors an explicit caller override that FORCES reviewRequired true', () => {
    const diff = diffContainers(emptyContainer(), emptyContainer(), [], { forceReviewRequired: true })
    expect(diff.reviewRequired).toBe(true)
    expect(diff.reviewRequiredReasons).toEqual(['caller_override'])
  })

  it('KAR-937 F1: forceReviewRequired is additive only — it can never suppress a real uncertain_variant_matches_present signal', () => {
    const alt = emptyContainer({ activeVariants: [variant('A1'), variant('A2')] })
    const neu = emptyContainer({ activeVariants: [variant('B1'), variant('B2')] })
    const matchResult = [ambiguous(['A1', 'A2'], ['B1', 'B2'])]

    // There is no boolean "force false" anymore — forceReviewRequired only
    // accepts `true`. This test locks in that there is no way, via options,
    // to make reviewRequired come back false while uncertainMatches is
    // non-empty: omitting the option (the only other state) must still
    // surface the computed signal.
    const diff = diffContainers(alt, neu, matchResult, {})
    expect(diff.variants.uncertainMatches.length).toBeGreaterThan(0)
    expect(diff.reviewRequired).toBe(true)
    expect(diff.reviewRequiredReasons).toContain('uncertain_variant_matches_present')
  })

  it('KAR-937 F1: caller_override reason is additive alongside computed reasons, both present when both apply', () => {
    const alt = emptyContainer({ warnings: [warning({ severity: 'critical' })], activeVariants: [variant('A1'), variant('A2')] })
    const neu = emptyContainer({ activeVariants: [variant('B1'), variant('B2')] })
    const matchResult = [ambiguous(['A1', 'A2'], ['B1', 'B2'])]

    const diff = diffContainers(alt, neu, matchResult, { forceReviewRequired: true })
    expect(diff.reviewRequired).toBe(true)
    expect(diff.reviewRequiredReasons).toContain('alt_container_review_required')
    expect(diff.reviewRequiredReasons).toContain('uncertain_variant_matches_present')
    expect(diff.reviewRequiredReasons).toContain('caller_override')
  })
})

// ── fail-closed contract ──────────────────────────────────────────────────

describe('diffContainers — fail-closed matchResult contract', () => {
  it('throws when matchResult references a variant id absent from the given container', () => {
    const alt = emptyContainer()
    const neu = emptyContainer()
    expect(() => diffContainers(alt, neu, [matched('ghost', 'ghost')])).toThrow(/ghost/)
  })
})

// ── determinism ────────────────────────────────────────────────────────────

describe('diffContainers — determinism', () => {
  it('produces a byte-identical diff for structurally identical inputs built independently', () => {
    function build() {
      const alt = emptyContainer({
        activeVariants: [variant('B1'), variant('A1')],
        variantDimensions: [
          { key: 'driveType', headerRow: 2, label: 'Antrieb', sheet: 'Zusammenfassung' },
          { key: 'region', headerRow: 1, label: 'Region', sheet: 'Zusammenfassung' },
        ],
        sharedMaterialMaster: [materialRow({ canonicalComponentIdentity: 'pos-2' }), materialRow({ canonicalComponentIdentity: 'pos-1' })],
      })
      const neu = emptyContainer({ activeVariants: [variant('A2')] })
      const matchResult: VariantMatchResult[] = [unmatchedLeft('B1'), unmatchedLeft('A1'), unmatchedRight('A2')]
      return { alt, neu, matchResult }
    }
    const run1 = build()
    const run2 = build()
    const diff1 = diffContainers(run1.alt, run1.neu, run1.matchResult)
    const diff2 = diffContainers(run2.alt, run2.neu, run2.matchResult)
    expect(JSON.stringify(diff1)).toBe(JSON.stringify(diff2))
  })
})

// ── allMultiQafContainerVariants helper ────────────────────────────────────

describe('allMultiQafContainerVariants', () => {
  it('concatenates active and inactive variants', () => {
    const c = emptyContainer({ activeVariants: [variant('A1')], inactiveVariants: [variant('B1')] })
    expect(allMultiQafContainerVariants(c).map((v) => v.stableInternalId)).toEqual(['A1', 'B1'])
  })
})
