// Datenqualitäts-Befunde als Differenzen (Spezifikation Kap. 7.6).
//
// Nicht jede Differenz stammt aus einer Zuordnung. Dass Auslegungsangaben
// verschwunden sind, dass eine Infozeile ihren Bestandteilen widerspricht, dass
// ein Werkzeug ohne zugehörige Stücklistenposition dasteht — das sind
// Unterschiede zwischen den Ständen, auch wenn keine Position dabei den Besitzer
// wechselt.
//
// Sie gehören in denselben Katalog. Sonst müsste eine Folie, die sich auf einen
// solchen Befund stützt, an der Rückverfolgung vorbei belegt werden — und genau
// diese Umgehung ist der Grund, warum in der Vorgängerfassung Aussagen ohne
// Quelle stehen konnten.
//
// Pure Funktionen, keine I/O.

import type { DifferenceCell, RawDifference } from './all-differences'
import type { DqFinding } from './data-quality'

/**
 * Zellen eines Befunds in die Form des Katalogs bringen.
 *
 * Der Zellzustand ist an dieser Stelle nicht erhoben — ein Befund nennt seine
 * Fundstelle, nicht die Herkunft des Werts. `constant` zu behaupten wäre eine
 * Angabe, die niemand geprüft hat; `empty` ebenso. Deshalb wird der Zustand aus
 * dem Befund übernommen, wo er bekannt ist, und sonst als unbekannt geführt.
 */
function cellsOf(finding: DqFinding, fallback: DifferenceCell['valueState']): DifferenceCell[] {
  return finding.affectedCells.map((c) => ({
    fileRole: c.fileRole === 'both' ? 'current' : c.fileRole,
    sheet: c.sheet,
    cell: c.cell,
    formula: null,
    valueState: fallback,
  }))
}

const rowOf = (cell: string): number | null => {
  const m = /(\d+)\s*$/.exec(cell)
  return m === null ? null : Number(m[1])
}

/** Welcher Befund welchen Bereich des Katalogs betrifft. */
const AREA_BY_DETECTOR: Partial<Record<DqFinding['detector'], RawDifference['area']>> = {
  D01: 'IDT',
  D02: 'MAT',
  D03: 'SBM',
  D05: 'SBM',
  D06: 'MFG',
  D07: 'SBM',
  D08: 'SBM',
  D11: 'MFG',
  D12: 'SUM',
  D14: 'SBM',
}

/** Welche Befunde eine eigene Differenz rechtfertigen. */
const KIND_BY_DETECTOR: Partial<Record<DqFinding['detector'], RawDifference['kind']>> = {
  D05: 'cleared',
  D07: 'changed',
  D08: 'cleared',
  D11: 'added',
  D14: 'newly_filled',
}

/**
 * Differenzen aus Datenqualitäts-Befunden.
 *
 * Bewusst nicht aus jedem Befund: Ein hart codierter Satz oder ein Textdatum
 * ist eine Eigenschaft der Datei, kein Unterschied zwischen den Ständen. Sie
 * bleiben Befunde und werden über `relatedDq` an die Differenzen gehängt, zu
 * denen sie gehören.
 */
export function differencesFromFindings(findings: readonly DqFinding[]): RawDifference[] {
  const out: RawDifference[] = []

  for (const f of findings) {
    const kind = KIND_BY_DETECTOR[f.detector]
    if (kind === undefined) continue
    const area = AREA_BY_DETECTOR[f.detector] ?? 'SBM'
    const erste = f.affectedCells[0]

    out.push({
      area,
      kind,
      // Ein Befund vergleicht keine Feldwerte: Es gibt keinen Formelvergleich,
      // und das fehlende Delta hat keinen der drei Rechen-Gründe — hier stand
      // nie eine Delta-Rechnung an.
      formulaFindingKind: null,
      descriptionDe: f.descriptionDe,
      valueAward: null,
      valueCurrent: null,
      delta: null,
      deltaNullReason: null,
      unit: '-',
      cells: cellsOf(f, kind === 'cleared' ? 'empty' : 'constant'),
      relatedDq: [f.findingId],
      anchorRow: erste === undefined ? null : rowOf(erste.cell),
    })
  }

  return out
}
