/** WCS 任务模板引擎原型 — 类型与 DSL（schemaVersion=1） */

export type Severity = 'hard' | 'soft'
export type ValueRef =
  | { ref: string }
  | { const: string | number | boolean | Array<string | number | boolean> | null }

export type CompareOp =
  | 'eq' | 'ne' | 'in' | 'notIn' | 'contains' | 'notContains'
  | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'exists' | 'notExists' | 'matchesRef'

export type ExprNode =
  | { type: 'group'; op: 'and' | 'or'; children: ExprNode[] }
  | { type: 'compare'; left: ValueRef; op: CompareOp; right?: ValueRef }

export interface FilterGroup {
  id: string
  severity: Severity
  expr: ExprNode
}

export interface DegradeStep {
  attempt: number
  requireGroups: string[]
  drop?: string[]
}

export interface ScoreRule {
  id: string
  function: 'nearer_to_ref' | 'fifo_age' | 'field_match_bonus' | 'constant'
  weight: number
  params: Record<string, unknown>
}

export interface LocationStrategy {
  filterGroups: FilterGroup[]
  degradeChain: DegradeStep[]
  scores: ScoreRule[]
  allocate: { mode: 'first' | 'topN_split'; topN?: number }
}

export interface ParamBinding {
  as: string
  from: string
  required?: boolean
  resolve?: 'eventPayload' | 'lookup' | 'session'
  key?: { template: string; args: Record<string, string> }
  onError?: 'cached' | 'suspend' | 'default' | 'fail'
  timeoutMs?: number
  cacheTtlSec?: number
  default?: unknown
}

export interface TaskTemplateDsl {
  schemaVersion: 1
  id: string
  name: string
  published: boolean
  meta: {
    priority: number
    mutexGroup?: string
    routeMode?: 'single_winner' | 'multi'
  }
  trigger: {
    type: 'event' | 'manual'
    source: string
    idempotency?: { keyMode: 'eventId' | 'businessKey+templateId'; onTerminalHit?: 'reject' | 'new_if_terminal' }
    merge?: { windowMs: number; keyFrom: string[] }
  }
  bindings: ParamBinding[]
  locationStrategies: {
    source: LocationStrategy
    target: LocationStrategy
  }
  blueprint: {
    taskType: 'transport'
    slots: { from: 'source'; to: 'target' }
    options: { autoDispatch: boolean; priority?: number }
  }
  policy: {
    reservationTtlSec: number
    allocateMaxAttempts: number
    allocateBackoffMs: number[]
    onNoCandidate: 'raise_alert' | 'fail'
    onRealityDrift?: 'fail' | 'reallocate'
    onDispatchReject?: 'compensate'
  }
}

export interface StorageLoc {
  storageId: string
  areaId: string
  areaType: string
  storageType: string
  status: string
  materialAffinity: string[]
  disabled: boolean
  lineId?: string
  allowInbound: boolean
  allowOutbound: boolean
  containerType?: string
  batchNo?: string
  qty?: number
  inboundAt?: string
  x: number
  y: number
  /** 演示用：被手动占住 */
  forceReserved?: boolean
}

export interface ParamFieldDef {
  module: string
  path: string
  valueType: 'string' | 'number' | 'bool'
  resolveMode: 'eventPayload' | 'lookup' | 'session'
  description: string
}

export type InstanceStatus =
  | 'Pending'
  | 'Assembling'
  | 'Suspended'
  | 'Allocating'
  | 'Reserved'
  | 'Dispatched'
  | 'InTransit'
  | 'Completed'
  | 'Compensating'
  | 'Failed'
  | 'Cancelled'
  | 'IgnoredDuplicate'

export interface ExplainSection {
  arbitration?: unknown
  bindings?: unknown
  filter?: unknown
  degrade?: unknown
  score?: unknown
  allocate?: unknown
  dispatch?: unknown
  [k: string]: unknown
}

export interface TaskInstance {
  id: string
  templateId: string
  templateName: string
  eventId: string
  status: InstanceStatus
  context: Record<string, unknown>
  sourceId?: string
  targetId?: string
  explain: ExplainSection
  createdAt: string
  updatedAt: string
  error?: string
  timeline: Array<{ at: string; status: InstanceStatus; note?: string }>
}

export interface Reservation {
  storageId: string
  instanceId: string
  status: 'Held' | 'Committed'
  createdAt: string
}

export interface TriggerEvent {
  eventId: string
  source: string
  payload: Record<string, unknown>
}

export interface TrialResult {
  ok: boolean
  context: Record<string, unknown>
  sourceCandidates: Array<{ storageId: string; total: number; scores: Record<string, number> }>
  targetCandidates: Array<{ storageId: string; total: number; scores: Record<string, number> }>
  eliminated: Array<{ storageId: string; strategy: string; groupId?: string; reason: string }>
  degrade: unknown[]
  pick?: { sourceId: string; targetId: string }
  error?: string
  explain: ExplainSection
}
