import test from 'node:test'
import assert from 'node:assert/strict'
import { createCamJob } from '../src/facade/cam'
import { CAM_PIPELINE_ORDER, camoticsSourceEvidence, prepareCamPipeline, submitCamPipelineToLinuxcnc } from '../src/facade/camPipeline'
import { OPEN_CAM_LIB_ARTIFACT_SHA256, OPEN_CAM_LIB_SOURCE_REVISION, type OpenCamLibDropCutterInput } from '../src/facade/camNativeSimulation'

const job = () => {
  const cam = createCamJob('pipeline', 'CAD/OCL/CAMotics/LinuxCNC')
  cam.addTool({ id: 'T1', name: 'end mill', diameter: 2, length: 20 })
  cam.addOperation({ id: 'profile', kind: 'profile', toolId: 'T1', depth: 1, feed: 100 })
  cam.generatePath('profile')
  return cam
}

const openCamLibRunner = async (input: OpenCamLibDropCutterInput) => ({
  backend: 'upstream-opencamlib-wasm' as const,
  sourceRevision: OPEN_CAM_LIB_SOURCE_REVISION,
  artifactSha256: OPEN_CAM_LIB_ARTIFACT_SHA256,
  algorithm: 'path-drop-cutter' as const,
  triangleCount: input.triangles.length,
  points: input.path.map((point) => [...point] as [number, number, number]),
})

test('pipeline evidence pins CAMotics source and canonical LinuxCNC policy', () => {
  assert.deepEqual(CAM_PIPELINE_ORDER, ['CAD', 'OCL', 'CAMotics', 'GCODE', 'LinuxCNC WASM'])
  const evidence = camoticsSourceEvidence()
  assert.equal(evidence.backend, 'camotics-source')
  assert.equal(evidence.generator, 'camotics-source-stage')
  assert.equal(evidence.workspaceNativeCli, 'verified-sidecar')
  assert.equal(evidence.browserNativeExecutable, false)
  assert.equal(evidence.parserAuthority, 'linuxcnc-wasm')
  assert.equal(evidence.camoticsParsesCanonicalGcode, false)
  assert.equal(evidence.sourceRevision.length, 40)
})

test('LinuxCNC WASM cannot be called before the OCL and G-code stages are prepared', async () => {
  const prepared = await prepareCamPipeline(job(), 'profile', { openCamLib: { runner: openCamLibRunner } })
  assert.equal(prepared.stage, 'GCODE')
  assert.equal(prepared.openCamLib.backend, 'upstream-opencamlib-wasm')
  assert.equal(prepared.openCamLib.inputPoints, 5)
  assert.equal(prepared.openCamLib.outputPoints, 5)
  const submitted = await submitCamPipelineToLinuxcnc(prepared, {
    submit: async (program) => ({ backend: 'linuxcnc-wasm', status: 'dry-run', programSha256: String(program.length) }),
  })
  assert.equal(submitted.stage, 'LinuxCNC WASM')
  assert.equal(submitted.linuxcnc?.status, 'dry-run')
  assert.match(submitted.gcode, /G90/)
  assert.match(submitted.camoticsMotionGcode, /G1 X/)
})

test('pipeline rejects non-LinuxCNC posts before any machine submission', async () => {
  await assert.rejects(() => prepareCamPipeline(job(), 'profile', { postprocessor: 'grbl', openCamLib: { runner: openCamLibRunner } }), /requires the linuxcnc postprocessor/)
})

test('pipeline rejects an unpinned OCL result instead of bypassing the native stage', async () => {
  await assert.rejects(() => prepareCamPipeline(job(), 'profile', { openCamLib: { runner: async (input) => ({ ...await openCamLibRunner(input), sourceRevision: '0'.repeat(40) }) } }), /pinned upstream WASM identity/)
})
