// Pure functions for the QAF Process Comparison Board (KAR-341).
// No React, no DB — all UI/state lives in components/qaf/qaf-comparison-board.tsx.

import type { QAFRow } from '@/lib/qaf-parser'

export const MAX_SLOTS = 5

export type ComparisonMode = 'vs-basis' | 'aufbauend'

export interface ComparisonSlot {
  /** stable identifier so React can key on it */
  slot_index: number
  /** which uploaded file this slot was drawn from */
  qaf_upload_id: string
  /** label used in slot-header (file_label || file_name) */
  file_label: string
  /** ISO string for the file's uploaded_at */
  uploaded_at?: string | null
  /** human-readable name of the process step / row */
  prozess_name: string
  /** the chosen row's raw values (subset of QAFRow used in slots) */
  row: QAFRow
}

/** Numeric fields we can compute Δ over. Mirrors the lowerBetter table in qaf-client. */
export const NUMERIC_FIELDS: ReadonlyArray<keyof QAFRow> = [
  'zykluszeit',
  'teileProZyklus',
  'anzahlMA',
  'lohnkosten',
  'lohnzuschlagssaetze',
  'mss',
  'ruestkosten',
  'fek',
  'rfgk',
  'fk',
  'wechselkurs',
  'anzahlProAngebotsteil',
  'fkAW',
  'ausschuss',
  'ausschusskosten',
] as const

/**
 * Felder, bei denen ein ANSTIEG die günstige Richtung ist (KAR-833).
 * Spiegel der lowerBetter:false-Einträge in der METRICS-Tabelle des
 * qaf-comparison-board — kanonische Quelle für Export + Detail-UI,
 * damit eine Verbesserung nie rot eingefärbt wird.
 */
export const HIGHER_IS_BETTER_FIELDS: ReadonlySet<string> = new Set([
  'teileProZyklus',
  'wechselkurs',
  'anzahlProAngebotsteil',
])

/**
 * For each slot, decide which other slot is its delta-reference.
 * - `vs-basis`: every slot compares against slot 0 (the Basis). slot 0 has no ref.
 * - `aufbauend`: slot N compares against slot N-1. slot 0 has no ref.
 *
 * Returns an array of indices the same length as `slots`. `null` means
 * "no delta to compute for this slot".
 */
export function deltaReferenceIndices(
  slotsCount: number,
  mode: ComparisonMode,
): Array<number | null> {
  if (slotsCount <= 0) return []
  const out: Array<number | null> = new Array(slotsCount).fill(null)
  for (let i = 1; i < slotsCount; i++) {
    out[i] = mode === 'vs-basis' ? 0 : i - 1
  }
  return out
}

/**
 * Compute the delta between two numeric values. Returns `null` if either
 * side is missing — caller renders "—" in that case.
 */
export function computeDelta(current: number | null, ref: number | null): number | null {
  if (current === null || current === undefined) return null
  if (ref === null || ref === undefined) return null
  return Number((current - ref).toFixed(4))
}

/**
 * Convenience: for one metric across all slots, compute the delta-array.
 * `null` at index 0 (or wherever the ref-index is null).
 */
export function computeMetricDeltas(
  slots: ComparisonSlot[],
  field: keyof QAFRow,
  mode: ComparisonMode,
): Array<number | null> {
  const refs = deltaReferenceIndices(slots.length, mode)
  return slots.map((slot, i) => {
    const refIdx = refs[i]
    if (refIdx === null) return null
    const cur = slot.row[field]
    const ref = slots[refIdx].row[field]
    if (typeof cur !== 'number' || typeof ref !== 'number') return null
    return computeDelta(cur, ref)
  })
}

/**
 * Determine which colour-class a Δ should get for a "lower is better" metric.
 * Returns one of 'better' | 'worse' | 'equal' | 'na' so the UI can map to
 * tokens (no hardcoded colors in this lib — that lives in the component).
 */
export type DeltaSentiment = 'better' | 'worse' | 'equal' | 'na'

export function deltaSentiment(
  delta: number | null,
  lowerBetter: boolean,
  epsilon = 1e-6,
): DeltaSentiment {
  if (delta === null) return 'na'
  if (Math.abs(delta) < epsilon) return 'equal'
  if (lowerBetter) {
    return delta < 0 ? 'better' : 'worse'
  }
  return delta > 0 ? 'better' : 'worse'
}
