// Ansicht-Partition des Differenzkatalogs (PO-Feedback Kais, 10.08.2026:
// „Wo keine Unterschiede sind, nicht auflisten — aber im JSON/Backend immer
// sehen").
//
// Diese Funktion ist REINE ANSICHTS-Logik: Katalog (buildCatalog), Persistenz,
// Exporte und das Meldewesen (validateTraceability) arbeiten weiter auf der
// vollen Record-Menge — der Aufrufer entscheidet nur, welche Partition er in
// welcher Sektion rendert. Nichts verschwindet: jede Partition wird gezählt
// und bleibt aufklappbar erreichbar (kein stilles Abschneiden — dasselbe
// Prinzip wie selectMovers/topMovers in movers.ts).
//
// Warum NICHT TableFilter.changedOnly (table-specs.ts): das arbeitet auf
// TableRow, wo formulaFindingKind bereits verloren ist, und behandelt
// delta:null als 0 — es würde Formelbefunde mit identischem Wert (Master-
// Prompt §12.4: nie verstecken) und neue/entfallene Positionen (delta:null
// ist dort artbedingt, keine Aussage über „unverändert") fälschlich
// einklappen. Die Partition muss deshalb VOR rowsFromRecords auf
// DifferenceRecord-Ebene laufen, wo beide Signale noch existieren.
//
// Messung am HICE-Golden (10.08.2026): die sichtbare 95-Zeilen-Wand der
// MFG-Tabelle besteht zu 100 % aus formel_geaendert-Befunden mit Δ 0,00 —
// deshalb ist „Formel geändert, Wert gleich" eine EIGENE Gruppe (gruppiert,
// nicht versteckt), nicht Teil von „unverändert".
//
// Pure. Kein DB-/Framework-Zugriff (check:qaf-core-portability).

import type { DifferenceRecord } from './all-differences'

/** Gleicher Wert wie table-specs.ts' DELTA_TOLERANCE (dort nicht exportiert,
 * bewusst hier benannt dupliziert mit Verweis — die beiden Schwellen meinen
 * dieselbe Anzeige-Rundungsgrenze von 2 Nachkommastellen). */
export const PARTITION_DELTA_TOLERANCE = 0.005

export interface PartitionedRecords {
  /** Echte Wert- oder Strukturänderungen — die Haupttabelle. */
  wertOderStruktur: DifferenceRecord[]
  /** Formelbefund bei praktisch unverändertem Wert (|Δ| ≤ Toleranz) —
   * eigene aufklappbare Gruppe; nie Teil von „unverändert". */
  formelOhneWertaenderung: DifferenceRecord[]
  /** kind 'changed' ohne Formelbefund mit |Δ| ≤ Toleranz — die Klappzeile
   * „N Positionen ohne nennenswerte Wertänderung". */
  ohneWertaenderung: DifferenceRecord[]
}

/**
 * Partitioniert Katalog-Sätze für die Ansicht. Sichtbarkeitsregeln:
 * - Struktur-Arten (added/removed/renamed/merged/split/…): immer Haupttabelle
 *   — delta:null ist dort artbedingt erklärt, nie „unverändert".
 * - deltaNullReason (nicht_berechenbar/nicht_anwendbar/blockiert): immer
 *   Haupttabelle — eine benannte Lücke ist ein Befund, kein Rauschen.
 * - formulaFindingKind gesetzt: bei |Δ| ≤ Toleranz eigene Formel-Gruppe,
 *   bei echter Wertänderung Haupttabelle (doppelt relevant).
 * - Rest (changed, |Δ| ≤ Toleranz): „ohne Wertänderung".
 */
export function partitionDifferenceRecords(
  records: readonly DifferenceRecord[],
  tolerance: number = PARTITION_DELTA_TOLERANCE,
): PartitionedRecords {
  const wertOderStruktur: DifferenceRecord[] = []
  const formelOhneWertaenderung: DifferenceRecord[] = []
  const ohneWertaenderung: DifferenceRecord[] = []

  for (const r of records) {
    const deltaKlein = r.delta !== null && Math.abs(r.delta) <= tolerance
    if (r.kind !== 'changed') {
      wertOderStruktur.push(r)
    } else if (r.deltaNullReason !== null) {
      wertOderStruktur.push(r)
    } else if (r.formulaFindingKind !== null) {
      if (deltaKlein) formelOhneWertaenderung.push(r)
      else wertOderStruktur.push(r)
    } else if (deltaKlein) {
      ohneWertaenderung.push(r)
    } else {
      wertOderStruktur.push(r)
    }
  }

  return { wertOderStruktur, formelOhneWertaenderung, ohneWertaenderung }
}
