// Canvas builder state (docs/21-FRONTEND-PHASE2.md §2), shaped for editing rather than for
// the wire. `page.tsx` converts between these and the real contract in `types/production.ts`.
//
// Two things the API shapes can't express and the canvas needs:
//
//   * **Stable React list keys.** Stage inputs and custom fields have no client-facing key in
//     the contract (only outputs do, because Upstream inputs reference them by key). Rendering
//     them by array index would make React reuse the wrong <input> when a row is removed, so
//     every editable row carries a throwaway `localId` that is stripped on save.
//   * **Half-filled rows.** A row the user just added has nothing picked yet, so `itemId` and a
//     WIP output's `uomId` are nullable here. `page.tsx` blocks the save until the required ones
//     are set, which is what makes the non-null assertions in its payload builder sound.
//
// A stage's identity IS its React Flow node id, which is its server key — the stringified
// stage id, or `tmp-<uuid>` for a stage drawn in this session. That is why edges need no
// translation on save: `edge.source`/`edge.target` are already `parentKey`/`childKey`.

import { CustomFieldType, StageInputSource, StageQtyUnit } from "@/types/production"

/** `tmp-` prefixed so the server can tell a newly drawn stage/output from one it already has. */
export function newKey(): string {
  return `tmp-${crypto.randomUUID()}`
}

/** Render-only identity for rows the contract keys by position. Never sent. */
export function newLocalId(): string {
  return crypto.randomUUID()
}

export interface BuilderInput {
  localId: string
  source: StageInputSource
  /** Stock inputs only. */
  itemId: number | null
  /** Upstream inputs only — an output key belonging to a *direct* parent stage. */
  fromOutputKey: string | null
  /**
   * Whether `qtyPerBatch` is a pack count or an amount of the item's content (ml/g).
   * `Content` is only offered for a Stock input whose item declares a content size.
   */
  qtyUnit: StageQtyUnit
  qtyPerBatch: number
}

export interface BuilderOutput {
  /** Server output id as a string, or `tmp-<uuid>`. Upstream inputs reference this. */
  key: string
  /** Terminal stage only — the finished good. Must stay null on WIP outputs (FR-MFG-05). */
  itemId: number | null
  name: string
  /** WIP label — required when `itemId` is null, and must stay null when it is set. */
  uomId: number | null
  qtyPerBatch: number
}

export interface BuilderFieldDef {
  localId: string
  /** Slugified from `label`; the run's `fieldValues` are keyed by it (FR-MFG-07). */
  key: string
  label: string
  type: CustomFieldType
  /** Only sent when `type` is `Select`. */
  options: string[]
  required: boolean
}

export interface StageNodeData extends Record<string, unknown> {
  name: string
  roleLabel: string
  estimatedMinutes: number
  inputs: BuilderInput[]
  outputs: BuilderOutput[]
  fieldDefs: BuilderFieldDef[]
  /** Recomputed by the page on every graph change, not user-editable — no in/out edges at all. */
  disconnected?: boolean
  /** Set when a server `422 GRAPH_*` named this stage, so the canvas can point at it. */
  focused?: boolean
  /** Injected by the page at render time — deletes this node (and any edges touching it). */
  onDelete?: () => void
}

/**
 * Free-floating annotations — grouping boxes and divider lines. Purely visual: they carry no
 * graph semantics (no ports, never part of the cycle/terminal/entry/disconnected checks or the
 * save-blocking issue list), unlike "stage" nodes. Persisted verbatim in the template's
 * `annotations` jsonb so a layout survives a reload.
 */
export interface AnnotationData extends Record<string, unknown> {
  label: string
  /** Degrees, applied as a CSS rotation around the node's own center. Lines only. */
  rotation?: number
}
