export type CamPoint = [number, number, number]
export type CamStockMode = 'from-base-bound-box' | 'create-box' | 'create-cylinder' | 'existing-solid'
export type CamStock = { min: CamPoint; max: CamPoint; mode?: CamStockMode }
export type CamToolShape = 'endmill' | 'ballend' | 'bullnose' | 'drill' | 'v-bit' | 'thread-mill' | 'probe'
export type CamTool = {
  id: string
  name: string
  diameter: number
  length: number
  shape?: CamToolShape
  cuttingEdgeHeight?: number
  shankDiameter?: number
  holderDiameter?: number
  holderLength?: number
}
export type CamToolBitAsset = { schemaVersion: 1; tool: CamTool }
export type CamSpindleDirection = 'forward' | 'reverse' | 'none'
export type CamCoolantMode = 'none' | 'flood' | 'mist'
export type CamToolController = {
  id: string
  label: string
  toolId: string
  toolNumber: number
  spindleSpeed: number
  spindleDirection: CamSpindleDirection
  horizontalFeed: number
  verticalFeed: number
  horizontalRapid: number
  verticalRapid: number
}
export type CamSetupSheet = {
  horizontalRapid: number
  verticalRapid: number
  safeHeightOffset: number
  clearanceHeightOffset: number
  startDepthExpression: string
  finalDepthExpression: string
  stepDownExpression: string
  coolantMode: CamCoolantMode
}
export type CamOperationKind =
  | 'profile'
  | 'pocket'
  | 'contour'
  | 'mill-face'
  | 'helix'
  | 'adaptive'
  | 'slot'
  | 'drilling'
  | 'tapping'
  | 'engrave'
  | 'deburr'
  | 'v-carve'
  | 'pocket-3d'
  | 'surface'
  | 'waterline'
  | 'thread-milling'
  | 'probe'
  | 'area'
  | 'area-workplane'
  | 'custom'
  | 'shape'
  | 'path-shape-tool-controller'
export type CamCutDirection = 'climb' | 'conventional' | 'either'
export type CamDressupKind = 'array' | 'axis-map' | 'boundary' | 'dogbone' | 'drag-knife' | 'lead-in-out' | 'ramp-entry' | 'holding-tags' | 'z-correct'
export type CamDressup = { kind: CamDressupKind; parameters: Record<string, number | string | boolean> }
export type CamOperation = {
  id: string
  label: string
  kind: CamOperationKind
  toolId: string
  controllerId: string
  depth: number
  feed: number
  verticalFeed: number
  stepDown: number
  stepOver: number
  direction: CamCutDirection
  coolantMode: CamCoolantMode
  active: boolean
  locations: CamPoint[]
  dressups: CamDressup[]
  path: CamPoint[]
  status: 'draft' | 'generated' | 'collision' | 'disabled'
}
export type CamOperationInput = {
  id: string
  label?: string
  kind: CamOperationKind
  toolId: string
  controllerId?: string
  depth: number
  feed: number
  verticalFeed?: number
  stepDown?: number
  stepOver?: number
  direction?: CamCutDirection
  coolantMode?: CamCoolantMode
  active?: boolean
  locations?: CamPoint[]
}
export type CamCollisionFixture = { id: string; kind?: 'fixture' | 'obstacle'; min: CamPoint; max: CamPoint }
export type CamMaterialRemovalTimelineEntry = {
  operationId: string
  pathIndex: number
  removedVolume: number
  remainingVolume: number
  startTimeMinutes?: number
  endTimeMinutes?: number
  distance?: number
  feed?: number
  sampleCount?: number
}
export type CamMaterialRemoval = { enabled: boolean; stockVolume: number; removedVolume: number; remainingVolume: number; timeline: CamMaterialRemovalTimelineEntry[] }
export type CamMaterialRemovalOptions = { resolution?: number; maxCells?: number }
export type CamMaterialRemovalEvent = {
  operationId: string
  pathIndex: number
  from: CamPoint
  to: CamPoint
  startTimeMinutes: number
  endTimeMinutes: number
  distance: number
  feed: number
  sampleCount: number
  removedVolume: number
  cumulativeRemovedVolume: number
  remainingVolume: number
}
export type CamMaterialRemovalTimeline = {
  status: 'pass' | 'incomplete'
  backend: 'browser-raster'
  model: '2d-column-raster'
  nativeEquivalent: false
  resolution: number
  grid: { columns: number; rows: number; cellSize: [number, number] }
  initialVolume: number
  removedVolume: number
  remainingVolume: number
  durationMinutes: number
  events: CamMaterialRemovalEvent[]
}
export type CamCollisionKind = 'holder-stock' | 'cutter-fixture' | 'holder-fixture'
export type CamCollisionEvent = {
  operationId: string
  pathIndex: number
  kind: CamCollisionKind
  body: 'cutter' | 'holder'
  target: 'stock' | 'fixture'
  fixtureId?: string
  point: CamPoint
  clearance: number
  penetration: number
  message: string
}
export type CamCollisionReport = {
  status: 'pass' | 'collision'
  backend: 'browser-envelope'
  nativeEquivalent: false
  collisions: CamCollisionEvent[]
  operationIds: string[]
  holderCollisions: number
  fixtureCollisions: number
}
export type CamAxisConfiguration = '3-axis' | '4-axis' | '5-axis'
export type CamRotaryAxis = 'A' | 'B' | 'C'
export type CamAxisMotion = { axis: CamRotaryAxis; startDegrees: number; endDegrees: number }
export type CamKinematicOptions = { configuration: CamAxisConfiguration; rotary?: CamAxisMotion; tilt?: CamAxisMotion; limits?: Partial<Record<CamRotaryAxis, [number, number]>> }
export type CamMachinePose = { pathIndex: number; position: CamPoint; a: number; b: number; c: number }
export type CamKinematicViolation = { pathIndex: number; axis: CamRotaryAxis; value: number; minimum: number; maximum: number }
export type CamKinematicResult = { status: 'pass' | 'limit'; configuration: CamAxisConfiguration; poses: CamMachinePose[]; violations: CamKinematicViolation[]; nativeEquivalent: false }
export type CamSanityIssue = { code: string; severity: 'warning' | 'error'; objectId: string; message: string }
export type CamSimulationDiagnostic = { operationId: string; type: 'missing-path' | 'stock' | 'tool-holder' | 'fixture' | 'setup'; message: string }
export type CamSimulationResult = {
  status: 'pass' | 'collision'
  collisions: string[]
  diagnostics: CamSimulationDiagnostic[]
  activeOperations: number
  pathPoints: number
  cutDistance: number
  materialRemoval: CamMaterialRemoval
  collisionReport: CamCollisionReport
}
export type CamPropertyValue = string | number | boolean
export type CamCompound = { id: string; label: string; operationIds: string[] }
export type CamJobMetadata = { comments: string[]; propertyBag: Record<string, CamPropertyValue>; compounds: CamCompound[] }
export type CamSnapshot = {
  id: string
  label: string
  stock: CamStock
  setupSheet: CamSetupSheet
  tools: CamTool[]
  toolControllers: CamToolController[]
  operations: CamOperation[]
  collisionFixtures: CamCollisionFixture[]
  metadata: CamJobMetadata
  materialRemovalEnabled: boolean
  version: number
}
export type CamJobAsset = { schemaVersion: 1; job: CamSnapshot }
export type CamPostprocessor = 'grbl' | 'linuxcnc' | 'mach3-mach4' | 'centroid' | 'marlin' | 'masso-g3' | 'snapmaker'
export type CamApi = {
  snapshot(): CamSnapshot
  subscribe(listener: (snapshot: CamSnapshot) => void): () => void
  exportJob(): string
  importJob(serialized: string): CamSnapshot
  canUndo(): boolean
  canRedo(): boolean
  undo(): CamSnapshot
  redo(): CamSnapshot
  updateStock(stock: CamStock): CamStock
  updateSetupSheet(update: Partial<CamSetupSheet>): CamSetupSheet
  addTool(tool: CamTool): CamTool
  updateTool(toolId: string, update: Partial<Omit<CamTool, 'id'>>): CamTool
  removeTool(toolId: string): void
  exportToolBit(toolId: string): string
  importToolBit(serialized: string, idOverride?: string): CamTool
  addToolController(controller: CamToolController): CamToolController
  updateToolController(controllerId: string, update: Partial<Omit<CamToolController, 'id'>>): CamToolController
  removeToolController(controllerId: string): void
  updateCollisionFixtures(fixtures: CamCollisionFixture[]): CamCollisionFixture[]
  setMaterialRemovalEnabled(enabled: boolean): boolean
  addComment(comment: string): string
  setPropertyBagValue(name: string, value: CamPropertyValue): CamJobMetadata
  createCompound(operationIds: string[], compoundId?: string, label?: string): CamCompound
  addOperation(operation: CamOperationInput): CamOperation
  generatePath(operationId: string): CamOperation
  generateRectangularPath(operationId: string): CamOperation
  replaceGeneratedPath(operationId: string, path: CamPoint[]): CamOperation
  applyDressup(operationId: string, dressup: CamDressup): CamOperation
  toggleOperation(operationId: string, active?: boolean): CamOperation
  copyOperation(operationId: string, copyId?: string): CamOperation
  setStartPoint(operationId: string, pathIndex: number): CamOperation
  removeOperation(operationId: string): void
  sanityCheck(): { status: 'pass' | 'warning' | 'error'; issues: CamSanityIssue[] }
  materialRemovalTimeline(options?: CamMaterialRemovalOptions): CamMaterialRemovalTimeline
  collisionReport(): CamCollisionReport
  kinematics(operationId: string, options: CamKinematicOptions): CamKinematicResult
  simulate(): CamSimulationResult
  postprocessors(): CamPostprocessor[]
  exportGcode(postprocessor?: CamPostprocessor): string
  exportMultiAxisGcode(operationId: string, postprocessor: CamPostprocessor, options: CamKinematicOptions): string
}

const DEFAULT_SETUP: CamSetupSheet = {
  horizontalRapid: 0,
  verticalRapid: 0,
  safeHeightOffset: 3,
  clearanceHeightOffset: 5,
  startDepthExpression: 'OpStartDepth',
  finalDepthExpression: 'OpFinalDepth',
  stepDownExpression: 'OpToolDiameter',
  coolantMode: 'none',
}
const POSTS: CamPostprocessor[] = ['grbl', 'linuxcnc', 'mach3-mach4', 'centroid', 'marlin', 'masso-g3', 'snapmaker']
const MULTI_AXIS_POSTS: CamPostprocessor[] = ['linuxcnc', 'mach3-mach4', 'centroid']
const OPERATION_KINDS: CamOperationKind[] = ['profile', 'pocket', 'contour', 'mill-face', 'helix', 'adaptive', 'slot', 'drilling', 'tapping', 'engrave', 'deburr', 'v-carve', 'pocket-3d', 'surface', 'waterline', 'thread-milling', 'probe', 'area', 'area-workplane', 'custom', 'shape', 'path-shape-tool-controller']
const DRESSUP_KINDS: CamDressupKind[] = ['array', 'axis-map', 'boundary', 'dogbone', 'drag-knife', 'lead-in-out', 'ramp-entry', 'holding-tags', 'z-correct']
const finite = (value: number, label: string) => { if (!Number.isFinite(value)) throw new RangeError(`${label} must be finite.`) }
const positive = (value: number, label: string) => { finite(value, label); if (value <= 0) throw new RangeError(`${label} must be positive.`) }
const nonNegative = (value: number, label: string) => { finite(value, label); if (value < 0) throw new RangeError(`${label} cannot be negative.`) }
const point = (value: CamPoint, label: string) => { if (value.length !== 3 || value.some((entry) => !Number.isFinite(entry))) throw new RangeError(`${label} must contain three finite coordinates.`); return [...value] as CamPoint }
const clone = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T
const distance = (a: CamPoint, b: CamPoint) => Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2])
const interpolatePoint = (a: CamPoint, b: CamPoint, amount: number): CamPoint => [a[0] + (b[0] - a[0]) * amount, a[1] + (b[1] - a[1]) * amount, a[2] + (b[2] - a[2]) * amount]
const slug = (value: string) => value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '') || 'operation'

const normalizeStock = (stock: CamStock): CamStock => {
  const normalized = { mode: stock.mode ?? 'create-box', min: point(stock.min, 'CAM stock.min'), max: point(stock.max, 'CAM stock.max') }
  if (normalized.min.some((value, index) => value >= normalized.max[index])) throw new RangeError('CAM stock bounds must increase.')
  return normalized
}

const normalizeTool = (input: CamTool): CamTool => {
  if (!input.id.trim()) throw new RangeError('CAM tool id is required.')
  if (!input.name.trim()) throw new RangeError('CAM tool name is required.')
  positive(input.diameter, 'CAM tool diameter')
  positive(input.length, 'CAM tool length')
  const tool = { ...input, shape: input.shape ?? 'endmill', cuttingEdgeHeight: input.cuttingEdgeHeight ?? input.length, shankDiameter: input.shankDiameter ?? input.diameter, holderDiameter: input.holderDiameter ?? Math.max(input.shankDiameter ?? input.diameter, (input.shankDiameter ?? input.diameter) * 1.5), holderLength: input.holderLength ?? Math.max(input.length, (input.cuttingEdgeHeight ?? input.length) + 20) }
  positive(tool.cuttingEdgeHeight, 'CAM tool cutting edge height')
  positive(tool.shankDiameter, 'CAM tool shank diameter')
  positive(tool.holderDiameter, 'CAM tool holder diameter')
  positive(tool.holderLength, 'CAM tool holder length')
  if (tool.holderLength < tool.cuttingEdgeHeight) throw new RangeError('CAM tool holder length cannot be below cutting edge height.')
  if (tool.cuttingEdgeHeight > tool.length) throw new RangeError('CAM tool cutting edge height cannot exceed tool length.')
  return tool
}

const normalizeFixture = (input: CamCollisionFixture): CamCollisionFixture => {
  if (!input.id.trim()) throw new RangeError('CAM collision fixture id is required.')
  const min = point(input.min, `CAM collision fixture ${input.id}.min`)
  const max = point(input.max, `CAM collision fixture ${input.id}.max`)
  if (min.some((value, index) => value >= max[index])) throw new RangeError(`CAM collision fixture bounds must increase: ${input.id}`)
  return { id: input.id, kind: input.kind ?? 'fixture', min, max }
}

const boxPenetration = (minA: CamPoint, maxA: CamPoint, minB: CamPoint, maxB: CamPoint) => {
  const overlaps = minA.map((value, index) => Math.min(maxA[index], maxB[index]) - Math.max(value, minB[index]))
  return overlaps.every((value) => value > 1e-9) ? Math.min(...overlaps) : 0
}

const segmentBoxInterval = (from: CamPoint, to: CamPoint, min: CamPoint, max: CamPoint): [number, number] | null => {
  let enter = 0
  let exit = 1
  for (let axis = 0; axis < 3; axis += 1) {
    const delta = to[axis] - from[axis]
    if (Math.abs(delta) < 1e-12) {
      if (from[axis] < min[axis] || from[axis] > max[axis]) return null
      continue
    }
    const first = (min[axis] - from[axis]) / delta
    const second = (max[axis] - from[axis]) / delta
    enter = Math.max(enter, Math.min(first, second))
    exit = Math.min(exit, Math.max(first, second))
    if (enter > exit) return null
  }
  return [enter, exit]
}

const rotatePoint = (input: CamPoint, center: CamPoint, axis: CamRotaryAxis, degrees: number): CamPoint => {
  const radians = degrees * Math.PI / 180
  const cosine = Math.cos(radians)
  const sine = Math.sin(radians)
  const x = input[0] - center[0]
  const y = input[1] - center[1]
  const z = input[2] - center[2]
  if (axis === 'A') return [input[0], center[1] + y * cosine - z * sine, center[2] + y * sine + z * cosine]
  if (axis === 'B') return [center[0] + x * cosine + z * sine, input[1], center[2] - x * sine + z * cosine]
  return [center[0] + x * cosine - y * sine, center[1] + x * sine + y * cosine, input[2]]
}

const normalizeController = (input: CamToolController, tools: Map<string, CamTool>): CamToolController => {
  if (!input.id.trim() || !input.label.trim()) throw new RangeError('CAM tool controller id and label are required.')
  if (!tools.has(input.toolId)) throw new RangeError(`CAM tool does not exist: ${input.toolId}`)
  if (!Number.isInteger(input.toolNumber) || input.toolNumber < 0 || input.toolNumber > 10000) throw new RangeError('CAM tool number must be an integer from 0 to 10000.')
  nonNegative(input.spindleSpeed, 'CAM spindle speed')
  positive(input.horizontalFeed, 'CAM horizontal feed')
  positive(input.verticalFeed, 'CAM vertical feed')
  nonNegative(input.horizontalRapid, 'CAM horizontal rapid')
  nonNegative(input.verticalRapid, 'CAM vertical rapid')
  return clone(input)
}

const normalizeSetupSheet = (input: Partial<CamSetupSheet>): CamSetupSheet => {
  const next = { ...DEFAULT_SETUP, ...input }
  nonNegative(next.horizontalRapid, 'CAM horizontal rapid')
  nonNegative(next.verticalRapid, 'CAM vertical rapid')
  nonNegative(next.safeHeightOffset, 'CAM safe height offset')
  nonNegative(next.clearanceHeightOffset, 'CAM clearance height offset')
  if (next.clearanceHeightOffset < next.safeHeightOffset) throw new RangeError('CAM clearance height offset cannot be below the safe height offset.')
  if (![next.startDepthExpression, next.finalDepthExpression, next.stepDownExpression].every((value) => typeof value === 'string' && value.trim())) throw new RangeError('CAM SetupSheet expressions cannot be empty.')
  if (!['none', 'flood', 'mist'].includes(next.coolantMode)) throw new RangeError(`CAM coolant mode is not supported: ${next.coolantMode}`)
  return clone(next)
}

const normalizeMetadata = (input: Partial<CamJobMetadata> | undefined, operations: Map<string, CamOperation>): CamJobMetadata => {
  if (input?.comments !== undefined && !Array.isArray(input.comments)) throw new RangeError('CAM comments must be an array.')
  const comments = Array.isArray(input?.comments) ? input!.comments.map((comment) => {
    if (typeof comment !== 'string' || !comment.trim() || comment.length > 4096) throw new RangeError('CAM comments must be non-empty strings of at most 4096 characters.')
    return comment.trim()
  }) : []
  if (comments.length > 1024) throw new RangeError('CAM comment count exceeds 1024 entries.')
  const propertyBag: Record<string, CamPropertyValue> = {}
  const source = input?.propertyBag
  if (source !== undefined && (typeof source !== 'object' || Array.isArray(source) || source === null)) throw new RangeError('CAM property bag must be an object.')
  for (const [rawName, value] of Object.entries(source || {})) {
    const name = rawName.trim()
    if (!/^[A-Za-z_][A-Za-z0-9_.-]{0,127}$/.test(name)) throw new RangeError(`CAM property name is invalid: ${rawName}`)
    if (typeof value === 'number') finite(value, `CAM property ${name}`)
    else if (typeof value === 'string') { if (value.length > 4096) throw new RangeError(`CAM property ${name} is too long.`) }
    else if (typeof value !== 'boolean') throw new RangeError(`CAM property ${name} has an unsupported value.`)
    propertyBag[name] = value
  }
  if (Object.keys(propertyBag).length > 256) throw new RangeError('CAM property bag exceeds 256 entries.')
  const compounds: CamCompound[] = []
  const seen = new Set<string>()
  if (input?.compounds !== undefined && !Array.isArray(input.compounds)) throw new RangeError('CAM compounds must be an array.')
  for (const raw of input?.compounds || []) {
    if (!raw || typeof raw.id !== 'string' || !raw.id.trim() || typeof raw.label !== 'string' || !raw.label.trim() || !Array.isArray(raw.operationIds) || raw.operationIds.length < 2) throw new RangeError('CAM compound requires an id, label and at least two operations.')
    const id = raw.id.trim()
    if (seen.has(id)) throw new RangeError(`CAM compound already exists: ${id}`)
    const operationIds = raw.operationIds.map((operationId) => typeof operationId === 'string' ? operationId.trim() : '')
    if (operationIds.some((operationId) => !operationId) || new Set(operationIds).size !== operationIds.length || operationIds.some((operationId) => !operations.has(operationId))) throw new RangeError(`CAM compound operations are invalid: ${id}`)
    seen.add(id)
    compounds.push({ id, label: raw.label.trim(), operationIds })
  }
  if (compounds.length > 1024) throw new RangeError('CAM compound count exceeds 1024 entries.')
  return { comments, propertyBag, compounds }
}

const rectangle = (stock: CamStock, z: number): CamPoint[] => {
  const [minX, minY] = stock.min
  const [maxX, maxY] = stock.max
  return [[minX, minY, z], [maxX, minY, z], [maxX, maxY, z], [minX, maxY, z], [minX, minY, z]]
}

const zigzag = (stock: CamStock, z: number, spacing: number): CamPoint[] => {
  const [minX, minY] = stock.min
  const [maxX, maxY] = stock.max
  const rows = Math.max(1, Math.min(100, Math.ceil((maxY - minY) / spacing)))
  const path: CamPoint[] = []
  for (let row = 0; row <= rows; row += 1) {
    const y = minY + (maxY - minY) * (row / rows)
    path.push(row % 2 === 0 ? [minX, y, z] : [maxX, y, z], row % 2 === 0 ? [maxX, y, z] : [minX, y, z])
  }
  return path
}

const helical = (stock: CamStock, startZ: number, finalZ: number): CamPoint[] => {
  const centerX = (stock.min[0] + stock.max[0]) / 2
  const centerY = (stock.min[1] + stock.max[1]) / 2
  const radius = Math.min(stock.max[0] - stock.min[0], stock.max[1] - stock.min[1]) * 0.35
  const segments = 32
  return Array.from({ length: segments + 1 }, (_, index) => {
    const angle = Math.PI * 2 * index / segments
    return [centerX + Math.cos(angle) * radius, centerY + Math.sin(angle) * radius, startZ + (finalZ - startZ) * index / segments] as CamPoint
  })
}

const applyDressups = (path: CamPoint[], dressups: CamDressup[], stock: CamStock): CamPoint[] => {
  let result = path.map((entry) => [...entry] as CamPoint)
  for (const dressup of dressups) {
    if (dressup.kind === 'array' && result.length > 0) {
      const count = Math.max(1, Math.min(20, Math.floor(typeof dressup.parameters.count === 'number' ? dressup.parameters.count : 2)))
      const offsetX = typeof dressup.parameters.offsetX === 'number' ? dressup.parameters.offsetX : 0
      const offsetY = typeof dressup.parameters.offsetY === 'number' ? dressup.parameters.offsetY : 0
      const source = result
      result = Array.from({ length: count }, (_, index) => source.map(([x, y, z]) => [x + offsetX * index, y + offsetY * index, z] as CamPoint)).flat()
    } else if (dressup.kind === 'axis-map') {
      result = result.map(([x, y, z]) => [y, x, z])
    } else if (dressup.kind === 'boundary') {
      result = result.map(([x, y, z]) => [Math.max(stock.min[0], Math.min(stock.max[0], x)), Math.max(stock.min[1], Math.min(stock.max[1], y)), z])
    } else if (dressup.kind === 'dogbone' && result.length > 2) {
      const radius = Math.max(0, typeof dressup.parameters.radius === 'number' ? dressup.parameters.radius : 0.5)
      const centerX = (stock.min[0] + stock.max[0]) / 2
      const centerY = (stock.min[1] + stock.max[1]) / 2
      result = result.flatMap(([x, y, z]) => {
        const dx = centerX === x ? 0 : Math.sign(centerX - x) * radius
        const dy = centerY === y ? 0 : Math.sign(centerY - y) * radius
        return [[x, y, z] as CamPoint, [Math.max(stock.min[0], Math.min(stock.max[0], x + dx)), Math.max(stock.min[1], Math.min(stock.max[1], y + dy)), z] as CamPoint]
      })
    } else if (dressup.kind === 'drag-knife' && result.length > 1) {
      const offset = Math.max(0, typeof dressup.parameters.offset === 'number' ? dressup.parameters.offset : 0.5)
      result = result.map((entry, index) => {
        if (index === 0) return entry
        const previous = result[index - 1]
        const segmentLength = Math.hypot(entry[0] - previous[0], entry[1] - previous[1]) || 1
        return [Math.max(stock.min[0], Math.min(stock.max[0], entry[0] + (entry[0] - previous[0]) / segmentLength * offset)), Math.max(stock.min[1], Math.min(stock.max[1], entry[1] + (entry[1] - previous[1]) / segmentLength * offset)), entry[2]] as CamPoint
      })
    } else if (dressup.kind === 'ramp-entry' && result.length > 1) {
      result[0] = [result[0][0], result[0][1], stock.max[2]]
    } else if (dressup.kind === 'lead-in-out' && result.length > 1) {
      const length = typeof dressup.parameters.length === 'number' ? dressup.parameters.length : 1
      const first = result[0]
      const last = result[result.length - 1]
      result = [[Math.max(stock.min[0], first[0] - length), first[1], first[2]], ...result, [Math.max(stock.min[0], last[0] - length), last[1], last[2]]]
    } else if (dressup.kind === 'holding-tags' && result.length > 2) {
      const height = typeof dressup.parameters.height === 'number' ? dressup.parameters.height : 0.5
      const index = Math.floor(result.length / 2)
      result[index] = [result[index][0], result[index][1], Math.min(stock.max[2], result[index][2] + height)]
    } else if (dressup.kind === 'z-correct') {
      const offset = typeof dressup.parameters.offset === 'number' ? dressup.parameters.offset : 0
      result = result.map(([x, y, z]) => [x, y, Math.max(stock.min[2], Math.min(stock.max[2], z + offset))])
    }
  }
  return result
}

export const createCamJob = (id = 'cam', label = 'CAM job', initialStock: CamStock = { min: [0, 0, 0], max: [10, 10, 10], mode: 'create-box' }): CamApi => {
  let stock = normalizeStock(initialStock)
  let setupSheet = clone(DEFAULT_SETUP)
  const tools = new Map<string, CamTool>()
  const controllers = new Map<string, CamToolController>()
  const operations = new Map<string, CamOperation>()
  const collisionFixtures = new Map<string, CamCollisionFixture>()
  let metadata: CamJobMetadata = { comments: [], propertyBag: {}, compounds: [] }
  let materialRemovalEnabled = true
  const listeners = new Set<(snapshot: CamSnapshot) => void>()
  const undoStack: CamSnapshot[] = []
  const redoStack: CamSnapshot[] = []
  let version = 0

  const snapshot = (): CamSnapshot => ({ id, label, stock: clone(stock), setupSheet: clone(setupSheet), tools: [...tools.values()].map(clone), toolControllers: [...controllers.values()].map(clone), operations: [...operations.values()].map(clone), collisionFixtures: [...collisionFixtures.values()].map(clone), metadata: clone(metadata), materialRemovalEnabled, version })
  const emit = () => { const value = snapshot(); listeners.forEach((listener) => listener(value)) }
  let committedSnapshot = snapshot()
  const changed = () => { undoStack.push(clone(committedSnapshot)); if (undoStack.length > 100) undoStack.shift(); redoStack.length = 0; version += 1; committedSnapshot = snapshot(); emit() }
  const tool = (toolId: string) => { const value = tools.get(toolId); if (!value) throw new RangeError(`CAM tool does not exist: ${toolId}`); return value }
  const controller = (controllerId: string) => { const value = controllers.get(controllerId); if (!value) throw new RangeError(`CAM tool controller does not exist: ${controllerId}`); return value }
  const operation = (operationId: string) => { const value = operations.get(operationId); if (!value) throw new RangeError(`CAM operation does not exist: ${operationId}`); return value }

  const normalizeImportedSnapshot = (source: CamSnapshot): CamSnapshot => {
    if (source.id !== id || source.label !== label) throw new RangeError(`CAM Job asset identity does not match ${id}.`)
    if (!Number.isSafeInteger(source.version) || source.version < 0) throw new RangeError('CAM Job asset version must be a non-negative integer.')
    if (!Array.isArray(source.tools) || !Array.isArray(source.toolControllers) || !Array.isArray(source.operations) || !Array.isArray(source.collisionFixtures)) throw new RangeError('CAM Job asset collections are missing.')
    if (source.tools.length > 10_000 || source.toolControllers.length > 10_000 || source.operations.length > 100_000 || source.collisionFixtures.length > 10_000) throw new RangeError('CAM Job asset exceeds collection limits.')
    const importedStock = normalizeStock(source.stock)
    const importedSetup = normalizeSetupSheet(source.setupSheet)
    const importedTools = new Map<string, CamTool>()
    for (const input of source.tools) {
      if (importedTools.has(input.id)) throw new RangeError(`CAM Job asset has duplicate tool: ${input.id}`)
      const value = normalizeTool(input)
      importedTools.set(value.id, value)
    }
    const importedControllers = new Map<string, CamToolController>()
    const toolNumbers = new Set<number>()
    for (const input of source.toolControllers) {
      if (importedControllers.has(input.id)) throw new RangeError(`CAM Job asset has duplicate Tool Controller: ${input.id}`)
      const value = normalizeController(input, importedTools)
      if (toolNumbers.has(value.toolNumber)) throw new RangeError(`CAM Job asset has duplicate tool number: ${value.toolNumber}`)
      importedControllers.set(value.id, value)
      toolNumbers.add(value.toolNumber)
    }
    const importedOperations = new Map<string, CamOperation>()
    let totalPathPoints = 0
    for (const input of source.operations) {
      if (!input.id?.trim() || importedOperations.has(input.id)) throw new RangeError(`CAM Job asset has duplicate or empty operation: ${input.id || ''}`)
      if (!OPERATION_KINDS.includes(input.kind)) throw new RangeError(`CAM Job asset operation kind is not supported: ${input.kind}`)
      if (!importedTools.has(input.toolId)) throw new RangeError(`CAM Job asset operation tool does not exist: ${input.toolId}`)
      const activeController = importedControllers.get(input.controllerId)
      if (!activeController || activeController.toolId !== input.toolId) throw new RangeError(`CAM Job asset operation controller does not match: ${input.id}`)
      positive(input.depth, 'CAM operation depth')
      if (input.depth > importedStock.max[2] - importedStock.min[2]) throw new RangeError('CAM operation depth is outside stock.')
      positive(input.feed, 'CAM operation feed')
      positive(input.verticalFeed, 'CAM operation vertical feed')
      positive(input.stepDown, 'CAM operation step down')
      positive(input.stepOver, 'CAM operation step over')
      if (input.stepOver > 100) throw new RangeError('CAM operation step over cannot exceed 100 percent.')
      if (!['climb', 'conventional', 'either'].includes(input.direction) || !['none', 'flood', 'mist'].includes(input.coolantMode)) throw new RangeError(`CAM Job asset operation modes are invalid: ${input.id}`)
      if (!['draft', 'generated', 'collision', 'disabled'].includes(input.status)) throw new RangeError(`CAM Job asset operation status is invalid: ${input.id}`)
      if (!Array.isArray(input.locations) || !Array.isArray(input.path) || !Array.isArray(input.dressups)) throw new RangeError(`CAM Job asset operation arrays are missing: ${input.id}`)
      totalPathPoints += input.path.length
      if (totalPathPoints > 1_000_000) throw new RangeError('CAM Job asset exceeds the path point limit.')
      const dressups = input.dressups.map((dressup) => {
        if (!DRESSUP_KINDS.includes(dressup.kind) || !dressup.parameters || typeof dressup.parameters !== 'object' || Array.isArray(dressup.parameters)) throw new RangeError(`CAM Job asset dress-up is invalid: ${input.id}`)
        for (const [key, value] of Object.entries(dressup.parameters)) if (typeof value === 'number') finite(value, `CAM dress-up ${key}`); else if (typeof value !== 'string' && typeof value !== 'boolean') throw new RangeError(`CAM dress-up parameter is invalid: ${key}`)
        return clone(dressup)
      })
      importedOperations.set(input.id, { ...clone(input), locations: input.locations.map((entry, index) => point(entry, `CAM operation location ${index + 1}`)), path: input.path.map((entry, index) => point(entry, `CAM operation path point ${index + 1}`)), dressups })
    }
    const importedFixtures = source.collisionFixtures.map(normalizeFixture)
    if (new Set(importedFixtures.map((fixture) => fixture.id)).size !== importedFixtures.length) throw new RangeError('CAM collision fixture ids must be unique.')
    const importedMetadata = normalizeMetadata(source.metadata, importedOperations)
    return { id, label, stock: importedStock, setupSheet: importedSetup, tools: [...importedTools.values()].map(clone), toolControllers: [...importedControllers.values()].map(clone), operations: [...importedOperations.values()].map(clone), collisionFixtures: importedFixtures.map(clone), metadata: importedMetadata, materialRemovalEnabled: source.materialRemovalEnabled === true, version: source.version }
  }
  const restoreSnapshot = (source: CamSnapshot) => {
    stock = clone(source.stock)
    setupSheet = clone(source.setupSheet)
    tools.clear(); source.tools.forEach((entry) => tools.set(entry.id, clone(entry)))
    controllers.clear(); source.toolControllers.forEach((entry) => controllers.set(entry.id, clone(entry)))
    operations.clear(); source.operations.forEach((entry) => operations.set(entry.id, clone(entry)))
    collisionFixtures.clear(); source.collisionFixtures.forEach((entry) => collisionFixtures.set(entry.id, clone(entry)))
    metadata = clone(source.metadata)
    materialRemovalEnabled = source.materialRemovalEnabled
    version = source.version
  }
  const exportJob = () => `${JSON.stringify({ schemaVersion: 1, job: snapshot() } satisfies CamJobAsset, null, 2)}\n`
  const importJob = (serialized: string) => {
    if (new TextEncoder().encode(serialized).byteLength > 16 * 1024 * 1024) throw new RangeError('CAM Job asset exceeds the 16 MiB import limit.')
    let parsed: unknown
    try { parsed = JSON.parse(serialized) } catch { throw new RangeError('CAM Job asset must be valid JSON.') }
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || !('schemaVersion' in parsed) || parsed.schemaVersion !== 1 || !('job' in parsed) || !parsed.job || typeof parsed.job !== 'object' || Array.isArray(parsed.job)) throw new RangeError('CAM Job asset schema is not supported.')
    const imported = normalizeImportedSnapshot(parsed.job as CamSnapshot)
    undoStack.push(clone(committedSnapshot)); if (undoStack.length > 100) undoStack.shift()
    redoStack.length = 0
    restoreSnapshot(imported)
    committedSnapshot = snapshot()
    emit()
    return snapshot()
  }
  const canUndo = () => undoStack.length > 0
  const canRedo = () => redoStack.length > 0
  const undo = () => {
    const previous = undoStack.pop()
    if (!previous) return snapshot()
    redoStack.push(snapshot())
    restoreSnapshot(previous)
    committedSnapshot = snapshot()
    emit()
    return snapshot()
  }
  const redo = () => {
    const next = redoStack.pop()
    if (!next) return snapshot()
    undoStack.push(snapshot())
    restoreSnapshot(next)
    committedSnapshot = snapshot()
    emit()
    return snapshot()
  }

  const updateStock = (input: CamStock) => {
    stock = normalizeStock(input)
    for (const target of operations.values()) { target.path = []; target.status = target.active ? 'draft' : 'disabled' }
    changed()
    return clone(stock)
  }
  const updateSetupSheet = (update: Partial<CamSetupSheet>) => {
    setupSheet = normalizeSetupSheet({ ...setupSheet, ...update })
    changed()
    return clone(setupSheet)
  }
  const addTool = (input: CamTool) => {
    if (tools.has(input.id)) throw new RangeError(`CAM tool already exists: ${input.id}`)
    const value = normalizeTool(input)
    tools.set(value.id, value)
    changed()
    return clone(value)
  }
  const updateTool = (toolId: string, update: Partial<Omit<CamTool, 'id'>>) => {
    const current = tool(toolId)
    const value = normalizeTool({ ...current, ...update, id: toolId })
    tools.set(toolId, value)
    for (const target of operations.values()) if (target.toolId === toolId) { target.path = []; target.status = target.active ? 'draft' : 'disabled' }
    changed()
    return clone(value)
  }
  const removeTool = (toolId: string) => {
    tool(toolId)
    if ([...controllers.values()].some((candidate) => candidate.toolId === toolId)) throw new RangeError(`CAM tool is used by a Tool Controller: ${toolId}`)
    tools.delete(toolId)
    changed()
  }
  const exportToolBit = (toolId: string) => JSON.stringify({ schemaVersion: 1, tool: clone(tool(toolId)) } satisfies CamToolBitAsset, null, 2)
  const importToolBit = (serialized: string, idOverride?: string) => {
    let parsed: unknown
    try { parsed = JSON.parse(serialized) } catch { throw new RangeError('CAM ToolBit asset must be valid JSON.') }
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || !('schemaVersion' in parsed) || parsed.schemaVersion !== 1 || !('tool' in parsed) || !parsed.tool || typeof parsed.tool !== 'object' || Array.isArray(parsed.tool)) throw new RangeError('CAM ToolBit asset schema is not supported.')
    const assetTool = parsed.tool as CamTool
    return addTool({ ...assetTool, id: idOverride?.trim() || assetTool.id })
  }
  const addToolController = (input: CamToolController) => {
    if (controllers.has(input.id)) throw new RangeError(`CAM tool controller already exists: ${input.id}`)
    if ([...controllers.values()].some((candidate) => candidate.toolNumber === input.toolNumber)) throw new RangeError(`CAM tool number is already assigned: ${input.toolNumber}`)
    const value = normalizeController(input, tools)
    controllers.set(value.id, value)
    changed()
    return clone(value)
  }
  const updateToolController = (controllerId: string, update: Partial<Omit<CamToolController, 'id'>>) => {
    const current = controller(controllerId)
    const value = normalizeController({ ...current, ...update, id: controllerId }, tools)
    if ([...controllers.values()].some((candidate) => candidate.id !== controllerId && candidate.toolNumber === value.toolNumber)) throw new RangeError(`CAM tool number is already assigned: ${value.toolNumber}`)
    controllers.set(controllerId, value)
    for (const target of operations.values()) if (target.controllerId === controllerId) { target.path = []; target.status = target.active ? 'draft' : 'disabled' }
    changed()
    return clone(value)
  }
  const removeToolController = (controllerId: string) => {
    controller(controllerId)
    if ([...operations.values()].some((candidate) => candidate.controllerId === controllerId)) throw new RangeError(`CAM Tool Controller is used by an operation: ${controllerId}`)
    controllers.delete(controllerId)
    changed()
  }
  const updateCollisionFixtures = (fixtures: CamCollisionFixture[]) => {
    const normalized = fixtures.map(normalizeFixture)
    if (new Set(normalized.map((fixture) => fixture.id)).size !== normalized.length) throw new RangeError('CAM collision fixture ids must be unique.')
    collisionFixtures.clear()
    normalized.forEach((fixture) => collisionFixtures.set(fixture.id, fixture))
    changed()
    return normalized.map(clone)
  }
  const setMaterialRemovalEnabled = (enabled: boolean) => {
    materialRemovalEnabled = enabled === true
    changed()
    return materialRemovalEnabled
  }
  const addComment = (comment: string) => {
    if (typeof comment !== 'string' || !comment.trim() || comment.length > 4096) throw new RangeError('CAM comment must be a non-empty string of at most 4096 characters.')
    const value = comment.trim()
    if (metadata.comments.length >= 1024) throw new RangeError('CAM comment count exceeds 1024 entries.')
    metadata = { ...metadata, comments: [...metadata.comments, value] }
    changed()
    return value
  }
  const setPropertyBagValue = (name: string, value: CamPropertyValue) => {
    const key = name.trim()
    if (!/^[A-Za-z_][A-Za-z0-9_.-]{0,127}$/.test(key)) throw new RangeError(`CAM property name is invalid: ${name}`)
    if (typeof value === 'number') finite(value, `CAM property ${key}`)
    else if (typeof value === 'string') { if (value.length > 4096) throw new RangeError(`CAM property ${key} is too long.`) }
    else if (typeof value !== 'boolean') throw new RangeError(`CAM property ${key} has an unsupported value.`)
    if (!(key in metadata.propertyBag) && Object.keys(metadata.propertyBag).length >= 256) throw new RangeError('CAM property bag exceeds 256 entries.')
    metadata = { ...metadata, propertyBag: { ...metadata.propertyBag, [key]: value } }
    changed()
    return clone(metadata)
  }
  const createCompound = (operationIds: string[], compoundId?: string, label?: string) => {
    const ids = operationIds.map((operationId) => typeof operationId === 'string' ? operationId.trim() : '')
    if (ids.length < 2 || ids.some((operationId) => !operationId) || new Set(ids).size !== ids.length || ids.some((operationId) => !operations.has(operationId))) throw new RangeError('CAM compound requires at least two existing, unique operations.')
    if (metadata.compounds.length >= 1024) throw new RangeError('CAM compound count exceeds 1024 entries.')
    const idValue = (compoundId || `compound-${metadata.compounds.length + 1}`).trim()
    const labelValue = (label || idValue).trim()
    if (!idValue || !labelValue) throw new RangeError('CAM compound id and label are required.')
    if (metadata.compounds.some((compound) => compound.id === idValue)) throw new RangeError(`CAM compound already exists: ${idValue}`)
    const value = { id: idValue, label: labelValue, operationIds: ids }
    metadata = { ...metadata, compounds: [...metadata.compounds, value] }
    changed()
    return clone(value)
  }
  const ensureController = (toolId: string, feed: number, verticalFeed?: number) => {
    const existing = [...controllers.values()].find((candidate) => candidate.toolId === toolId)
    if (existing) return existing
    const value = normalizeController({ id: `TC-${toolId}`, label: `Tool Controller ${toolId}`, toolId, toolNumber: controllers.size + 1, spindleSpeed: 12000, spindleDirection: 'forward', horizontalFeed: feed, verticalFeed: verticalFeed ?? Math.max(1, feed / 3), horizontalRapid: setupSheet.horizontalRapid, verticalRapid: setupSheet.verticalRapid }, tools)
    controllers.set(value.id, value)
    return value
  }
  const addOperation = (input: CamOperationInput) => {
    if (!input.id.trim() || operations.has(input.id)) throw new RangeError(`CAM operation already exists: ${input.id}`)
    const cutter = tool(input.toolId)
    positive(input.depth, 'CAM operation depth')
    if (input.depth > stock.max[2] - stock.min[2]) throw new RangeError('CAM operation depth is outside stock.')
    positive(input.feed, 'CAM operation feed')
    const activeController = input.controllerId ? controller(input.controllerId) : ensureController(input.toolId, input.feed, input.verticalFeed)
    if (activeController.toolId !== input.toolId) throw new RangeError('CAM operation tool and controller do not match.')
    const stepDown = input.stepDown ?? Math.min(cutter.diameter, input.depth)
    positive(stepDown, 'CAM operation step down')
    const stepOver = input.stepOver ?? 50
    positive(stepOver, 'CAM operation step over')
    if (stepOver > 100) throw new RangeError('CAM operation step over cannot exceed 100 percent.')
    const locations = (input.locations ?? []).map((entry, index) => point(entry, `CAM operation location ${index + 1}`))
    const value: CamOperation = {
      id: input.id,
      label: input.label?.trim() || input.id,
      kind: input.kind,
      toolId: input.toolId,
      controllerId: activeController.id,
      depth: input.depth,
      feed: input.feed,
      verticalFeed: input.verticalFeed ?? activeController.verticalFeed,
      stepDown,
      stepOver,
      direction: input.direction ?? 'climb',
      coolantMode: input.coolantMode ?? setupSheet.coolantMode,
      active: input.active ?? true,
      locations,
      dressups: [],
      path: [],
      status: input.active === false ? 'disabled' : 'draft',
    }
    operations.set(value.id, value)
    changed()
    return clone(value)
  }
  const generatePath = (operationId: string) => {
    const target = operation(operationId)
    if (!target.active) throw new RangeError(`CAM operation is disabled: ${operationId}`)
    const cutter = tool(target.toolId)
    const top = stock.max[2]
    const layers = Math.max(1, Math.ceil(target.depth / target.stepDown))
    const generated: CamPoint[] = []
    for (let layer = 1; layer <= layers; layer += 1) {
      const z = top - Math.min(target.depth, layer * target.stepDown)
      if (target.kind === 'profile' || target.kind === 'contour' || target.kind === 'waterline' || target.kind === 'area' || target.kind === 'area-workplane' || target.kind === 'shape' || target.kind === 'path-shape-tool-controller') generated.push(...rectangle(stock, z))
      else if (target.kind === 'pocket' || target.kind === 'mill-face' || target.kind === 'adaptive' || target.kind === 'pocket-3d' || target.kind === 'surface') generated.push(...zigzag(stock, z, Math.max(0.01, cutter.diameter * target.stepOver / 100)))
      else if (target.kind === 'helix' || target.kind === 'thread-milling') generated.push(...helical(stock, top - Math.min(target.depth, (layer - 1) * target.stepDown), z))
      else if (target.kind === 'slot') generated.push([(stock.min[0] + stock.max[0]) / 2, stock.min[1], z], [(stock.min[0] + stock.max[0]) / 2, stock.max[1], z])
      else if (target.kind === 'drilling' || target.kind === 'tapping' || target.kind === 'probe') {
        const locations = target.locations.length ? target.locations : [[(stock.min[0] + stock.max[0]) / 2, (stock.min[1] + stock.max[1]) / 2, top] as CamPoint]
        for (const location of locations) generated.push([location[0], location[1], top], [location[0], location[1], z], [location[0], location[1], top])
      } else generated.push([stock.min[0], stock.min[1], z], [stock.max[0], stock.max[1], z])
    }
    target.path = applyDressups(generated, target.dressups, stock)
    target.status = 'generated'
    changed()
    return clone(target)
  }
  const replaceGeneratedPath = (operationId: string, replacement: CamPoint[]) => {
    const target = operation(operationId)
    if (!target.active) throw new RangeError(`CAM operation is disabled: ${operationId}`)
    if (replacement.length < 2) throw new RangeError('CAM generated path requires at least two points.')
    target.path = replacement.map((entry, index) => point(entry, `CAM replacement path point ${index + 1}`))
    target.status = 'generated'
    changed()
    return clone(target)
  }
  const applyDressup = (operationId: string, input: CamDressup) => {
    const target = operation(operationId)
    if (target.dressups.some((candidate) => candidate.kind === input.kind)) throw new RangeError(`CAM dress-up already exists on ${operationId}: ${input.kind}`)
    for (const [key, value] of Object.entries(input.parameters)) if (typeof value === 'number') finite(value, `CAM dress-up ${key}`)
    target.dressups.push(clone(input))
    target.path = []
    target.status = target.active ? 'draft' : 'disabled'
    changed()
    return clone(target)
  }
  const toggleOperation = (operationId: string, active = !operation(operationId).active) => {
    const target = operation(operationId)
    target.active = active
    target.status = active ? (target.path.length ? 'generated' : 'draft') : 'disabled'
    changed()
    return clone(target)
  }
  const copyOperation = (operationId: string, copyId?: string) => {
    const source = operation(operationId)
    const idCandidate = copyId?.trim() || `${source.id}-copy`
    if (operations.has(idCandidate)) throw new RangeError(`CAM operation already exists: ${idCandidate}`)
    const copied = clone({ ...source, id: idCandidate, label: `${source.label} Copy` })
    operations.set(copied.id, copied)
    changed()
    return clone(copied)
  }
  const setStartPoint = (operationId: string, pathIndex: number) => {
    const target = operation(operationId)
    if (target.path.length < 2) throw new RangeError(`CAM operation has no generated path: ${operationId}`)
    const closed = distance(target.path[0], target.path[target.path.length - 1]) < 1e-9
    const source = closed ? target.path.slice(0, -1) : [...target.path]
    if (!Number.isInteger(pathIndex) || pathIndex < 0 || pathIndex >= source.length) throw new RangeError('CAM start point index is outside the generated path.')
    const rotated = [...source.slice(pathIndex), ...source.slice(0, pathIndex)].map((entry) => [...entry] as CamPoint)
    target.path = closed ? [...rotated, [...rotated[0]] as CamPoint] : rotated
    changed()
    return clone(target)
  }
  const removeOperation = (operationId: string) => {
    if (!operations.delete(operationId)) throw new RangeError(`CAM operation does not exist: ${operationId}`)
    const compounds = metadata.compounds.map((compound) => ({ ...compound, operationIds: compound.operationIds.filter((id) => id !== operationId) })).filter((compound) => compound.operationIds.length >= 2)
    if (compounds.length !== metadata.compounds.length || compounds.some((compound, index) => compound.operationIds.length !== metadata.compounds[index]?.operationIds.length)) metadata = { ...metadata, compounds }
    changed()
  }
  const sanityCheck = () => {
    const issues: CamSanityIssue[] = []
    if (tools.size === 0) issues.push({ code: 'CAM_NO_TOOL', severity: 'error', objectId: id, message: 'The Job has no ToolBit.' })
    if (controllers.size === 0) issues.push({ code: 'CAM_NO_TOOL_CONTROLLER', severity: 'error', objectId: id, message: 'The Job has no Tool Controller.' })
    if (operations.size === 0) issues.push({ code: 'CAM_NO_OPERATION', severity: 'warning', objectId: id, message: 'The Job has no operations.' })
    for (const target of operations.values()) {
      if (!target.active) issues.push({ code: 'CAM_OPERATION_INACTIVE', severity: 'warning', objectId: target.id, message: `${target.label} is inactive.` })
      else if (target.path.length === 0) issues.push({ code: 'CAM_PATH_NOT_GENERATED', severity: 'error', objectId: target.id, message: `${target.label} has no generated path.` })
    }
    return { status: issues.some((issue) => issue.severity === 'error') ? 'error' as const : issues.length ? 'warning' as const : 'pass' as const, issues }
  }
  const materialRemovalTimeline = (options: CamMaterialRemovalOptions = {}): CamMaterialRemovalTimeline => {
    if (options.resolution !== undefined) positive(options.resolution, 'CAM material removal resolution')
    const maxCells = options.maxCells ?? 65_536
    if (!Number.isInteger(maxCells) || maxCells < 1 || maxCells > 1_000_000) throw new RangeError('CAM material removal maxCells must be an integer from 1 to 1000000.')
    const width = stock.max[0] - stock.min[0]
    const height = stock.max[1] - stock.min[1]
    const stockHeight = stock.max[2] - stock.min[2]
    const initialVolume = width * height * stockHeight
    const requestedResolution = options.resolution ?? Math.max(width, height) / 96
    let columns = Math.max(1, Math.ceil(width / requestedResolution))
    let rows = Math.max(1, Math.ceil(height / requestedResolution))
    if (columns * rows > maxCells) {
      const scale = Math.sqrt(maxCells / (columns * rows))
      columns = Math.max(1, Math.floor(columns * scale))
      rows = Math.max(1, Math.floor(rows * scale))
      while (columns * rows > maxCells) {
        if (columns >= rows && columns > 1) columns -= 1
        else rows -= 1
      }
    }
    const cellWidth = width / columns
    const cellHeight = height / rows
    const resolution = Math.max(cellWidth, cellHeight)
    const cellArea = cellWidth * cellHeight
    const columnTops = new Float64Array(columns * rows)
    columnTops.fill(stock.max[2])
    const events: CamMaterialRemovalEvent[] = []
    let removedVolume = 0
    let elapsedMinutes = 0
    let complete = false
    let incomplete = false

    const cutAt = (entry: CamPoint, radius: number) => {
      if (entry[2] >= stock.max[2] || entry[0] + radius < stock.min[0] || entry[0] - radius > stock.max[0] || entry[1] + radius < stock.min[1] || entry[1] - radius > stock.max[1]) return 0
      const cutZ = Math.max(stock.min[2], entry[2])
      const minColumn = Math.max(0, Math.floor((entry[0] - radius - stock.min[0]) / cellWidth))
      const maxColumn = Math.min(columns - 1, Math.floor((entry[0] + radius - stock.min[0]) / cellWidth))
      const minRow = Math.max(0, Math.floor((entry[1] - radius - stock.min[1]) / cellHeight))
      const maxRow = Math.min(rows - 1, Math.floor((entry[1] + radius - stock.min[1]) / cellHeight))
      let removed = 0
      for (let row = minRow; row <= maxRow; row += 1) {
        const cellMinY = stock.min[1] + row * cellHeight
        const cellMaxY = cellMinY + cellHeight
        const nearestY = Math.max(cellMinY, Math.min(cellMaxY, entry[1]))
        for (let column = minColumn; column <= maxColumn; column += 1) {
          const cellMinX = stock.min[0] + column * cellWidth
          const cellMaxX = cellMinX + cellWidth
          const nearestX = Math.max(cellMinX, Math.min(cellMaxX, entry[0]))
          if (Math.hypot(nearestX - entry[0], nearestY - entry[1]) > radius) continue
          const cellIndex = row * columns + column
          const previousTop = columnTops[cellIndex]
          if (cutZ >= previousTop) continue
          columnTops[cellIndex] = cutZ
          removed += (previousTop - cutZ) * cellArea
        }
      }
      return removed
    }

    for (const target of operations.values()) {
      if (!target.active) continue
      if (target.path.length < 2) { incomplete = true; continue }
      complete = true
      const cutter = tool(target.toolId)
      const radius = cutter.diameter / 2
      const sampleSpacing = Math.max(1e-6, Math.min(cutter.diameter / 2, resolution))
      for (let pathIndex = 0; pathIndex < target.path.length; pathIndex += 1) {
        const to = target.path[pathIndex]
        const from = pathIndex === 0 ? to : target.path[pathIndex - 1]
        const segmentDistance = distance(from, to)
        const sampleCount = pathIndex === 0 ? 1 : Math.max(1, Math.min(8192, Math.ceil(segmentDistance / sampleSpacing)))
        const startTimeMinutes = elapsedMinutes
        const beforeVolume = removedVolume
        if (pathIndex === 0) removedVolume = Math.min(initialVolume, removedVolume + cutAt(to, radius))
        else for (let sample = 1; sample <= sampleCount; sample += 1) removedVolume = Math.min(initialVolume, removedVolume + cutAt(interpolatePoint(from, to, sample / sampleCount), radius))
        elapsedMinutes = Number((elapsedMinutes + segmentDistance / target.feed).toFixed(12))
        events.push({
          operationId: target.id,
          pathIndex,
          from: [...from] as CamPoint,
          to: [...to] as CamPoint,
          startTimeMinutes,
          endTimeMinutes: elapsedMinutes,
          distance: segmentDistance,
          feed: target.feed,
          sampleCount,
          removedVolume: removedVolume - beforeVolume,
          cumulativeRemovedVolume: removedVolume,
          remainingVolume: Math.max(0, initialVolume - removedVolume),
        })
      }
    }
    return {
      status: complete && !incomplete ? 'pass' : 'incomplete',
      backend: 'browser-raster',
      model: '2d-column-raster',
      nativeEquivalent: false,
      resolution,
      grid: { columns, rows, cellSize: [cellWidth, cellHeight] },
      initialVolume,
      removedVolume,
      remainingVolume: Math.max(0, initialVolume - removedVolume),
      durationMinutes: elapsedMinutes,
      events,
    }
  }
  const collisionReport = (): CamCollisionReport => {
    const collisions: CamCollisionEvent[] = []
    const appendCollision = (target: CamOperation, pathIndex: number, from: CamPoint, to: CamPoint, body: 'cutter' | 'holder', collisionTarget: 'stock' | 'fixture', targetBounds: CamStock | CamCollisionFixture, fixtureId?: string) => {
      const cutter = tool(target.toolId)
      const radius = body === 'cutter' ? cutter.diameter / 2 : (cutter.holderDiameter ?? cutter.shankDiameter ?? cutter.diameter) / 2
      const minOffset = body === 'cutter' ? 0 : cutter.cuttingEdgeHeight!
      const maxOffset = body === 'cutter' ? cutter.cuttingEdgeHeight! : (cutter.holderLength ?? cutter.length)
      const expandedMin: CamPoint = [targetBounds.min[0] - radius, targetBounds.min[1] - radius, targetBounds.min[2] - maxOffset]
      const expandedMax: CamPoint = [targetBounds.max[0] + radius, targetBounds.max[1] + radius, targetBounds.max[2] - minOffset]
      const interval = segmentBoxInterval(from, to, expandedMin, expandedMax)
      if (!interval) return
      const collisionPoint = interpolatePoint(from, to, (interval[0] + interval[1]) / 2)
      const bodyMin: CamPoint = [collisionPoint[0] - radius, collisionPoint[1] - radius, collisionPoint[2] + minOffset]
      const bodyMax: CamPoint = [collisionPoint[0] + radius, collisionPoint[1] + radius, collisionPoint[2] + maxOffset]
      const penetration = boxPenetration(bodyMin, bodyMax, targetBounds.min, targetBounds.max)
      if (penetration <= 0) return
      const kind: CamCollisionKind = collisionTarget === 'stock' ? 'holder-stock' : body === 'cutter' ? 'cutter-fixture' : 'holder-fixture'
      const targetLabel = collisionTarget === 'stock' ? 'stock' : `fixture ${fixtureId}`
      collisions.push({
        operationId: target.id,
        pathIndex,
        kind,
        body,
        target: collisionTarget,
        ...(fixtureId ? { fixtureId } : {}),
        point: collisionPoint,
        clearance: -penetration,
        penetration,
        message: `${target.label} ${body} intersects ${targetLabel} by ${penetration.toFixed(3)} mm.`,
      })
    }

    for (const target of operations.values()) {
      if (!target.active || target.path.length === 0) continue
      for (let pathIndex = 0; pathIndex < target.path.length; pathIndex += 1) {
        const to = target.path[pathIndex]
        const from = pathIndex === 0 ? to : target.path[pathIndex - 1]
        appendCollision(target, pathIndex, from, to, 'holder', 'stock', stock)
        for (const fixture of collisionFixtures.values()) {
          appendCollision(target, pathIndex, from, to, 'cutter', 'fixture', fixture, fixture.id)
          appendCollision(target, pathIndex, from, to, 'holder', 'fixture', fixture, fixture.id)
        }
      }
    }
    const operationIds = [...new Set(collisions.map((collision) => collision.operationId))]
    return {
      status: collisions.length ? 'collision' : 'pass',
      backend: 'browser-envelope',
      nativeEquivalent: false,
      collisions,
      operationIds,
      holderCollisions: collisions.filter((collision) => collision.body === 'holder').length,
      fixtureCollisions: collisions.filter((collision) => collision.target === 'fixture').length,
    }
  }
  const kinematics = (operationId: string, options: CamKinematicOptions): CamKinematicResult => {
    const target = operation(operationId)
    if (target.path.length < 2) throw new RangeError(`CAM operation has no generated path: ${operationId}`)
    if (!['3-axis', '4-axis', '5-axis'].includes(options.configuration)) throw new RangeError(`CAM axis configuration is not supported: ${options.configuration}`)
    if (options.configuration !== '3-axis' && !options.rotary) throw new RangeError(`${options.configuration} kinematics requires a rotary axis.`)
    if (options.configuration === '5-axis' && !options.tilt) throw new RangeError('5-axis kinematics requires a tilt axis.')
    if (options.rotary && options.tilt && options.rotary.axis === options.tilt.axis) throw new RangeError('CAM rotary and tilt axes must be different.')
    const motions = options.configuration === '3-axis' ? [] : options.configuration === '4-axis' ? [options.rotary!] : [options.rotary!, options.tilt!]
    for (const motion of motions) {
      if (!['A', 'B', 'C'].includes(motion.axis)) throw new RangeError(`CAM rotary axis is not supported: ${motion.axis}`)
      finite(motion.startDegrees, `CAM ${motion.axis} start angle`)
      finite(motion.endDegrees, `CAM ${motion.axis} end angle`)
    }
    const defaultLimits: Record<CamRotaryAxis, [number, number]> = { A: [-360, 360], B: [-120, 120], C: [-360, 360] }
    const limits = { ...defaultLimits, ...options.limits }
    for (const axis of ['A', 'B', 'C'] as CamRotaryAxis[]) {
      const range = limits[axis]
      if (!Array.isArray(range) || range.length !== 2) throw new RangeError(`CAM ${axis} limit must contain two values.`)
      finite(range[0], `CAM ${axis} minimum`); finite(range[1], `CAM ${axis} maximum`)
      if (range[0] >= range[1]) throw new RangeError(`CAM ${axis} minimum must be below maximum.`)
    }
    const center: CamPoint = [(stock.min[0] + stock.max[0]) / 2, (stock.min[1] + stock.max[1]) / 2, (stock.min[2] + stock.max[2]) / 2]
    const violations: CamKinematicViolation[] = []
    const poses = target.path.map((entry, pathIndex) => {
      const amount = target.path.length === 1 ? 0 : pathIndex / (target.path.length - 1)
      const values: Record<CamRotaryAxis, number> = { A: 0, B: 0, C: 0 }
      let position = [...entry] as CamPoint
      for (const motion of motions) {
        const value = motion.startDegrees + (motion.endDegrees - motion.startDegrees) * amount
        values[motion.axis] = value
        position = rotatePoint(position, center, motion.axis, value)
        const [minimum, maximum] = limits[motion.axis]
        if (value < minimum || value > maximum) violations.push({ pathIndex, axis: motion.axis, value, minimum, maximum })
      }
      return { pathIndex, position, a: values.A, b: values.B, c: values.C }
    })
    return { status: violations.length ? 'limit' : 'pass', configuration: options.configuration, poses, violations, nativeEquivalent: false }
  }
  const simulate = (): CamSimulationResult => {
    const diagnostics: CamSimulationDiagnostic[] = []
    let pathPoints = 0
    let cutDistance = 0
    let activeOperations = 0
    const stockVolume = (stock.max[0] - stock.min[0]) * (stock.max[1] - stock.min[1]) * (stock.max[2] - stock.min[2])
    const collisionsByEnvelope = collisionReport()
    const diagnosticKeys = new Set<string>()
    for (const collision of collisionsByEnvelope.collisions) {
      const type = collision.target === 'fixture' ? 'fixture' as const : 'tool-holder' as const
      const key = `${collision.operationId}:${type}:${collision.kind}:${collision.fixtureId ?? ''}`
      if (diagnosticKeys.has(key)) continue
      diagnosticKeys.add(key)
      diagnostics.push({ operationId: collision.operationId, type, message: collision.message })
    }
    for (const target of operations.values()) {
      if (!target.active) { target.status = 'disabled'; continue }
      activeOperations += 1
      pathPoints += target.path.length
      if (target.path.length < 2) diagnostics.push({ operationId: target.id, type: 'missing-path', message: `${target.label} has no generated path.` })
      const cutter = tool(target.toolId)
      if (cutter.length < target.depth + setupSheet.safeHeightOffset) diagnostics.push({ operationId: target.id, type: 'tool-holder', message: `${target.label} exceeds the usable tool length.` })
      for (let index = 0; index < target.path.length; index += 1) {
        const entry = target.path[index]
        if (entry[0] < stock.min[0] || entry[0] > stock.max[0] || entry[1] < stock.min[1] || entry[1] > stock.max[1] || entry[2] < stock.min[2] || entry[2] > stock.max[2]) diagnostics.push({ operationId: target.id, type: 'stock', message: `${target.label} leaves the stock bounds.` })
        if (index > 0) {
          const segmentDistance = distance(target.path[index - 1], entry)
          cutDistance += segmentDistance
        }
      }
      target.status = diagnostics.some((diagnostic) => diagnostic.operationId === target.id) ? 'collision' : 'generated'
    }
    const collisions = [...new Set(diagnostics.map((diagnostic) => diagnostic.operationId))]
    const removal = materialRemovalEnabled ? materialRemovalTimeline() : null
    const materialRemoval: CamMaterialRemoval = {
      enabled: materialRemovalEnabled,
      stockVolume,
      removedVolume: removal?.removedVolume ?? 0,
      remainingVolume: removal?.remainingVolume ?? stockVolume,
      timeline: removal?.events.map((event) => ({ operationId: event.operationId, pathIndex: event.pathIndex, removedVolume: event.cumulativeRemovedVolume, remainingVolume: event.remainingVolume, startTimeMinutes: event.startTimeMinutes, endTimeMinutes: event.endTimeMinutes, distance: event.distance, feed: event.feed, sampleCount: event.sampleCount })) ?? [],
    }
    return { status: collisions.length ? 'collision' : 'pass', collisions, diagnostics, activeOperations, pathPoints, cutDistance, materialRemoval, collisionReport: collisionsByEnvelope }
  }
  const postprocessors = () => [...POSTS]
  const exportGcode = (postprocessor: CamPostprocessor = 'grbl') => {
    if (!POSTS.includes(postprocessor)) throw new RangeError(`CAM postprocessor is not allowed: ${postprocessor}`)
    const simulation = simulate()
    if (simulation.status !== 'pass') throw new RangeError(`CAM simulation has collisions: ${simulation.collisions.join(',')}`)
    const lines = [`( post:${postprocessor} )`, `( job:${label} )`, 'G90', 'G17', 'G21', 'G40', 'G49', 'G80']
    for (const target of operations.values()) {
      if (!target.active) continue
      const activeController = controller(target.controllerId)
      const first = target.path[0]
      const clearanceZ = stock.max[2] + setupSheet.clearanceHeightOffset
      const safeZ = stock.max[2] + setupSheet.safeHeightOffset
      lines.push(`( ${target.kind} ${target.id} )`, `T${activeController.toolNumber} M6`)
      if (activeController.spindleDirection !== 'none' && activeController.spindleSpeed > 0) lines.push(`S${activeController.spindleSpeed.toFixed(0)} ${activeController.spindleDirection === 'reverse' ? 'M4' : 'M3'}`)
      if (target.coolantMode === 'flood') lines.push('M8')
      else if (target.coolantMode === 'mist') lines.push('M7')
      lines.push(`G0 Z${clearanceZ.toFixed(3)}`, `G0 X${first[0].toFixed(3)} Y${first[1].toFixed(3)}`, `G0 Z${safeZ.toFixed(3)}`)
      for (const entry of target.path) lines.push(`G1 X${entry[0].toFixed(3)} Y${entry[1].toFixed(3)} Z${entry[2].toFixed(3)} F${target.feed.toFixed(1)}`)
      lines.push(`G0 Z${clearanceZ.toFixed(3)}`, 'M9', 'M5')
    }
    lines.push('M30')
    return `${lines.join('\n')}\n`
  }
  const exportMultiAxisGcode = (operationId: string, postprocessor: CamPostprocessor, options: CamKinematicOptions) => {
    if (!MULTI_AXIS_POSTS.includes(postprocessor)) throw new RangeError(`CAM postprocessor does not support controlled multi-axis output: ${postprocessor}`)
    if (options.configuration === '3-axis') throw new RangeError('CAM multi-axis export requires a 4-axis or 5-axis configuration.')
    const simulation = simulate()
    if (simulation.status !== 'pass') throw new RangeError(`CAM simulation has collisions: ${simulation.collisions.join(',')}`)
    const target = operation(operationId)
    const linuxcncXyzbc = postprocessor === 'linuxcnc'
    if (linuxcncXyzbc && options.configuration === '5-axis') {
      const axes = new Set([options.rotary?.axis, options.tilt?.axis])
      if (axes.size !== 2 || !axes.has('B') || !axes.has('C')) throw new RangeError('LinuxCNC XYZBC RTCP requires distinct B and C rotary axes.')
    }
    if (linuxcncXyzbc && options.configuration === '4-axis' && options.rotary?.axis === 'A') throw new RangeError('LinuxCNC XYZBC RTCP does not provide an A rotary axis.')
    const result = kinematics(operationId, linuxcncXyzbc ? { ...options, limits: { ...options.limits, B: [-35, 110], C: [-36000, 36000] } } : options)
    if (result.status !== 'pass') throw new RangeError(`CAM kinematics exceeds machine limits at ${result.violations.length} pose(s).`)
    const activeController = controller(target.controllerId)
    if (linuxcncXyzbc) {
      const firstPose = result.poses[0]
      const firstPoint = target.path[0]
      const lines = [
        '%',
        '( post:linuxcnc xyzbc-rtcp )',
        `( job:${label} operation:${target.id} )`,
        '( CAD -> OCL -> CAMotics -> G-code -> LinuxCNC WASM )',
        'G21 G17 G40 G49 G80 G90 G94',
        'M429',
        `T${activeController.toolNumber} M6`,
      ]
      if (activeController.spindleDirection !== 'none' && activeController.spindleSpeed > 0) lines.push(`S${activeController.spindleSpeed.toFixed(0)} ${activeController.spindleDirection === 'reverse' ? 'M4' : 'M3'}`)
      if (target.coolantMode === 'flood') lines.push('M8')
      else if (target.coolantMode === 'mist') lines.push('M7')
      lines.push('M428')
      lines.push(`G0 X${firstPoint[0].toFixed(3)} Y${firstPoint[1].toFixed(3)} Z${firstPoint[2].toFixed(3)} B${firstPose.b.toFixed(3)} C${firstPose.c.toFixed(3)}`)
      lines.push('G93')
      for (let index = 1; index < result.poses.length; index += 1) {
        const pose = result.poses[index]
        const previous = target.path[index - 1]
        const point = target.path[index]
        const segmentLength = Math.hypot(point[0] - previous[0], point[1] - previous[1], point[2] - previous[2])
        const inverseTimeFeed = segmentLength > 1e-9 ? target.feed / segmentLength : Math.max(1, target.feed)
        lines.push(`G1 X${point[0].toFixed(3)} Y${point[1].toFixed(3)} Z${point[2].toFixed(3)} B${pose.b.toFixed(3)} C${pose.c.toFixed(3)} F${inverseTimeFeed.toFixed(5)}`)
      }
      lines.push('G94', 'M429', 'M9', 'M5', 'M2', '%')
      return `${lines.join('\n')}\n`
    }
    const lines = [`( post:${postprocessor} multi-axis )`, `( job:${label} operation:${target.id} )`, 'G90', 'G17', 'G21', `T${activeController.toolNumber} M6`]
    if (activeController.spindleDirection !== 'none' && activeController.spindleSpeed > 0) lines.push(`S${activeController.spindleSpeed.toFixed(0)} ${activeController.spindleDirection === 'reverse' ? 'M4' : 'M3'}`)
    for (const pose of result.poses) lines.push(`G1 X${pose.position[0].toFixed(3)} Y${pose.position[1].toFixed(3)} Z${pose.position[2].toFixed(3)} A${pose.a.toFixed(3)} B${pose.b.toFixed(3)} C${pose.c.toFixed(3)} F${target.feed.toFixed(1)}`)
    lines.push('M5', 'M30')
    return `${lines.join('\n')}\n`
  }

  return {
    snapshot,
    subscribe: (listener) => { listeners.add(listener); return () => listeners.delete(listener) },
    exportJob,
    importJob,
    canUndo,
    canRedo,
    undo,
    redo,
    updateStock,
    updateSetupSheet,
    addTool,
    updateTool,
    removeTool,
    exportToolBit,
    importToolBit,
    addToolController,
    updateToolController,
    removeToolController,
    updateCollisionFixtures,
    setMaterialRemovalEnabled,
    addComment,
    setPropertyBagValue,
    createCompound,
    addOperation,
    generatePath,
    generateRectangularPath: generatePath,
    replaceGeneratedPath,
    applyDressup,
    toggleOperation,
    copyOperation,
    setStartPoint,
    removeOperation,
    sanityCheck,
    materialRemovalTimeline,
    collisionReport,
    kinematics,
    simulate,
    postprocessors,
    exportGcode,
    exportMultiAxisGcode,
  }
}

export const camOperationId = (kind: CamOperationKind, sequence: number) => `${slug(kind)}-${sequence}`
