// Zeilenrollen-Registry des QAF-Preisblocks (V2-Spezifikation R-05/R-07/R-22).
//
// Warum das hier steht und nicht in summary-metrics.ts: summary-metrics.ts weiß,
// WO eine Metrik im Blatt liegt (Label-Anchoring + Zeilen-Prior je Template).
// Diese Registry sagt, WAS die Zeile im Preisblock BEDEUTET — additiv,
// Memo-Unterzeile, Zwischensumme, Endsumme, Einmalzahlung. Beides zu mischen war
// die Ursache von Befund F-01: die Preisbrücke summierte Zeilen nach dem
// Kriterium "hat ein Delta" statt nach dem Kriterium "ist additiv", buchte damit
// die Memo-Zeile "Enthaltene Verpackung und Transport" ein, ließ den echten
// additiven Block 7 aus und glich die Differenz über einen Restposten "Übrige"
// aus. Mit expliziten Rollen ist diese Fehlklasse strukturell ausgeschlossen.
//
// Additivitätsvertrag laut Template (Zelltexte L24/L29 der Zusammenfassung):
//   ANGEBOTSBASISPREIS = 1. + 2. + 3. + 4. + 5.
//   ANGEBOTSPREIS      = Summe 1. bis 8.
// Die Blöcke 4 und 6 belegen je zwei Blattzeilen (Ausschuss Material/Fertigung,
// Rohstoff-Preisanteil Material/Energie), deshalb ist blockNo kein Primärschlüssel.
//
// Pure Daten + pure Selektoren, keine I/O, kein Blattzugriff.

import type { SummaryMetricKey } from './summary-metrics'

/**
 * Rolle einer Zeile im Preisblock.
 *
 * - `additive`     — geht in ANGEBOTSPREIS ein (Blöcke 1..8), Brückenschritt
 * - `memo`         — nachrichtliche Unterzeile eines Blocks, NIE in einer Summe
 * - `subtotal`     — im Blatt berechnete Zwischensumme (Herstellkosten, Gesamtkosten, Basispreis)
 * - `total`        — ANGEBOTSPREIS
 * - `one_time`     — Einmalzahlungsposition (eigener Block unter dem Preisblock)
 * - `one_time_total` — GESAMTEINMALZAHLUNG
 * - `info`         — Anzeigezeile ohne Rechenbezug
 */
export type SummaryRowRole = 'additive' | 'memo' | 'subtotal' | 'total' | 'one_time' | 'one_time_total' | 'info'

/**
 * Woher das Anzeige-Label einer Zeile stammen kann.
 *
 * `positional` markiert Zeilen, die im Blatt bewusst KEIN eigenes Label in
 * Spalte G tragen und nur über ihre Position bestimmbar sind. Für sie ist eine
 * Label-Verifikation strukturell unmöglich — sie dürfen deshalb kein
 * Label-Mismatch-Finding erzeugen (sonst meldet jeder Lauf einen Fehlalarm).
 */
export type SummaryLabelSource = 'label' | 'positional'

export interface SummaryRowSpec {
  /** Stabiler, sprachneutraler Schlüssel der Registry. */
  rowKey: string
  /** Bindeglied zum bestehenden Parser und zur Persistenz. */
  metricKey: SummaryMetricKey
  /** Nummer im Preisblock ("1".."8"), null für alles Nicht-Additive. */
  blockNo: string | null
  role: SummaryRowRole
  labelSource: SummaryLabelSource
  unit: 'EUR_per_piece' | 'EUR'
}

/**
 * Der Preisblock in Blattreihenfolge. Diese Reihenfolge ist zugleich die
 * Ausgabereihenfolge: eine Zeile ohne Wert wird künftig mit null ausgegeben
 * statt weggelassen (R-22), damit die Zeilenmenge über alle Läufe konstant ist.
 *
 * Zur historischen Benennung von Block 7: der Metrik-Schlüssel heißt
 * `customsSupplierToBMW`, die Zeile trägt im realen Template aber "JIS" (Block 7);
 * die Zollangabe des Preisblocks ist die Memo-Zeile "Enthaltene Zölle". Der
 * Schlüssel bleibt hier unverändert, weil er persistiert ist — korrigiert wird
 * die ANZEIGE, indem das Label aus der Datei gelesen und verifiziert wird
 * (R-04). Eine Umbenennung des Schlüssels ist ein eigener Schritt mit Migration.
 */
export const SUMMARY_ROW_REGISTRY: readonly SummaryRowSpec[] = [
  { rowKey: 'summary.material', metricKey: 'materialCosts', blockNo: '1', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.manufacturing', metricKey: 'manufacturingCosts', blockNo: '2', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.production_costs_total', metricKey: 'totalProductionCosts', blockNo: null, role: 'subtotal', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.packaging_transport_included', metricKey: 'packagingTransportIncluded', blockNo: null, role: 'memo', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.customs_included', metricKey: 'customsIncluded', blockNo: null, role: 'memo', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.devices_and_tools', metricKey: 'devicesAndTools', blockNo: '3', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.scrap_material', metricKey: 'scrapMaterial', blockNo: '4', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.scrap_manufacturing', metricKey: 'scrapManufacturing', blockNo: '4', role: 'additive', labelSource: 'positional', unit: 'EUR_per_piece' },
  { rowKey: 'summary.total_costs', metricKey: 'totalCosts', blockNo: null, role: 'subtotal', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.other_surcharges', metricKey: 'otherSurcharges', blockNo: '5', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.quotation_base_price', metricKey: 'quotationBasePrice', blockNo: null, role: 'subtotal', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.raw_material_share_material', metricKey: 'rawMaterialPriceShareMaterial', blockNo: '6', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.raw_material_share_energy', metricKey: 'rawMaterialPriceShareEnergy', blockNo: '6', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  // row_key folgt der Blattwahrheit (die Spezifikation nennt ihn ebenso), der
  // metricKey bleibt aus Kompatibilität der historische — siehe Notiz oben.
  { rowKey: 'summary.jis', metricKey: 'customsSupplierToBMW', blockNo: '7', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.transport_supplier_to_bmw', metricKey: 'transportSupplierToBMW', blockNo: '8', role: 'additive', labelSource: 'label', unit: 'EUR_per_piece' },
  { rowKey: 'summary.quotation_price', metricKey: 'quotationPrice', blockNo: null, role: 'total', labelSource: 'label', unit: 'EUR_per_piece' },
  // gitleaks:allow — der Metrik-Schlüssel enthält als einziger eine Ziffer und
  // löst damit die generic-api-key-Heuristik aus (Entropie 3,75). Kein Secret,
  // sondern derselbe False-Positive-Typ wie der Fingerprint in .gitleaksignore.
  // Einheit ist eine Annahme aus dem Preisblock-Kontext: diese Zeile ist im
  // Leitfaden nicht beschrieben (siehe SUMMARY_METRIC_KEYS-Kommentar), sie
  // trägt deshalb die Rolle `info` und geht in keine Summe ein.
  { rowKey: 'summary.cost_breakdown_aw1', metricKey: 'costBreakdownAw1', blockNo: null, role: 'info', labelSource: 'label', unit: 'EUR_per_piece' }, // gitleaks:allow
  { rowKey: 'summary.one_time_development', metricKey: 'oneTimeDevelopment', blockNo: null, role: 'one_time', labelSource: 'label', unit: 'EUR' },
  { rowKey: 'summary.one_time_tools', metricKey: 'oneTimeTools', blockNo: null, role: 'one_time', labelSource: 'label', unit: 'EUR' },
  { rowKey: 'summary.one_time_total', metricKey: 'totalOneTimePayment', blockNo: null, role: 'one_time_total', labelSource: 'label', unit: 'EUR' },
] as const

const BY_METRIC: ReadonlyMap<SummaryMetricKey, SummaryRowSpec> = new Map(
  SUMMARY_ROW_REGISTRY.map((spec) => [spec.metricKey, spec]),
)

/** Registry-Eintrag einer Metrik, `undefined` wenn die Metrik nicht im Preisblock steht. */
export function registrySpecFor(metricKey: SummaryMetricKey): SummaryRowSpec | undefined {
  return BY_METRIC.get(metricKey)
}

/** Rolle einer Metrik im Preisblock. */
export function roleOfMetric(metricKey: SummaryMetricKey): SummaryRowRole | undefined {
  return BY_METRIC.get(metricKey)?.role
}

/**
 * Die additiven Zeilen in Blattreihenfolge — die einzige zulässige Grundlage
 * für Preisbrücke und Additivitätsprüfung. Wer eine Brücke aus etwas anderem
 * baut, baut wieder F-01.
 */
export function additiveRowSpecs(): SummaryRowSpec[] {
  return SUMMARY_ROW_REGISTRY.filter((spec) => spec.role === 'additive')
}
