// Adapter: persistierte QAF-Formen → Benchmark-Eingabe (KAR-993 P2).
//
// `supplier-benchmark.ts` kennt absichtlich KEIN Dateiformat — es rechnet nur
// auf `BenchmarkVariantInput[]`. Dieses Modul stellt die Brücke von den zwei
// Formen her, in denen der Ingest Kostenpositionen ablegt:
//
//   Multi-QAF  → `qaf_file.g60_meta.multiQafVirtualVariants.variants[]`,
//                je Variante ein `VirtualVariantSummaryTotals`
//   Standard   → `qaf_summary_metric`-Zeilen (eine Datei = eine Variante)
//
// Diese Trennung ist der Grund, warum 'supplier_benchmark' format-agnostisch
// sein kann (Multi-QAF gegen Standard, Standard gegen Standard): die
// Formatkenntnis endet hier, die Engine sieht nur noch eine Variantenliste.

import type { MoneyValue } from './types'
import type { BenchmarkVariantInput } from './supplier-benchmark'
import { SUMMARY_METRIC_KEYS, type SummaryMetricKey } from './summary-metrics'

/** Die Teilmenge von `VirtualQafVariant`, die der Benchmark liest — als
 * eigener, schmaler Typ, damit dieses Modul nicht den kompletten
 * Multi-QAF-Container-Typbaum importieren muss (und damit auch eine
 * rehydrierte JSONB-Form ohne Klassen-Identität passt). */
export interface VirtualVariantTotalsLike {
  variantId: string
  summaryTotals: {
    materialCosts: MoneyValue | null
    manufacturingCosts: MoneyValue | null
    totalProductionCosts: MoneyValue | null
    toolingAndFixtureCost: MoneyValue | null
    setupCostAllocation: MoneyValue | null
    scrap: MoneyValue | null
    otherSurcharges: MoneyValue | null
    offerBasePrice: MoneyValue | null
    offerPrice: MoneyValue | null
  }
}

/** Eine Zeile aus `qaf_summary_metric`. `currency` ist dort pro DATEI gesetzt
 * (persistence-mapper.ts schreibt `parse.currency` in jede Zeile), nicht pro
 * Position — bei Standard-QAFs kann es also keinen währungsgemischten
 * Positionssatz geben. */
export interface SummaryMetricRowLike {
  metric_key: string
  currency: string | null
  value: number | null
}

/**
 * Multi-QAF-Container-Varianten → Benchmark-Eingabe.
 *
 * Die Feldnamen sind bewusst deckungsgleich mit `VirtualVariantSummaryTotals`
 * gewählt, deshalb ist das eine reine Umhängung ohne Interpretation. Ein
 * `null` bleibt `null` — es wird nichts ersetzt, nichts geschätzt und nichts
 * auf 0 gesetzt, damit die Engine "nicht ermittelbar" von "ist null" nicht
 * unterscheiden muss.
 */
export function benchmarkVariantsFromVirtualVariants(
  variants: readonly VirtualVariantTotalsLike[]
): BenchmarkVariantInput[] {
  return variants.map((v) => ({
    variantId: v.variantId,
    materialCosts: v.summaryTotals.materialCosts,
    manufacturingCosts: v.summaryTotals.manufacturingCosts,
    totalProductionCosts: v.summaryTotals.totalProductionCosts,
    scrap: v.summaryTotals.scrap,
    otherSurcharges: v.summaryTotals.otherSurcharges,
    setupCostAllocation: v.summaryTotals.setupCostAllocation,
    toolingAndFixtureCost: v.summaryTotals.toolingAndFixtureCost,
    offerBasePrice: v.summaryTotals.offerBasePrice,
    offerPrice: v.summaryTotals.offerPrice,
  }))
}

/** Zuordnung Standard-Metrik → Benchmark-Feld. Nur die Positionen, die der
 * Benchmark braucht; alles andere aus SUMMARY_METRIC_KEYS bleibt bewusst
 * ungenutzt. */
const DIRECT_METRIC_MAP: Readonly<Record<string, keyof Omit<BenchmarkVariantInput, 'variantId'>>> = {
  materialCosts: 'materialCosts',
  manufacturingCosts: 'manufacturingCosts',
  totalProductionCosts: 'totalProductionCosts',
  otherSurcharges: 'otherSurcharges',
  devicesAndTools: 'toolingAndFixtureCost',
  quotationBasePrice: 'offerBasePrice',
  quotationPrice: 'offerPrice',
}

/**
 * Standard-Summary-Metriken → Benchmark-Eingabe mit GENAU EINER Variante.
 *
 * Ein Standard-QAF beschreibt einen Umfang, nicht mehrere Varianten. Für den
 * Benchmark ist das kein Sonderfall: eine Seite mit einer Variante liefert
 * Median = min = max und `contributingVariants: 1`. Die Spanne ist dann
 * trivial, die Kennzahl aber genauso vergleichbar — deshalb funktioniert
 * Multi-QAF gegen Standard ohne Zusatzlogik.
 *
 * **Ausschuss:** Der Container führt `scrap` als EINE Position (Master-Prompt
 * §13), die Standard-Metriken führen `scrapMaterial` und
 * `scrapManufacturing` getrennt. Beide werden nur dann addiert, wenn sie
 * dieselbe Währung tragen; andernfalls bleibt `scrap` null und die davon
 * abhängige Kennzahl meldet sich in der Engine als nicht ermittelbar. Eine
 * Summe über Währungsgrenzen wäre genau die stille Fehlzahl, die
 * supplier-benchmark.ts an jeder anderen Stelle verhindert.
 */
export function benchmarkVariantsFromSummaryMetrics(
  rows: readonly SummaryMetricRowLike[],
  variantId = 'standard'
): BenchmarkVariantInput[] {
  const base: BenchmarkVariantInput = {
    variantId,
    materialCosts: null,
    manufacturingCosts: null,
    totalProductionCosts: null,
    scrap: null,
    otherSurcharges: null,
    setupCostAllocation: null,
    toolingAndFixtureCost: null,
    offerBasePrice: null,
    offerPrice: null,
  }

  const known = new Set<string>(SUMMARY_METRIC_KEYS as readonly string[])
  const byKey = new Map<SummaryMetricKey, MoneyValue>()

  for (const row of rows) {
    if (row.value === null) continue
    // Unbekannte metric_key-Werte still überspringen: die Spalte ist freier
    // TEXT, und eine spätere Metrik-Erweiterung darf den Benchmark nicht
    // brechen.
    if (!known.has(row.metric_key)) continue
    byKey.set(row.metric_key as SummaryMetricKey, { value: row.value, currency: row.currency })
  }

  for (const [metricKey, field] of Object.entries(DIRECT_METRIC_MAP)) {
    const m = byKey.get(metricKey as SummaryMetricKey)
    if (m) base[field] = m
  }

  const scrapMaterial = byKey.get('scrapMaterial')
  const scrapManufacturing = byKey.get('scrapManufacturing')
  if (scrapMaterial && scrapManufacturing) {
    const sameCurrency =
      scrapMaterial.currency !== null && scrapMaterial.currency === scrapManufacturing.currency
    base.scrap = sameCurrency
      ? {
          value: (scrapMaterial.value ?? 0) + (scrapManufacturing.value ?? 0),
          currency: scrapMaterial.currency,
        }
      : null
  } else {
    // Nur eine der beiden Hälften vorhanden: die vorhandene übernehmen. Sie
    // ist ehrlich unvollständig, aber sie ist keine erfundene Zahl — und der
    // Ausschussanteil bleibt größenordnungsmäßig lesbar.
    base.scrap = scrapMaterial ?? scrapManufacturing ?? null
  }

  return [base]
}
