/**
 * qaf-value-stream module types (QVS-P1, KAR-970).
 *
 * Pure types only — no runtime logic. See mapper.ts / manufacturing-capability.ts
 * / va-classification.ts / naming.ts for the functions that use these.
 *
 * tdd-guard:skip — types-only file, exercised through the functions that use it.
 */

import type { QAFFieldKey, QAFParseMeta } from '@/lib/qaf-parser'
import type { VsmConnection, VsmNode, VsmNodeEditableFieldKey } from '@/lib/vsm-types'

/** Injectable node/connection/import id generator — default is
 * `crypto.randomUUID`. Injectable so tests can get deterministic ids
 * (KAR-970 P1 test requirement). */
export type IdFactory = () => string

export interface MapperOptions {
  /** Defaults to `() => crypto.randomUUID()`. */
  idFactory?: IdFactory
  /** FK-to-be for `value_stream_imports.id` (P2 wires the real value once
   * that record exists). Defaults to a fresh `idFactory()` call so the
   * `VsmNodeQafSource` shape is valid standalone in P1. */
  importId?: string
  /** Parse-level diagnostics from `parseQAFTemplate` (lib/qaf-parser.ts).
   * Optional. When present, its `parseConfidence` (arithmetic mean of
   * per-column tier confidence, 1.0 exact / 0.9 normalized) is used as the
   * confidence value for every sourced field — the current `QAFRow` shape
   * does not retain true per-field tier confidence (only the parser's
   * internal per-column map does, which is aggregated away before
   * `QAFRow[]` is returned), so this is an honest file-level proxy, not a
   * fabricated per-field number. When absent, `confidence` is `null`
   * (unknown) rather than guessed. */
  parseMeta?: QAFParseMeta
  /** Optional confidence threshold (0-1). qvs-1 ships with NO default cutoff
   * (field-mapping report: "Kein Mindest-Cutoff in qvs-1 ... Schwelle ist im
   * Mapper parametrisiert") — when set, a field whose resolved confidence is
   * below this value is excluded from the node (recorded as
   * `missingFieldReasons` reason `below_confidence`) even though a value was
   * present. */
  minConfidence?: number
  /** x of the first node. Default 40. */
  startX?: number
  /** Constant y for every node. Default 200. */
  y?: number
  /** Horizontal gap between nodes, added to NODE_W (components/wertstrom/vsm-geometry.ts). Default 40. */
  xGap?: number
}

export interface ExcludedRow {
  /** Index of the row in the INPUT rows array (source order). */
  rowIndex: number
  reason: 'missing_process_name'
}

/** One of the 4 fixed reason codes from reports/qaf-value-stream-field-mapping.md
 * ("Missing-Value"): `no_column_mapped` (header column never located),
 * `cell_empty` (column located, cell blank), `not_numeric[<rawText>]` (numeric
 * field, explicit non-numeric marker like "n.a." — rawText interpolated),
 * `below_confidence` (value present but under `minConfidence`). */
export type MissingFieldReasonCode = 'no_column_mapped' | 'cell_empty' | 'below_confidence' | `not_numeric[${string}]`

export interface MissingFieldReason {
  rowIndex: number
  nodeId: string
  /** QAF source field key (e.g. "ruestkosten"). */
  field: QAFFieldKey
  /** VSM target field name (e.g. "setupCostPerUnit"). */
  targetField: string
  reason: MissingFieldReasonCode
}

export type QvsWarningCode =
  /** positionsnummer contradicts source row order — see mapper.ts assessSequence. */
  | 'sequence_ambiguous'
  /** Rows were parsed but none carry a `prozessbezeichnung` — see manufacturing-capability.ts. */
  | 'no_eligible_rows'

export interface QvsWarning {
  code: QvsWarningCode
  message: string
  /** First row (by input array index) involved in the condition, when applicable. */
  rowIndex?: number
}

/** Persisted-warning severity (QVS-P2, creation.ts `QVS_WARNING_SEVERITY`) —
 * named here so P3's preview UI can import the type without reaching into
 * creation.ts's internal table shape. See `severityForQvsWarningCode`
 * (creation.ts) for the lookup this type describes. */
export type QvsWarningSeverity = 'information' | 'warning' | 'blocking_error'

export interface MapperResult {
  nodes: VsmNode[]
  connections: VsmConnection[]
  warnings: QvsWarning[]
  excludedRows: ExcludedRow[]
  missingFieldReasons: MissingFieldReason[]
}

export interface CapabilityAssessment {
  /** true iff at least one row has a non-blank `prozessbezeichnung`. */
  eligible: boolean
  /** Count of rows with a non-blank `prozessbezeichnung` (would become nodes). */
  stepCount: number
  /** Fraction (0-1) of eligible steps that carry a value for each primary
   * value-bearing field (the 12 `PRIMARY_FIELD_MAPPINGS` targets in mapper.ts).
   * Keyed by QAFFieldKey source name. Empty object when stepCount is 0. */
  fieldCoverage: Partial<Record<QAFFieldKey, number>>
  warnings: QvsWarning[]
}

// ── QVS-P5 (KAR-974, Reimport & Synchronisation) ────────────────────────────
// Spec 21 ("Reimport and Synchronization"), gap-analysis G8, architecture.md
// §7 P5. See sync.ts module header for the full matching/rollup rationale —
// these are the shared wire types both sync.ts (pure) and reimport.ts
// (DB-wired) build on, same convention as every other internal/ module in
// this barrel.

/** The 6 statuses Spec 21 names verbatim. Used at BOTH grains (Schritt UND
 * Feld) per KAR-974's own task framing — `QvsFieldSyncStatus` below narrows
 * it for the field grain, where the 2 add/remove members never apply (see
 * sync.ts). */
export type QvsSyncStatus = 'UNCHANGED' | 'SOURCE_CHANGED' | 'LOCAL_CHANGED' | 'BOTH_CHANGED' | 'NEW_IN_SOURCE' | 'REMOVED_FROM_SOURCE'

/** Field-grain status: NEW_IN_SOURCE/REMOVED_FROM_SOURCE are step-grain-only
 * (a whole step appearing/disappearing, not one field of an existing,
 * matched step) — see sync.ts `computeSyncDelta` doc. */
export type QvsFieldSyncStatus = Exclude<QvsSyncStatus, 'NEW_IN_SOURCE' | 'REMOVED_FROM_SOURCE'>

export interface QvsFieldDelta {
  field: VsmNodeEditableFieldKey
  status: QvsFieldSyncStatus
  /** Value at last sync (`import_snapshot`), the value this field's status is
   * measured AGAINST for both the source and local axes. */
  snapshotValue: unknown
  /** Value in the freshly re-mapped QAF source (current `qaf_manufacturing_step`
   * read). */
  sourceValue: unknown
  /** Value currently in `value_stream_maps.nodes` — `undefined` when
   * `locallyDeleted` (no live node to read from). */
  liveValue: unknown
}

/** One matched (or new/removed) step's full delta. `stepMatchId` is the
 * identity a `QvsSyncAdoptionPlan` references: the ORIGINAL node `id`
 * (stable across snapshot↔live, see sync.ts) for a matched or
 * REMOVED_FROM_SOURCE step, or a synthesized `source:<rowIndex>` key for a
 * NEW_IN_SOURCE step (nothing durable to reuse yet — a real id is only
 * minted on adoption). */
export interface QvsStepDelta {
  stepMatchId: string
  status: QvsSyncStatus
  /** Best available display name — the matched name for a matched/removed
   * step, the new step's own name for NEW_IN_SOURCE. */
  name: string
  /** true when a snapshot node existed and (via `sameProfileVariants`-style
   * name+order matching) still exists conceptually, but NO live node with
   * that id remains in `value_stream_maps.nodes` — the user deleted this
   * step in the editor since the last import/sync. Field-level adoption has
   * nothing to write into in this case (sync.ts `applySyncAdoption` skips
   * it) — surfaced so the UI can say so rather than silently omitting the
   * step. Always `false` for NEW_IN_SOURCE (no live node ever existed for a
   * step nobody has imported yet — that is the normal, expected state, not
   * a deletion). */
  locallyDeleted: boolean
  /** The freshly re-mapped source node — `null` only for REMOVED_FROM_SOURCE. */
  sourceNode: VsmNode | null
  /** The `import_snapshot` node — `null` only for NEW_IN_SOURCE. */
  snapshotNode: VsmNode | null
  /** The current live node — `null` for NEW_IN_SOURCE or `locallyDeleted`. */
  liveNode: VsmNode | null
  /** Per-field deltas — empty for NEW_IN_SOURCE/REMOVED_FROM_SOURCE (adopting
   * either is a whole-step decision, not a per-field one; see sync.ts). */
  fields: QvsFieldDelta[]
}

/** A snapshot- or source-side step that could not be confidently matched to
 * anything on the other side — see sync.ts `computeSyncDelta` doc
 * ("bei Ambiguität ehrlich 'unmatched' statt raten"). Deliberately NOT folded
 * into NEW_IN_SOURCE/REMOVED_FROM_SOURCE (that would be a guess dressed up as
 * a classification) and deliberately NOT part of `QvsSyncStatus` (the task's
 * 6 named statuses are all confident classifications; this is the honest
 * 7th, unclassified bucket). */
export interface QvsUnmatchedStep {
  side: 'source' | 'snapshot'
  rowIndex: number
  name: string
  reason: 'ambiguous_name_count_mismatch'
}

export interface QvsSyncSummary {
  unchanged: number
  sourceChanged: number
  localChanged: number
  bothChanged: number
  newInSource: number
  removedFromSource: number
  unmatched: number
}

export interface QvsSyncDelta {
  steps: QvsStepDelta[]
  unmatched: QvsUnmatchedStep[]
  /** Step-grain counts (each step counted once, by its rolled-up `status`) —
   * the "X übernehmen, Y behalten, Z Konflikte" summary line (Spec 21). */
  summary: QvsSyncSummary
}

export type QvsSyncAdoptionMode = 'all' | 'selected' | 'none'

export interface QvsFieldAdoptionSelection {
  stepMatchId: string
  field: VsmNodeEditableFieldKey
}

/** BOTH_CHANGED is a conflict — Leitplanke: "BOTH_CHANGED nur mit expliziter
 * Einzel-Entscheidung des Nutzers". A field with status BOTH_CHANGED is
 * NEVER touched by `mode: 'all'`/`'selected'` alone; only an explicit
 * `QvsConflictResolution` entry can move it, in either direction. */
export interface QvsConflictResolution {
  stepMatchId: string
  field: VsmNodeEditableFieldKey
  resolution: 'keep_local' | 'take_source'
}

/** REMOVED_FROM_SOURCE default is 'keep' (Spec 21: "Option Schritt behalten
 * (Default) oder entfernen") — a step with no entry here is always kept. */
export interface QvsRemovedStepDecision {
  stepMatchId: string
  action: 'keep' | 'remove'
}

export interface QvsNewStepSelection {
  /** The synthesized `source:<rowIndex>` id of a NEW_IN_SOURCE QvsStepDelta. */
  stepMatchId: string
}

export interface QvsSyncAdoptionPlan {
  mode: QvsSyncAdoptionMode
  /** Consulted only when `mode === 'selected'`; ignored otherwise (under
   * `'all'` every SOURCE_CHANGED field is adopted, under `'none'` none are). */
  selectedFieldAdoptions?: readonly QvsFieldAdoptionSelection[]
  /** Consulted only when `mode === 'selected'`; ignored otherwise (under
   * `'all'` every NEW_IN_SOURCE step is added, under `'none'` none are). */
  selectedNewSteps?: readonly QvsNewStepSelection[]
  /** Always consulted regardless of `mode` — see `QvsConflictResolution` doc. */
  conflictResolutions?: readonly QvsConflictResolution[]
  /** Always consulted regardless of `mode` — see `QvsRemovedStepDecision` doc. */
  removedStepDecisions?: readonly QvsRemovedStepDecision[]
}

export interface QvsSyncApplySummary {
  fieldsAdopted: number
  stepsAdded: number
  stepsRemoved: number
  conflictsResolvedTakeSource: number
  conflictsResolvedKeepLocal: number
  /** LOCAL_CHANGED fields left untouched — informational, never a count of
   * anything that failed. */
  fieldsKeptLocal: number
}

export interface QvsSyncApplyResult {
  nodes: VsmNode[]
  connections: VsmConnection[]
  summary: QvsSyncApplySummary
}
