// Unit tests for profile-differ.ts (KAR-939 / Multi-QAF-Programm P2.5).
//
// FIXTURE-DATEN-REGEL (same discipline as container-differ.test.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 §12 finding group this module
// closes (items 1/2/4/5/6/7/8 — item 3 is deliberately NOT this module's
// job, see the dedicated boundary test below), plus determinism and
// self-diff safety. Real-file regression lives in
// profile-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,
  VariantProfileBinding,
} from '../types'
import type { VariantMatchResult } from '../variant-matcher'
import { DEFAULT_PROFILE_DIFFER_CONFIG, diffProfiles } from '../profile-differ'

// ── Minimal fixture builders (mirrors container-differ.test.ts /
// material-differ.test.ts's own local builders — established per-test-file
// duplication precedent, not shared). ───────────────────────────────────────

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 !== undefined ? 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 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 } : {}),
  }
}

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 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 ambiguous',
  }
}

// ── Item 1: component value changed ─────────────────────────────────────────

describe('diffProfiles — item 1: component value changed', () => {
  it('reports a component-value change for a structurally matched profile, excluding totalPerUnit', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50, U: 10 } })],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U12', label: 'LV Detail EU', values: { totalPerUnit: 50, U: 12 } })],
    })

    const diff = diffProfiles(alt, neu, [])

    expect(diff.componentValueChanges).toHaveLength(1)
    const finding = diff.componentValueChanges[0]!
    expect(finding.changes).toEqual([expect.objectContaining({ key: 'U', altValue: 10, neuValue: 12 })])
    // totalPerUnit unchanged -> no item-2 finding.
    expect(diff.totalChanges).toEqual([])
  })
})

// ── KAR-945 / KAR-942 adversarial-review F2 fix (13.07.2026): non-numeric
// profile value fields must be compared for real equality, not unconditionally
// flagged as 'nicht_berechenbar' ──────────────────────────────────────────────

describe('diffProfiles — non-numeric component values (KAR-945)', () => {
  it('an identical non-numeric field (e.g. `site`) on both sides produces NO componentValueChanges finding', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50, site: 'Werk Nordwerk' } })],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50, site: 'Werk Nordwerk' } })],
    })

    const diff = diffProfiles(alt, neu, [])

    expect(diff.componentValueChanges).toEqual([])
  })

  it('an identical non-numeric field differing only in surrounding whitespace still resolves to no finding (trim/normalize)', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50, site: 'Werk Nordwerk' } })],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50, site: '  Werk Nordwerk  ' } })],
    })

    const diff = diffProfiles(alt, neu, [])

    expect(diff.componentValueChanges).toEqual([])
  })

  it('a genuinely different non-numeric field reports a real change with alt/neu text and status nicht_berechenbar (no numeric delta possible)', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50, site: 'Werk Nordwerk' } })],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50, site: 'Werk Suedwerk' } })],
    })

    const diff = diffProfiles(alt, neu, [])

    expect(diff.componentValueChanges).toHaveLength(1)
    const finding = diff.componentValueChanges[0]!
    expect(finding.changes).toEqual([
      expect.objectContaining({ key: 'site', altValue: 'Werk Nordwerk', neuValue: 'Werk Suedwerk', deltaAbsolute: null, deltaPercent: null, status: 'nicht_berechenbar' }),
    ])
  })
})

// ── Item 2: profile total changed ───────────────────────────────────────────

describe('diffProfiles — item 2: profile total changed', () => {
  it('reports a total change separate from component changes, with correctly identified affected bound variants', () => {
    const altProfile = profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50 } })
    const neuProfile = profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 55 } })
    const alt = emptyContainer({
      activeVariants: [variant('V1'), variant('V2')],
      sharedManufacturingProfiles: [altProfile],
      variantProfileBindings: [binding('V1', altProfile.profileId), binding('V2', altProfile.profileId)],
    })
    const neu = emptyContainer({
      activeVariants: [variant('V1'), variant('V2')],
      sharedManufacturingProfiles: [neuProfile],
      variantProfileBindings: [binding('V1', neuProfile.profileId), binding('V2', neuProfile.profileId)],
    })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1'), matched('V2', 'V2')])

    expect(diff.totalChanges).toHaveLength(1)
    const finding = diff.totalChanges[0]!
    expect(finding.altTotal).toBe(50)
    expect(finding.neuTotal).toBe(55)
    expect(finding.deltaAbsolute).toBeCloseTo(5, 4)
    expect(finding.affectedVariantIds).toEqual(['V1', 'V2'])
    expect(diff.componentValueChanges).toEqual([])
  })

  it('does NOT list a variant as affected once its binding also changed (that is item 4s job)', () => {
    const altP = profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50 } })
    const otherAltP = profile({ profileId: 'Fertigungskosten!U20', label: 'Other', values: { totalPerUnit: 20 } })
    const neuP = profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 55 } })
    const alt = emptyContainer({
      activeVariants: [variant('V1'), variant('V2')],
      sharedManufacturingProfiles: [altP, otherAltP],
      variantProfileBindings: [binding('V1', altP.profileId), binding('V2', otherAltP.profileId)],
    })
    const neu = emptyContainer({
      activeVariants: [variant('V1'), variant('V2')],
      sharedManufacturingProfiles: [neuP],
      variantProfileBindings: [binding('V1', neuP.profileId), binding('V2', neuP.profileId)],
    })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1'), matched('V2', 'V2')])

    expect(diff.totalChanges).toHaveLength(1)
    expect(diff.totalChanges[0]!.affectedVariantIds).toEqual(['V1']) // V2 rebound, not "still bound + value changed".
    expect(diff.bindingValueImpacts.some((f) => f.alt.variantId === 'V2' && f.direction === 'rebound')).toBe(true)
  })
})

// ── #309-boundary: volume-band threshold changes are NOT reported here ─────

describe('diffProfiles — #309 boundary: no double-report of volume-band threshold changes', () => {
  it('a threshold-only change on a volumeBand profile produces NEITHER a componentValueChanges NOR a totalChanges finding', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U40', kind: 'volumeBand', label: '>50.000 Stk/a', values: { threshold: 50000 } })],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U40', kind: 'volumeBand', label: '>50.000 Stk/a', values: { threshold: 60000 } })],
    })

    const diff = diffProfiles(alt, neu, [])

    expect(diff.componentValueChanges).toEqual([])
    expect(diff.totalChanges).toEqual([])
  })

  it('a volumeBand lookup-table band_N_upperBound/lotCount change is also excluded', () => {
    const alt = emptyContainer({
      sharedManufacturingProfiles: [
        profile({ profileId: 'Rüstkosten!A1:B1', kind: 'volumeBand', label: 'Jahresvolumen / Anzahl Lose', values: { band_1_upperBound: 10000, band_1_lotCount: 2 } }),
      ],
    })
    const neu = emptyContainer({
      sharedManufacturingProfiles: [
        profile({ profileId: 'Rüstkosten!A1:B1', kind: 'volumeBand', label: 'Jahresvolumen / Anzahl Lose', values: { band_1_upperBound: 12000, band_1_lotCount: 3 } }),
      ],
    })

    const diff = diffProfiles(alt, neu, [])

    expect(diff.componentValueChanges).toEqual([])
    expect(diff.totalChanges).toEqual([])
  })
})

// ── Item 4: variant moved to another profile (value impact) ────────────────

describe('diffProfiles — item 4: variant moved to another profile (value impact)', () => {
  it('reports rebound with altValue/neuValue and a computable delta when currencies agree', () => {
    const altP = profile({ profileId: 'Fertigungskosten!U10', label: 'A', values: { totalPerUnit: 40 } })
    const neuP = profile({ profileId: 'Fertigungskosten!U20', label: 'B', values: { totalPerUnit: 44 } })
    const alt = emptyContainer({
      activeVariants: [variant('V1', { currency: 'EUR' })],
      sharedManufacturingProfiles: [altP],
      variantProfileBindings: [binding('V1', altP.profileId)],
    })
    const neu = emptyContainer({
      activeVariants: [variant('V1', { currency: 'EUR' })],
      sharedManufacturingProfiles: [neuP],
      variantProfileBindings: [binding('V1', neuP.profileId)],
    })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.bindingValueImpacts).toHaveLength(1)
    const f = diff.bindingValueImpacts[0]!
    expect(f.direction).toBe('rebound')
    expect(f.altValue).toBe(40)
    expect(f.neuValue).toBe(44)
    expect(f.currencyState).toBe('same_currency')
    expect(f.deltaAbsolute).toBeCloseTo(4, 4)
  })

  it('reports bound/unbound directions for a variant gaining/losing a binding', () => {
    const neuP = profile({ profileId: 'Fertigungskosten!U20', label: 'B', values: { totalPerUnit: 44 } })
    const altP = profile({ profileId: 'Fertigungskosten!U10', label: 'A', values: { totalPerUnit: 40 } })

    const altBound = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [altP], variantProfileBindings: [binding('V1', altP.profileId)] })
    const neuUnbound = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [altP], variantProfileBindings: [] })
    const diffUnbind = diffProfiles(altBound, neuUnbound, [matched('V1', 'V1')])
    expect(diffUnbind.bindingValueImpacts).toHaveLength(1)
    expect(diffUnbind.bindingValueImpacts[0]!.direction).toBe('unbound')

    const altUnbound = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [neuP], variantProfileBindings: [] })
    const neuBound = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [neuP], variantProfileBindings: [binding('V1', neuP.profileId)] })
    const diffBind = diffProfiles(altUnbound, neuBound, [matched('V1', 'V1')])
    expect(diffBind.bindingValueImpacts).toHaveLength(1)
    expect(diffBind.bindingValueImpacts[0]!.direction).toBe('bound')
  })

  it('does NOT report a binding change for a profile that merely shifted rows (same structural identity)', () => {
    const altP = profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', kind: 'manufacturing', values: { totalPerUnit: 40 } })
    const neuP = profile({ profileId: 'Fertigungskosten!U11', label: 'LV Detail EU', kind: 'manufacturing', values: { totalPerUnit: 40 } }) // row-shifted, same structural identity.
    const alt = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [altP], variantProfileBindings: [binding('V1', altP.profileId)] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [neuP], variantProfileBindings: [binding('V1', neuP.profileId)] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.bindingValueImpacts).toEqual([])
  })

  it('gates the delta to null with currencyState=currency_changed when the matched variants own currency differs', () => {
    const altP = profile({ profileId: 'Fertigungskosten!U10', label: 'A', values: { totalPerUnit: 40 } })
    const neuP = profile({ profileId: 'Fertigungskosten!U20', label: 'B', values: { totalPerUnit: 44 } })
    const alt = emptyContainer({ activeVariants: [variant('V1', { currency: 'EUR' })], sharedManufacturingProfiles: [altP], variantProfileBindings: [binding('V1', altP.profileId)] })
    const neu = emptyContainer({ activeVariants: [variant('V1', { currency: 'USD' })], sharedManufacturingProfiles: [neuP], variantProfileBindings: [binding('V1', neuP.profileId)] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.bindingValueImpacts).toHaveLength(1)
    const f = diff.bindingValueImpacts[0]!
    expect(f.currencyState).toBe('currency_changed')
    expect(f.deltaAbsolute).toBeNull()
    expect(f.status).toBeNull()
    // The raw values are still surfaced even though no delta was fabricated.
    expect(f.altValue).toBe(40)
    expect(f.neuValue).toBe(44)
  })

  it('gates the delta to null with currencyState=currency_unknown when a variants currency is null', () => {
    const altP = profile({ profileId: 'Fertigungskosten!U10', label: 'A', values: { totalPerUnit: 40 } })
    const neuP = profile({ profileId: 'Fertigungskosten!U20', label: 'B', values: { totalPerUnit: 44 } })
    const alt = emptyContainer({ activeVariants: [variant('V1', { currency: null })], sharedManufacturingProfiles: [altP], variantProfileBindings: [binding('V1', altP.profileId)] })
    const neu = emptyContainer({ activeVariants: [variant('V1', { currency: 'EUR' })], sharedManufacturingProfiles: [neuP], variantProfileBindings: [binding('V1', neuP.profileId)] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.bindingValueImpacts).toHaveLength(1)
    expect(diff.bindingValueImpacts[0]!.currencyState).toBe('currency_unknown')
    expect(diff.bindingValueImpacts[0]!.deltaAbsolute).toBeNull()
  })
})

// ── Item 5/6: newly created / removed profile (with values) ────────────────

describe('diffProfiles — item 5/6: added/removed profile with values', () => {
  it('reports an added profile with its full values and totalPerUnit', () => {
    const alt = emptyContainer({})
    const neu = emptyContainer({ sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U30', label: 'New Profile', values: { totalPerUnit: 33, U: 5 } })] })

    const diff = diffProfiles(alt, neu, [])

    expect(diff.added).toHaveLength(1)
    expect(diff.added[0]!.values).toEqual({ totalPerUnit: 33, U: 5 })
    expect(diff.added[0]!.totalPerUnit).toBe(33)
    expect(diff.removed).toEqual([])
  })

  it('reports a removed profile with its LAST KNOWN values', () => {
    const alt = emptyContainer({ sharedManufacturingProfiles: [profile({ profileId: 'Fertigungskosten!U30', label: 'Gone Profile', values: { totalPerUnit: 33, U: 5 } })] })
    const neu = emptyContainer({})

    const diff = diffProfiles(alt, neu, [])

    expect(diff.removed).toHaveLength(1)
    expect(diff.removed[0]!.values).toEqual({ totalPerUnit: 33, U: 5 })
    expect(diff.added).toEqual([])
  })
})

// ── Item 7: inconsistent binding (unbound / unresolved / ambiguous / dangling) ─

describe('diffProfiles — item 7: inconsistent binding', () => {
  it('reports a newly unbound variant as `added` (present in NEU, not ALT)', () => {
    const p = profile({ profileId: 'Fertigungskosten!U10' })
    const alt = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [p], variantProfileBindings: [binding('V1', p.profileId)] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [p], variantProfileBindings: [] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.inconsistentBindings.added).toEqual([expect.objectContaining({ variantId: 'V1', reason: 'unbound' })])
    expect(diff.inconsistentBindings.removed).toEqual([])
  })

  it('surfaces unresolved_reference / ambiguous_reference from container warnings', () => {
    const unresolvedWarning = warning({ code: 'variant_profile_binding_unresolved', variantIds: ['V1'], message: 'unresolved' })
    const ambiguousWarning = warning({ code: 'ambiguous_literal_profile_binding', variantIds: ['V2'], message: 'ambiguous' })
    const alt = emptyContainer({ activeVariants: [variant('V1'), variant('V2')] })
    const neu = emptyContainer({ activeVariants: [variant('V1'), variant('V2')], warnings: [unresolvedWarning, ambiguousWarning] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1'), matched('V2', 'V2')])

    const reasons = diff.inconsistentBindings.added.map((e) => e.reason).sort()
    expect(reasons).toEqual(['ambiguous_reference', 'unresolved_reference'])
  })

  it('reports a dangling profileId reference (bound to a profile that no longer exists)', () => {
    const p = profile({ profileId: 'Fertigungskosten!U10' })
    const alt = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [p], variantProfileBindings: [binding('V1', p.profileId)] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [], variantProfileBindings: [binding('V1', p.profileId)] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.inconsistentBindings.added).toEqual([expect.objectContaining({ variantId: 'V1', reason: 'unresolvable_profile_id', profileId: p.profileId })])
  })

  it('never reports a reserved (no-identity) variant as unbound', () => {
    const alt = emptyContainer({})
    const neu = emptyContainer({ inactiveVariants: [variant('SLOT1', { activeState: 'reserved' })] })

    const diff = diffProfiles(alt, neu, [])

    expect(diff.inconsistentBindings.added).toEqual([])
  })

  it('reports a target change (same reason, same variant) as BOTH added and removed (KAR-939 adversarial review F2)', () => {
    // Both warnings carry `variantIds: ['V1']` and the SAME code
    // ('variant_profile_binding_unresolved' -> reason 'unresolved_reference')
    // — pre-F2 the key was `reason::variantId::''` (profileId is always null
    // for this reason), so a variant flipping from one non-resolvable target
    // to a DIFFERENT non-resolvable target was invisible.
    const unresolvedAlt = warning({
      code: 'variant_profile_binding_unresolved',
      variantIds: ['V1'],
      message: 'Variante V1: Fertigungs-/Rüstkosten-Profilbindung nicht auflösbar (U10) — Zelle enthält weder Formel noch numerischen Literalwert.',
    })
    const unresolvedNeu = warning({
      code: 'variant_profile_binding_unresolved',
      variantIds: ['V1'],
      message: 'Variante V1: Fertigungs-/Rüstkosten-Profilbindung nicht auflösbar (U10) — Literalwert 40 entspricht keinem erkannten Profil-Total (nie geraten).',
    })
    const alt = emptyContainer({ activeVariants: [variant('V1')], warnings: [unresolvedAlt] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], warnings: [unresolvedNeu] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.inconsistentBindings.added).toEqual([expect.objectContaining({ variantId: 'V1', reason: 'unresolved_reference' })])
    expect(diff.inconsistentBindings.removed).toEqual([expect.objectContaining({ variantId: 'V1', reason: 'unresolved_reference' })])
    expect(diff.inconsistentBindings.added[0]!.targetRef).not.toBe(diff.inconsistentBindings.removed[0]!.targetRef)
    expect(diff.inconsistentBindings.added[0]!.targetRef).toContain('Literalwert 40')
    expect(diff.inconsistentBindings.removed[0]!.targetRef).toContain('Zelle enthält weder Formel')
  })

  it('does NOT report a change when the SAME target repeats (same reason, same variant, same message)', () => {
    const unresolved = warning({
      code: 'variant_profile_binding_unresolved',
      variantIds: ['V1'],
      message: 'Variante V1: Fertigungs-/Rüstkosten-Profilbindung nicht auflösbar (U10) — Zelle enthält weder Formel noch numerischen Literalwert.',
    })
    const alt = emptyContainer({ activeVariants: [variant('V1')], warnings: [unresolved] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], warnings: [unresolved] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.inconsistentBindings.added).toEqual([])
    expect(diff.inconsistentBindings.removed).toEqual([])
  })
})

// ── Item 8: summary-value reconciliation ────────────────────────────────────

describe('diffProfiles — item 8: summary reconciliation', () => {
  // KAR-939 adversarial review F1: `boundProfileValue` (now `values.
  // totalPerUnit`, F4) and `virtualVariantTotal` (`generateVirtualVariants`,
  // which ALSO reads that same profile's `values.totalPerUnit` via the same
  // binding — container-assembly.ts's `profileTotalAsMoney`) are PROVABLY
  // the same source whenever a binding resolves cleanly. Pre-fix this made
  // the comparison a silent no-op ('bestanden', no entry at all) on every
  // real freshly-parsed file. Post-fix it is reported honestly instead.

  it('pushes an explicit nicht_pruefbar/same_source_no_independent_check entry instead of silently skipping a same-source binding (F1a)', () => {
    const p = profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50 }, formulaAndCachedValue: { formula: null, cachedValue: 50 } })
    // V1 has NO binding at all in ALT, so ALT produces no reconciliation
    // entry for it — an asymmetric setup that isolates NEU's own entry.
    // Pre-fix, NEU's same-source binding would ALSO have produced nothing
    // (the old silent-skip 'bestanden' path), so `added` would stay empty
    // even here; post-fix it must show up.
    const alt = emptyContainer({ activeVariants: [variant('V1')] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [p], variantProfileBindings: [binding('V1', p.profileId)] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.summaryReconciliation.added).toEqual([
      expect.objectContaining({
        variantId: 'V1',
        status: 'nicht_pruefbar',
        nichtPruefbarReason: 'same_source_no_independent_check',
        boundProfileValue: 50,
        virtualVariantTotal: 50,
        cachedValueDrift: null,
      }),
    ])
  })

  it('reports abweichung via cachedValueDrift when a profiles cachedValue and totalPerUnit genuinely diverge (F1b, the ONE real check today)', () => {
    // formulaAndCachedValue.cachedValue stays the ORIGINAL total while
    // values.totalPerUnit is mutated — see profile-differ.ts module header
    // "Item 8 honesty fix" for why this in-profile comparison (not
    // boundProfileValue vs virtualVariantTotal, which is tautological, F4)
    // is the one genuinely independent signal this module can catch.
    const altP = profile({
      profileId: 'Fertigungskosten!U10',
      values: { totalPerUnit: 50 },
      formulaAndCachedValue: { formula: '=SUMIF(...)', cachedValue: 50 }, // same source — no drift.
    })
    const neuP = profile({
      profileId: 'Fertigungskosten!U10',
      values: { totalPerUnit: 55 },
      formulaAndCachedValue: { formula: '=SUMIF(...)', cachedValue: 50 }, // drift: cachedValue stayed, totalPerUnit moved.
    })
    const alt = emptyContainer({
      activeVariants: [variant('V1', { currency: 'EUR' })],
      sharedManufacturingProfiles: [altP],
      variantProfileBindings: [binding('V1', altP.profileId)],
    })
    const neu = emptyContainer({
      activeVariants: [variant('V1', { currency: 'EUR' })],
      sharedManufacturingProfiles: [neuP],
      variantProfileBindings: [binding('V1', neuP.profileId)],
    })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.summaryReconciliation.added).toHaveLength(1)
    const entry = diff.summaryReconciliation.added[0]!
    expect(entry.variantId).toBe('V1')
    expect(entry.status).toBe('abweichung')
    expect(entry.boundProfileValue).toBe(55) // F4: totalPerUnit primary, same source as item 4 / virtualVariantTotal.
    expect(entry.virtualVariantTotal).toBe(55)
    expect(entry.cachedValueDrift).toEqual({ cachedValue: 50, totalPerUnit: 55, deltaAbsolute: -5 })
    expect(entry.deltaAbsolute).toBeCloseTo(-5, 4)

    // ALT's own same-source entry (nicht_pruefbar) is a DIFFERENT full key
    // (different status) — it disappears on this side, i.e. shows as removed.
    expect(diff.summaryReconciliation.removed).toEqual([
      expect.objectContaining({ variantId: 'V1', status: 'nicht_pruefbar', nichtPruefbarReason: 'same_source_no_independent_check' }),
    ])
  })

  it('reports nicht_pruefbar/missing_value when the bound profile has no numeric value at all, only as a transition', () => {
    const pMissing = profile({ profileId: 'Fertigungskosten!U10', values: {}, formulaAndCachedValue: null })
    const pPresent = profile({ profileId: 'Fertigungskosten!U10', values: { totalPerUnit: 50 }, formulaAndCachedValue: { formula: null, cachedValue: 50 } })
    const alt = emptyContainer({
      activeVariants: [variant('V1')],
      sharedManufacturingProfiles: [pPresent],
      variantProfileBindings: [binding('V1', pPresent.profileId)],
    })
    const neu = emptyContainer({
      activeVariants: [variant('V1')],
      sharedManufacturingProfiles: [pMissing],
      variantProfileBindings: [binding('V1', pMissing.profileId)],
    })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.summaryReconciliation.added).toEqual([expect.objectContaining({ variantId: 'V1', status: 'nicht_pruefbar', nichtPruefbarReason: 'missing_value' })])
  })

  it('reports NOTHING (added/removed/changed) when the SAME profile is bound identically on both sides — same source cancels out in the set-diff', () => {
    const p = profile({ profileId: 'Fertigungskosten!U10', values: { totalPerUnit: 100 }, formulaAndCachedValue: { formula: null, cachedValue: 100.001 } })
    const alt = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [p], variantProfileBindings: [binding('V1', p.profileId)] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [p], variantProfileBindings: [binding('V1', p.profileId)] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.summaryReconciliation.added).toEqual([])
    expect(diff.summaryReconciliation.removed).toEqual([])
    expect(diff.summaryReconciliation.changed).toEqual([])
  })

  it('reports a `changed` entry (KAR-939 adversarial review F3) when both sides show abweichung via cachedValueDrift but with DIFFERENT deltas', () => {
    // Same base key (variantId::profileId) AND same status ('abweichung')
    // on both sides — pre-F3 this pair was silently dropped from BOTH
    // `added` and `removed` (identical full key), even though the actual
    // drift magnitude changed substantially.
    const altP = profile({ profileId: 'Fertigungskosten!U10', values: { totalPerUnit: 50 }, formulaAndCachedValue: { formula: '=SUMIF(...)', cachedValue: 45 } }) // drift -5
    const neuP = profile({ profileId: 'Fertigungskosten!U10', values: { totalPerUnit: 50 }, formulaAndCachedValue: { formula: '=SUMIF(...)', cachedValue: 20 } }) // drift -30
    const alt = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [altP], variantProfileBindings: [binding('V1', altP.profileId)] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [neuP], variantProfileBindings: [binding('V1', neuP.profileId)] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    expect(diff.summaryReconciliation.added).toEqual([])
    expect(diff.summaryReconciliation.removed).toEqual([])
    expect(diff.summaryReconciliation.changed).toHaveLength(1)
    const c = diff.summaryReconciliation.changed[0]!
    expect(c.variantId).toBe('V1')
    expect(c.profileId).toBe('Fertigungskosten!U10')
    expect(c.status).toBe('abweichung')
    expect(c.altDeltaAbsolute).toBeCloseTo(-5, 4)
    expect(c.neuDeltaAbsolute).toBeCloseTo(-30, 4)
    expect(c.band).not.toBe('konstant')
  })

  it('does NOT report `changed` when both sides are nicht_pruefbar/same_source (both deltas null -> nicht_berechenbar band, self-diff-safe)', () => {
    const altP = profile({ profileId: 'Fertigungskosten!U10', values: { totalPerUnit: 40 }, formulaAndCachedValue: { formula: null, cachedValue: 40 } })
    const neuP = profile({ profileId: 'Fertigungskosten!U10', values: { totalPerUnit: 60 }, formulaAndCachedValue: { formula: null, cachedValue: 60 } })
    const alt = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [altP], variantProfileBindings: [binding('V1', altP.profileId)] })
    const neu = emptyContainer({ activeVariants: [variant('V1')], sharedManufacturingProfiles: [neuP], variantProfileBindings: [binding('V1', neuP.profileId)] })

    const diff = diffProfiles(alt, neu, [matched('V1', 'V1')])

    // Both sides: same-source, both deltaAbsolute === null -> same full key
    // (nicht_pruefbar/same_source_no_independent_check) -> no added/removed
    // AND no `changed` (a real totalPerUnit change of 40 -> 60 is instead
    // surfaced by item 2's `totalChanges`, never double-reported here).
    expect(diff.summaryReconciliation.added).toEqual([])
    expect(diff.summaryReconciliation.removed).toEqual([])
    expect(diff.summaryReconciliation.changed).toEqual([])
  })
})

// ── Uncertain matches passthrough ───────────────────────────────────────────

describe('diffProfiles — uncertain matches passthrough', () => {
  it('passes ambiguous match results through verbatim, never resolving them', () => {
    const alt = emptyContainer({})
    const neu = emptyContainer({})
    const diff = diffProfiles(alt, neu, [ambiguous(['V1'], ['W1', 'W2'])])
    expect(diff.uncertainMatches).toHaveLength(1)
    expect(diff.uncertainMatches[0]!.kind).toBe('ambiguous')
  })
})

// ── Self-diff safety (the required anchor property) ─────────────────────────

describe('diffProfiles — self-diff safety', () => {
  it('produces zero findings in every one of the 8 groups when alt and neu are the SAME container, even with pre-existing gaps (unbound variant, dangling reference)', () => {
    const p = profile({ profileId: 'Fertigungskosten!U10', label: 'LV Detail EU', values: { totalPerUnit: 50, U: 10 }, formulaAndCachedValue: { formula: null, cachedValue: 50 } })
    const container = emptyContainer({
      activeVariants: [variant('V1'), variant('V2'), variant('V3')],
      sharedManufacturingProfiles: [p],
      // V1 bound consistently; V2 deliberately UNBOUND (a pre-existing,
      // honest gap — real-corpus evidence per profile-parser.real-files.test.ts
      // "Datei 2"); V3 bound to a DANGLING profileId (pre-existing defect).
      variantProfileBindings: [binding('V1', p.profileId), binding('V3', 'Fertigungskosten!GONE')],
    })
    const copy: MultiQafContainer = JSON.parse(JSON.stringify(container))
    const matchResult = [matched('V1', 'V1'), matched('V2', 'V2'), matched('V3', 'V3')]

    const diff = diffProfiles(container, copy, matchResult)

    expect(diff.componentValueChanges).toEqual([])
    expect(diff.totalChanges).toEqual([])
    expect(diff.bindingValueImpacts).toEqual([])
    expect(diff.added).toEqual([])
    expect(diff.removed).toEqual([])
    expect(diff.inconsistentBindings.added).toEqual([])
    expect(diff.inconsistentBindings.removed).toEqual([])
    // KAR-939 adversarial review F1: post-fix, V1's binding now DOES produce
    // an explicit nicht_pruefbar/same_source_no_independent_check entry on
    // BOTH sides (no longer a silent skip) — same variantId/profileId/
    // status/reason on both sides means the SAME set-diff key, so it still
    // cancels out to zero findings here, for an honest reason instead of an
    // accidental one (see module header "Item 8 honesty fix").
    expect(diff.summaryReconciliation.added).toEqual([])
    expect(diff.summaryReconciliation.removed).toEqual([])
    expect(diff.summaryReconciliation.changed).toEqual([])
  })
})

// ── Determinism ─────────────────────────────────────────────────────────

describe('diffProfiles — determinism', () => {
  it('produces a byte-identical result across repeated calls with the same input', () => {
    const altP = profile({ profileId: 'Fertigungskosten!U10', label: 'A', values: { totalPerUnit: 40, U: 1 } })
    const neuP = profile({ profileId: 'Fertigungskosten!U10', label: 'A', values: { totalPerUnit: 44, U: 2 } })
    const alt = emptyContainer({
      activeVariants: [variant('V1'), variant('V2')],
      sharedManufacturingProfiles: [altP],
      variantProfileBindings: [binding('V1', altP.profileId), binding('V2', altP.profileId)],
    })
    const neu = emptyContainer({
      activeVariants: [variant('V1'), variant('V2')],
      sharedManufacturingProfiles: [neuP],
      variantProfileBindings: [binding('V1', neuP.profileId), binding('V2', neuP.profileId)],
    })
    const matchResult = [matched('V1', 'V1'), matched('V2', 'V2')]

    const a = diffProfiles(alt, neu, matchResult)
    const b = diffProfiles(alt, neu, matchResult)

    expect(JSON.stringify(a)).toBe(JSON.stringify(b))
  })
})

// ── Config default sanity ───────────────────────────────────────────────────

describe('DEFAULT_PROFILE_DIFFER_CONFIG', () => {
  it('is usable as an explicit options.config without changing behavior', () => {
    const alt = emptyContainer({})
    const neu = emptyContainer({})
    expect(diffProfiles(alt, neu, [], { config: DEFAULT_PROFILE_DIFFER_CONFIG })).toEqual(diffProfiles(alt, neu, []))
  })
})
