// Validierungs-Sektion des Ergebnis-JSON (Spezifikation Kap. 7.8, R-14;
// Befund F-05).
//
// V1 hatte keinen Validierungsblock. Es gab keine Kontrollsummen, keine
// benannten Prüfungen, keine Toleranzdefinition — und das Brücken-Residual
// wurde verschluckt statt gemeldet. Ein Lauf konnte also fehlerhaft sein, ohne
// dass irgendetwas im Dokument darauf hinwies.
//
// Der Grundsatz der Spezifikation: ein Lauf schlägt sichtbar fehl, statt still
// zu approximieren. Diese Sektion macht die Prüfungen benennbar — mit Messwert
// und Grenze, damit ein Konsument nicht nur „bestanden" liest, sondern nachrechnen
// kann.
//
// Pure Funktionen, keine I/O.

export type CheckStatus = 'pass' | 'fail' | 'not_applicable'

export interface ValidationCheck {
  checkId: string
  descriptionDe: string
  status: CheckStatus
  /** Gemessener Wert, sofern die Prüfung einen liefert. */
  measured: number | null
  /** Grenze, gegen die gemessen wurde. */
  limit: number | null
}

export interface ValidationResult {
  checks: ValidationCheck[]
  /** Zusammenfassung: hat mindestens eine Prüfung versagt? */
  passed: boolean
  counts: { pass: number; fail: number; not_applicable: number }
}

/** Was die Prüfungen aus den fertigen Sichten lesen. */
export interface ValidationInput {
  bridge?: { residual: number; residualWithinTolerance: boolean } | null
  movers?: { reconciliation: { residual: number | null; passed: boolean | null } } | null
  /** Zeilen der Kennzahlen-Tabelle mit ihrem Verifikationsstand. */
  metricsRows?: Array<{ labelVerified?: boolean | null }> | null
  /** Erwartete Zeilenzahl laut Registry — Schema-Stabilität (R-22). */
  expectedMetricsRowCount?: number
  contentHashPresent?: boolean
  tolerances: { bridgeEur: number; reconciliationEur: number }
}

function check(
  checkId: string,
  descriptionDe: string,
  status: CheckStatus,
  measured: number | null = null,
  limit: number | null = null,
): ValidationCheck {
  return { checkId, descriptionDe, status, measured, limit }
}

/**
 * Die Prüfungen, die sich aus dem heutigen Datenbestand belegen lassen.
 *
 * Bewusst nur diese: Ein Check, der mangels Daten immer „bestanden" meldet,
 * ist schlimmer als keiner — er erzeugt Vertrauen, das er nicht deckt. Fehlt
 * die Grundlage, steht `not_applicable` da.
 */
export function buildValidation(input: ValidationInput): ValidationResult {
  const checks: ValidationCheck[] = []

  // 1. Schliesst die Preisbrücke aus ihren additiven Zeilen? (R-06)
  if (input.bridge) {
    checks.push(
      check(
        'bridge_closes',
        'Die Preisbrücke wird vollständig aus additiven Zeilen erklärt.',
        input.bridge.residualWithinTolerance ? 'pass' : 'fail',
        Math.abs(input.bridge.residual),
        input.tolerances.bridgeEur,
      ),
    )
  } else {
    checks.push(check('bridge_closes', 'Die Preisbrücke wird vollständig aus additiven Zeilen erklärt.', 'not_applicable'))
  }

  // 2. Deckt sich die Treiber-Liste mit der Blattzeile, aus der sie stammt? (R-20)
  const rec = input.movers?.reconciliation
  if (rec && rec.passed !== null && rec.residual !== null) {
    checks.push(
      check(
        'movers_reconcile',
        'Die Treiber-Liste erklärt das Delta ihrer Quellzeile.',
        rec.passed ? 'pass' : 'fail',
        Math.abs(rec.residual),
        input.tolerances.reconciliationEur,
      ),
    )
  } else {
    checks.push(
      check('movers_reconcile', 'Die Treiber-Liste erklärt das Delta ihrer Quellzeile.', 'not_applicable'),
    )
  }

  // 3. Ist die Zeilenmenge vollständig? (R-22, Befund F-19)
  if (input.metricsRows && typeof input.expectedMetricsRowCount === 'number') {
    checks.push(
      check(
        'metrics_row_set_complete',
        'Die Kennzahlen-Tabelle führt die vollständige Zeilenmenge der Registry.',
        input.metricsRows.length === input.expectedMetricsRowCount ? 'pass' : 'fail',
        input.metricsRows.length,
        input.expectedMetricsRowCount,
      ),
    )
  } else {
    checks.push(
      check('metrics_row_set_complete', 'Die Kennzahlen-Tabelle führt die vollständige Zeilenmenge der Registry.', 'not_applicable'),
    )
  }

  // 4. Trägt eine Zeile ein Label, das die Datei nicht bestätigt? (R-04, Befund F-02)
  if (input.metricsRows && input.metricsRows.length > 0) {
    const contradicted = input.metricsRows.filter((r) => r.labelVerified === false).length
    checks.push(
      check(
        'registry_labels_verified',
        'Kein angezeigtes Label widerspricht der Datei.',
        contradicted === 0 ? 'pass' : 'fail',
        contradicted,
        0,
      ),
    )
  } else {
    checks.push(check('registry_labels_verified', 'Kein angezeigtes Label widerspricht der Datei.', 'not_applicable'))
  }

  // 5. Ist der Lauf reproduzierbar belegt? (R-19, Befund F-15)
  checks.push(
    check(
      'determinism_hash_present',
      'Das Dokument trägt einen Inhalts-Hash für den Determinismus-Nachweis.',
      input.contentHashPresent ? 'pass' : 'fail',
    ),
  )

  const counts = {
    pass: checks.filter((c) => c.status === 'pass').length,
    fail: checks.filter((c) => c.status === 'fail').length,
    not_applicable: checks.filter((c) => c.status === 'not_applicable').length,
  }
  return { checks, passed: counts.fail === 0, counts }
}
