export type NodeType = 'process' | 'machine' | 'inventory' | 'transport' | 'customer' | 'supplier' | 'timevalue'
export type ProcessType = 'manual' | 'robot' | 'human_robot' | 'machine'

/**
 * Wertstrom P2 (KAR-878/KAR-986, Capability-Matrix A7): the connection's
 * flow type. Absent = `'materialFlow'` (today's only kind) — every
 * pre-existing connection in the DB has no `kind` field at all and must
 * keep meaning exactly what it always meant, per the P2 leitplanke "fehlendes
 * Feld = heutige Semantik". Only `'information'` is ever written explicitly
 * (chosen at connect-time in the editor); a connection is never written with
 * an explicit `kind: 'materialFlow'` literal, so there is exactly one way to
 * express the default (absence), not two.
 */
export type ConnectionKind = 'materialFlow' | 'information'

/**
 * Wertstrom P2 (Capability-Matrix A8): the storage element's Lean sub-type.
 * Absent = generic/undifferentiated storage (today's only shape) — same
 * "absence is the default, not a redundant literal" discipline as
 * ConnectionKind above. Only meaningful on a node with `type === 'inventory'`.
 */
export type InventoryKind = 'fifo' | 'supermarket' | 'push'

/**
 * Wertstrom P2 (execution-prompt §7.3 "Clear Data Semantics", Capability-Matrix
 * A12): where a node's data came from — generalizes the QAF-only
 * `fieldStatus` 'imported' concept to every creation path (LSC/Stoppuhr
 * measurement, manual planning assumption, a future calculated value, …).
 * One scalar per node (not per-field, unlike `fieldStatus`): a single small
 * "Herkunft" badge per node is the P2 minimal-UI scope; per-field provenance
 * is a possible future refinement, not required by today's shape. Absent =
 * unknown provenance — never fabricated, never defaulted to a guess.
 */
export type ValueProvenance = 'planned' | 'measured' | 'calculated' | 'imported' | 'assumed'

/**
 * Wertstrom P2 (Capability-Matrix A9, execution-prompt §9): which kind of
 * scenario a `value_stream_maps` row is. Mirrors the DB column
 * `value_stream_maps.scenario_kind` (supabase-migration-wertstrom-scenarios.sql)
 * — `NOT NULL DEFAULT 'current'`, so unlike Connection/InventoryKind above
 * this is always present on a real row (see `ValueStreamMap` below), never
 * optional/absent.
 */
export type ScenarioKind = 'current' | 'future' | 'alternative'

/**
 * QVS-P1 (KAR-970): value-added classification derived conservatively from
 * the process name by `lib/qaf-value-stream/internal/va-classification.ts`.
 * Never auto-'va' — see that module for the classification rules. Leading
 * over `isValueAdded` once set (architecture.md §3.1 "Kompatibilitätsregeln");
 * `isValueAdded` stays the field the editor/metrics read today.
 */
export type VaClass = 'va' | 'nnva' | 'nva' | 'unknown'

/**
 * QVS-P1 (KAR-970): per-field provenance for one QAF source field, keyed by
 * QAFFieldKey (lib/qaf-parser.ts) inside `VsmNodeQafSource.fields`. Mirrors
 * `schemas/value-stream-process-source.schema.json` `fields.additionalProperties`
 * — keep both in sync; `lib/qaf-value-stream`'s mapping-drift test guards the
 * mapper side, this is the wire-shape source of truth on the TS side.
 *
 * Kept string-keyed (not `QAFFieldKey`-typed) here deliberately: vsm-types.ts
 * is a core/shared type module (components/wertstrom + others depend on it)
 * and must not import lib/qaf-parser's domain types. The mapper module knows
 * the real QAFFieldKey union and produces a `Partial<Record<QAFFieldKey, ...>>`
 * that structurally satisfies this shape.
 */
export interface VsmNodeQafSourceField {
  /** Cell reference "Sheet!A1" from QAFRow.sourceCells; null if not captured. */
  cell?: string | null
  /** Original value at import time — number, text, or null for an explicit n.a. marker. */
  original: string | number | null
  /** Original cell text when a numeric field did not parse as a number (e.g. "n.a."). */
  rawText?: string | null
  /** Unit per the MANUFACTURING canonical registry (s, %, BW/h, BW, AW, Stück, …). */
  unit?: string | null
  /** Parser column-match confidence (1.0 exact / 0.9 normalized); null if unknown. */
  confidence?: number | null
  /** VSM target field name this source field was mapped to; null = metadata-only
   * (also used for the sequence-driving `positionsnummer`, which sets no
   * discrete node field — see mapper.ts). */
  mappedTo?: string | null
}

/** How confidently the node's position in the sequence is known (Spec 7). qvs-1
 * only ever produces 'row_order' or 'ambiguous' — 'explicit' is reserved for a
 * future rule that isn't implemented yet. */
export type VsmNodeSequenceConfidence = 'explicit' | 'row_order' | 'ambiguous'

/**
 * QVS-P1 (KAR-970): source-of-truth lineage for one VSM process node that was
 * generated from a QAF import — embedded as the node's `qafSource` field.
 * Conforms to `schemas/value-stream-process-source.schema.json`.
 */
export interface VsmNodeQafSource {
  /** FK on value_stream_imports.id (P2). P1 has no persisted import record yet;
   * the mapper synthesizes one via idFactory so the shape is already valid. */
  importId: string
  /** Source worksheet name, derived from the row's sourceCells; null if unknown. */
  sheet?: string | null
  /** Source-order index (Quell-Reihenfolge) — the row's position in the input
   * QAFRow[] array (row_index, Spec 7). */
  rowIndex: number
  /** Raw `positionsnummer` value — secondary sequence evidence + display only. */
  positionNumber?: string | null
  /** FK on qaf_manufacturing_step.id — only available once rows are rehydrated
   * from the DB (P2); always null for P1's in-memory QAFRow[] mapper input. */
  manufacturingStepId?: string | null
  /** All extracted QAF source fields (up to 22), keyed by QAFFieldKey. */
  fields?: Record<string, VsmNodeQafSourceField>
  sequenceConfidence?: VsmNodeSequenceConfidence | null
  /** Multi-QAF variant context — not populated before P4 (Gap G5). */
  variantContext?: { shared: boolean; variantKeys: string[] } | null
}

/**
 * QVS-P1 (KAR-970): VsmNode fields a QAF import can set, and the editor can
 * subsequently mark 'modified' — see `fieldStatus` on VsmNode. Absence of
 * `fieldStatus`/`qafSource` on a node means "manually created" (no UI noise).
 */
export type VsmNodeEditableFieldKey =
  | 'name'
  | 'cycleTimeSec'
  | 'partsPerCycle'
  | 'numWorkers'
  | 'machineType'
  | 'location'
  | 'currency'
  | 'scrapRate'
  | 'scrapCostPerUnit'
  | 'machineHourRate'
  | 'laborHourRate'
  | 'setupCostPerUnit'
  | 'costPerUnit'
  | 'vaClass'

export interface VsmNode {
  id: string
  type: NodeType
  x: number
  y: number
  name: string
  cycleTimeSec?: number
  machineTimeSec?: number
  manualTimeSec?: number
  setupTimeSec?: number
  waitTimeSec?: number
  isValueAdded?: boolean
  capacityPerHour?: number
  notes?: string
  oee?: number
  quantity?: number
  distance?: number
  transportTimeSec?: number
  demand?: number
  numWorkers?: number
  processType?: ProcessType
  machineType?: string
  // ── QVS-P1 (KAR-970) additive fields — all optional, existing fields above
  // are untouched. See reports/qaf-value-stream-architecture.md §3.1. ──────
  /** Production location (Freitext) — QAF `standort`. */
  location?: string
  /** Procurement currency (BW) of the cost fields below EXCEPT
   * `scrapCostPerUnit` (that one is AW — see its own doc) — QAF
   * `beschaffungswaehrung`. */
  currency?: string
  /** Parts produced per cycle — QAF `teileProZyklus`. No silent division of cycleTimeSec. */
  partsPerCycle?: number
  /** Scrap rate [%] — QAF `ausschuss`. */
  scrapRate?: number
  /** Scrap cost per unit [AW — quotation currency, NOT `currency`/BW] — QAF
   * `ausschusskosten`. Conversion via `wechselkurs` is a P2+ design decision;
   * never converted silently. */
  scrapCostPerUnit?: number
  /** Machine-hour rate [currency/h] — QAF `mss`. */
  machineHourRate?: number
  /** Direct labor hour rate [currency/h] — QAF `lohnkosten`. Rate, NOT
   * per-unit; per-unit requires FEK-style conversion (cycle time,
   * parts/cycle, SGK) — never done silently here. */
  laborHourRate?: number
  /** Setup COST per unit — QAF `ruestkosten`. NEVER setupTimeSec (cost ≠ time). */
  setupCostPerUnit?: number
  /** Primary manufacturing cost per part (BW view) — QAF `fk`. */
  costPerUnit?: number
  /** Conservative value-added classification — see VaClass. */
  vaClass?: VaClass
  /** Multi-QAF variant tags — not populated before P4 (Gap G5). */
  variantTags?: string[]
  /** QAF import lineage — absent on manually created nodes. */
  qafSource?: VsmNodeQafSource
  /** Per-field import/edit status — absent on manually created nodes. */
  fieldStatus?: Partial<Record<VsmNodeEditableFieldKey, 'imported' | 'modified'>>
  /** Wertstrom P2 (A8) — Lean storage sub-type. Only meaningful when
   * `type === 'inventory'`. Absent = generic/undifferentiated storage. */
  inventoryKind?: InventoryKind
  /** Wertstrom P2 (A8) — FIFO lane capacity limit ("FIFO mit Kapazität",
   * Capability-Matrix). Generically named/available (not type-narrowed at
   * the type level, consistent with e.g. `distance` being conceptually
   * transport-only above) but primarily meaningful for `inventoryKind ===
   * 'fifo'`. */
  inventoryMaxQuantity?: number
  /** Wertstrom P2 (A12) — see `ValueProvenance`. Absent = unknown. */
  provenance?: ValueProvenance
  /** Wertstrom P4 (B3, Capability-Matrix, KAR-878/KAR-986) — Standard OEE
   * field "am Prozess": Verfügbarkeit [%], 0–100. Feeds
   * `lib/vsm-engine`'s `computeEffectiveCapacity` as
   * `NodeCapacityOverrides.availabilityPct` — NEVER written into `oee`
   * above (that field means a full MEASURED OEE and takes a different,
   * double-counting-guarded priority branch in the capacity engine; see
   * lib/vsm-engine/internal/capacity.ts). Only meaningful for
   * `type === 'process'` (B3 scope is explicitly "am Prozess", not the
   * separate `machine` node type, which keeps its own pre-existing `oee`
   * field untouched). */
  availabilityPct?: number
  /** Wertstrom P4 (B3, Expert-Teil) — Mean Time Between Failures [min].
   * Reference/reliability data, not itself read by the capacity engine.
   * When both `mtbfMin` and `mttrMin` are set, the editor offers a derived
   * Verfügbarkeit via `lib/oee`'s `calculateAvailabilityFromMtbfMttr` as an
   * explicit "übernehmen" action into `availabilityPct` — never silently
   * (same "kein stilles Überschreiben" doctrine as the cycle-time
   * provenance-downgrade guard). */
  mtbfMin?: number
  /** Wertstrom P4 (B3, Expert-Teil) — Mean Time To Repair [min]. See `mtbfMin`. */
  mttrMin?: number
  /** Wertstrom P4 (B4, Capability-Matrix, KAR-878/KAR-986) — Standard
   * transport field "Frequenz" (e.g. "3x täglich", "wöchentlich"). Free
   * text, same convention as `VsmConnection.frequency` below — no
   * structured unit is imposed (nothing in `lib/vsm-engine` consumes this
   * today; formula/engine changes are explicitly out of P4 scope). Only
   * meaningful for `type === 'transport'`. */
  transportFrequency?: string
  /** Wertstrom P8.1 (A18, KAR-878/KAR-986, execution-prompt §15.5 "Measure
   * Management") — Kaizen-Marker: flags this element (any node type — a
   * Kaizen-Chance/Engpass/Risiko can sit on a process, inventory, transport,
   * …) as convertible into a measure, with an optional short text. Presence
   * of the field (even `''`) IS the marker; absence = not marked. Removable
   * via the Node-Panel action (sets it back to `undefined`) — never a
   * separate boolean, so there is exactly one way to represent "not
   * marked". */
  kaizenNote?: string
  /** Wertstrom P8.1 (A18, §15.5) — `workshop_actions.id` references. TWO
   * paths populate this array: measures created FROM this node via
   * "Maßnahme erstellen" (P8.1), and — since P8.2a (Baustein 4, "Bestehende
   * Maßnahme verknüpfen") — an ALREADY-EXISTING project measure picked via
   * `VsmLinkMeasureDialog`. Both go through the SAME `linkMeasureToNode`
   * (vsm-editor.tsx, Sofort-PUT, idempotent against a repeat link of the
   * same measure). `workshop_actions` itself is untouched (Architektur-
   * Entscheidung: KEIN DDL) — this is the ONLY link, living entirely on the
   * VSM side. A ref whose row no longer exists (deleted in the Workshop-
   * Modul) is a "tote Referenz" — the Node-Panel surfaces it honestly with a
   * Remove action, never silently filters it (see vsm-editor.tsx/
   * vsm-measures.ts). Deliberately NOT copied when a Szenario is derived
   * (duplicate route) — a measure belongs to the Ist-Zustand; copying the
   * reference would double-count it as "open" against two different
   * value_stream_maps rows in computeManagementAnalysis. */
  measureRefs?: string[]
  /** P8.2a (KAR-878/KAR-986, Baustein 2 "PPS als echtes Datenfeld") — marks
   * this node as the central Produktionssteuerung/PPS element (Kais §5).
   * Only meaningful for `type === 'process'` (Panel-UI is gated to that
   * type). Deliberately `true` ONLY (never `false`) — same "presence IS the
   * marker" sparse-write discipline as `kaizenNote`/`measureRefs` above: to
   * unmark a node, the field is OMITTED from the PUT payload entirely
   * (K3-Lehre, PR #367 Fix-Runde — a save must never write a new key for an
   * unchanged/absent user choice), never sent as an explicit `false`. Before
   * this field existed, `resolveNodeSymbolKey` (vsm-config.ts) could only
   * INFER "this is probably the PPS element" from a structural heuristic
   * ("touches only information edges") — real evidence, but not proof (a
   * second, differently-purposed information-only process node, e.g. a
   * Qualitätsbüro, would resolve to the identical symbol/label). This field
   * is the declared P8.2 candidate named in that heuristic's own K6-Fix doc
   * comment: an explicit, positive signal that WINS over the heuristic when
   * set (see `resolveNodeSymbolKey`'s own doc comment for the "Feld vs.
   * Materialfluss-Kanten"-Konfliktentscheidung), so a user can mark the
   * REAL PPS unambiguously even in a Wertstrom with more than one
   * information-only process node. */
  isPps?: true
}

/**
 * P8.2a (KAR-878/KAR-986, Baustein 3 "Push/Pull/Kanban-Kantensemantik"): how
 * a materialFlow connection is CONTROLLED (Kais §5/§10: "zeige klar, welche
 * Prozesse über Push-Steuerung, Pull-Steuerung, Kanban … gesteuert werden" /
 * "Push-Pfeil" §10.10, "Pull-Verbindung"+"Kanban" §10.11/§10.12). Only
 * meaningful on a connection with `kind !== 'information'` (validated at the
 * schema level, `lib/api/schemas.ts`) — a Push/Pull/Kanban control concept
 * does not apply to an information flow. `'pull'` and `'kanban'` share the
 * SAME rendered marker (`VsmSymbolKanbanPull`, vsm-symbols.tsx §10.11/§10.12
 * "zusammengefasst") — kept as two distinct wire values rather than
 * collapsing them into one, since they are fachlich distinct concepts a
 * future consumer (or a real VSM purist) may want to tell apart even though
 * today's rendering does not. PURELY VISUAL — see the field's own "keine
 * Rechenwirkung" note at every render call site; no `lib/vsm-engine`
 * function reads it.
 */
export type FlowControl = 'push' | 'pull' | 'kanban'

export interface VsmConnection {
  id: string
  fromNodeId: string
  toNodeId: string
  label?: string
  transportTimeSec?: number
  batchSize?: number
  /** Wertstrom P2 (A7) — see `ConnectionKind`. Absent = `'materialFlow'`. */
  kind?: ConnectionKind
  /** Wertstrom P2 (A7, execution-prompt §11.4 "optional frequency") —
   * freeform, e.g. "täglich"/"wöchentlich". Only ever populated when `kind
   * === 'information'` in the editor's connect-flow, but not type-gated
   * here (a future relationship kind may want it too). */
  frequency?: string
  /** P8.2a (Baustein 3) — see `FlowControl`. Absent = the pre-existing,
   * undifferentiated marker (pixel-identical rendering to before this
   * Baustein — Doktrin: sichtbare Änderung NUR wenn ein Nutzer das Feld
   * setzt). KEINE Rechenwirkung in `lib/vsm-engine` — rein visuell/
   * fachliche Kennzeichnung (Kais §5/§10), siehe vsm-editor.tsx's
   * Render-Kommentar. */
  flowControl?: FlowControl
}

export interface ValueStreamMap {
  id: string
  project_id: string | null
  title: string
  description: string | null
  nodes: VsmNode[]
  connections: VsmConnection[]
  layout: Record<string, unknown>
  created_by: string | null
  created_at: string
  updated_at: string
  /** DB column is NOT NULL DEFAULT false (Wertstrom P0, KAR-878) — was missing
   * here, forcing every consumer to re-cast the raw Supabase row instead of
   * reading a typed field. See app/wertstrom/copilot-export-actions.ts. */
  is_demo: boolean
  /** Wertstrom P2 (A9, KAR-878/KAR-986) — DB column `uuid NULL`
   * (supabase-migration-wertstrom-scenarios.sql). `null` = this row is not a
   * derived scenario (either a top-level current-state map, or a map
   * created before this feature). Set exactly once, by
   * POST /api/wertstrom/[id]/duplicate — never editable afterwards through
   * this phase's UI. */
  parent_value_stream_id: string | null
  /** Wertstrom P2 (A9) — DB column `text NOT NULL DEFAULT 'current'`. See
   * `ScenarioKind`. */
  scenario_kind: ScenarioKind
}
