// Zellzustände (Spezifikation Kap. 6.3) — die Grundlage der zellgenauen
// Detektoren.
//
// Ein Betrag allein sagt nicht, woher er kommt. Ob eine Zelle rechnet, ob
// jemand das Ergebnis von Hand überschrieben hat, ob sie auf eine fremde Datei
// zeigt, die niemand mitliefert — das entscheidet, wie belastbar die Zahl ist.
// Genau diese Unterscheidung tragen die folgenden Zustände.
//
// Der Fall, um den es geht: Ein Preis, der im Vergabestand aus Einzelpreis mal
// Anzahl gerechnet wurde und jetzt als feste Zahl dasteht, sieht im Blatt
// gleich aus. Er ist aber nicht mehr nachvollziehbar — und das ist ein
// Verhandlungspunkt, kein Formatierungsdetail.
//
// Pure Funktionen, keine I/O.

/** Wie ein Zellwert zustande kommt. */
export type ValueState = 'formula_and_cached' | 'constant' | 'empty' | 'error' | 'external_link'

export const VALUE_STATES: readonly ValueState[] = [
  'formula_and_cached',
  'constant',
  'empty',
  'error',
  'external_link',
]

/**
 * Was von einer Zelle bekannt ist.
 *
 * Bewusst schmal gehalten und aus der Arbeitsmappe befüllt, nicht selbst
 * gelesen — so bleiben die Detektoren prüfbar, ohne eine Datei zu brauchen.
 */
export interface CellSnapshot {
  sheet: string
  /** Zellbezug wie `AP94`. */
  cell: string
  /** Formeltext ohne führendes Gleichheitszeichen, falls die Zelle rechnet. */
  formula: string | null
  /** Ergebnis beziehungsweise Inhalt. */
  value: string | number | boolean | null
  /** Rohtext, wie er im Blatt steht — trägt Datumsangaben, die als Text abgelegt sind. */
  text?: string | null
}

/** Fehlerwerte, die Excel in Rechenpfade schreibt. */
const ERROR_VALUES = ['#N/A', '#REF!', '#VALUE!', '#DIV/0!', '#NAME?', '#NULL!', '#NUM!']

/**
 * Verweis auf eine fremde Arbeitsmappe.
 *
 * Excel schreibt solche Bezüge als `[1]Blatt!A1` oder mit vollem Pfad in
 * eckigen Klammern. Beides zählt: Der Wert stammt dann aus einer Datei, die dem
 * Vergleich nicht beiliegt, und ist damit nicht prüfbar.
 */
const EXTERNAL_REF = /\[[^\]]+\]|'[^']*\.xls[xmb]?'?[!#]/i

export function isErrorValue(value: unknown): boolean {
  return typeof value === 'string' && ERROR_VALUES.includes(value.trim().toUpperCase())
}

export function referencesExternalWorkbook(formula: string | null): boolean {
  return formula !== null && EXTERNAL_REF.test(formula)
}

/**
 * Zustand einer Zelle bestimmen.
 *
 * Die Reihenfolge der Prüfungen ist nicht beliebig: Ein Fehlerwert bleibt ein
 * Fehlerwert, auch wenn eine Formel dahintersteht — für den Leser des Blatts
 * zählt, dass dort kein Betrag steht. Und ein externer Bezug ist die
 * einschneidendere Aussage als „hier wird gerechnet".
 */
export function classifyCell(snapshot: CellSnapshot): ValueState {
  if (isErrorValue(snapshot.value)) return 'error'
  if (referencesExternalWorkbook(snapshot.formula)) return 'external_link'
  if (snapshot.formula !== null && snapshot.formula.trim() !== '') return 'formula_and_cached'
  const v = snapshot.value
  if (v === null || v === undefined || (typeof v === 'string' && v.trim() === '')) return 'empty'
  return 'constant'
}

/**
 * Datum, das als Text abgelegt ist.
 *
 * Ein Textdatum sortiert falsch, rechnet nicht und wird beim Import zur
 * Fehlerquelle. Erkannt wird das Muster, nicht die Formatierung: Steht dort ein
 * Datum und die Zelle trägt keinen Datumswert, ist es Text.
 */
const DATE_PATTERN = /^\s*\d{1,2}[./-]\d{1,2}[./-]\d{2,4}\s*$|^\s*\d{4}-\d{2}-\d{2}\s*$/

export function looksLikeTextDate(snapshot: CellSnapshot): boolean {
  const raw = snapshot.text ?? (typeof snapshot.value === 'string' ? snapshot.value : null)
  if (raw === null) return false
  if (typeof snapshot.value !== 'string') return false
  return DATE_PATTERN.test(raw)
}

/**
 * Formel, die ausschliesslich mit festen Zahlen rechnet.
 *
 * Ein Satz, der als Konstante in der Formel steht statt aus einer Zelle zu
 * kommen, lässt sich nicht nachschlagen und bei einer Änderung nicht nachziehen.
 * Erkannt wird das daran, dass die Formel keinen einzigen Zellbezug enthält —
 * eine reine Rechnung aus Zahlen also.
 */
const CELL_REFERENCE = /\$?[A-Z]{1,3}\$?\d{1,7}\b|\b[A-Z_][A-Za-z0-9_.]*!/

export function isHardcodedComputation(formula: string | null): boolean {
  if (formula === null || formula.trim() === '') return false
  if (CELL_REFERENCE.test(formula)) return false
  // Eine Formel ohne Bezug und ohne Zahl ist keine Rechnung, sondern Text.
  return /\d/.test(formula)
}
