import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'

const report = JSON.parse(await readFile(new URL('../config/freecad-cam-path-oracle.json', import.meta.url), 'utf8'))

const commandGcode = (object) => object.commands.map((command) => command.gcode)
const objectByName = (objects, name) => objects.find((object) => object.name === name)

test('native Profile oracle is a locked FreeCAD algorithm result', () => {
  assert.equal(report.profileAlgorithm.operationTypeId, 'Path::FeaturePython')
  assert.equal(report.profileAlgorithm.pathPropertyType, 'Path::PropertyPath')
  assert.equal(report.profileAlgorithm.commandCount, 32)
  assert.equal(report.profileAlgorithm.cuttingCommandCount, 20)
  assert.equal(report.profileAlgorithm.normalizationToleranceMm, 0.01)
  assert.equal(report.profileAlgorithm.commandSha256, '5fcc36e3fc3c022599096f4110c890401f285b5878030de7c481786470a7a455')
  assert.deepEqual(report.profileAlgorithm.variants.map((variant) => ({ id: variant.id, useComp: variant.useComp, hash: variant.commandSha256 })), [
    { id: 'outside-cw-tool-comp', useComp: true, hash: '5fcc36e3fc3c022599096f4110c890401f285b5878030de7c481786470a7a455' },
    { id: 'outside-cw-no-comp', useComp: false, hash: 'c4c8bc58c4b21bc7be8796ff5d074fbe02656e31a26cc91ee65a67537a7d240a' },
  ])
})

test('native Helix oracle locks all four cut-direction combinations', () => {
  assert.equal(report.helixAlgorithm.fixture, 'Mod/CAM/CAMTests/test_holes00.fcstd')
  assert.equal(report.helixAlgorithm.baseSubElementCount, 9)
  assert.equal(report.helixAlgorithm.toolDiameterMm, 0.9)
  assert.deepEqual(report.helixAlgorithm.scenarios.map((scenario) => ({
    id: scenario.id,
    direction: scenario.direction,
    arcs: scenario.arcNames,
    arcCount: scenario.arcCommandCount,
    hash: scenario.commandSha256,
  })), [
    { id: 'inside-conventional', direction: 'CW', arcs: ['G2'], arcCount: 1260, hash: '6499583c26dbf01694abc0a98fdbeb9d240cce0f73d32634284ddfa132030e0b' },
    { id: 'outside-climb', direction: 'CW', arcs: ['G2'], arcCount: 1260, hash: '37ee85fe360bfa82235f1b7aefce31cb7150900bb8d6f08751741fce6df18626' },
    { id: 'inside-climb', direction: 'CCW', arcs: ['G3'], arcCount: 1260, hash: 'f2fd26ca11be5efb037f809fb65949d24956f6ce330f87d152d2674770a27bd8' },
    { id: 'outside-conventional', direction: 'CCW', arcs: ['G3'], arcCount: 1260, hash: '0220f90245a74a50c1a54cb501439d14e7370d9d9d34fcc89dccdaff345688b7' },
  ])
})

test('Path Feature and FeaturePython survive native save-open-mutate-save-open', () => {
  for (const name of ['NativePath', 'PythonPath']) {
    const before = objectByName(report.fcstdRoundTrip.sourceBeforeClose, name)
    const reopened = objectByName(report.fcstdRoundTrip.sourceReopened, name)
    const mutated = objectByName(report.fcstdRoundTrip.mutatedReopened, name)
    assert.deepEqual(commandGcode(reopened), commandGcode(before))
    assert.deepEqual(commandGcode(mutated).slice(0, 5), commandGcode(before))
    assert.equal(mutated.commandCount, 6)
    assert.equal(mutated.commands.at(-1).name, name === 'NativePath' ? 'M5' : 'M2')
  }
})

test('Web byte-transparent proxy reopens natively without changing Path commands', () => {
  assert.equal(report.fcstdRoundTrip.webTransparentPreservation.byteExact, true)
  assert.equal(report.fcstdRoundTrip.webTransparentPreservation.nativeReopenVerified, true)
  for (const name of ['NativePath', 'PythonPath']) {
    const native = objectByName(report.fcstdRoundTrip.sourceReopened, name)
    const webPreserved = objectByName(report.fcstdRoundTrip.webTransparentPreservation.objects, name)
    assert.deepEqual(commandGcode(webPreserved), commandGcode(native))
  }
  assert.equal(report.claims.webEditablePathPropertyCodec, 'verified-native-path-feature')
  assert.equal(report.boundary.exactParityClaim, false)
})

test('Web Path::PropertyPath edit is reopened by FreeCAD while FeaturePython stays blocked', () => {
  const editable = report.fcstdRoundTrip.webEditablePathProperty
  assert.equal(editable.objectName, 'NativePath')
  assert.equal(editable.sourceCommandCount, 5)
  assert.equal(editable.editedCommandCount, 6)
  assert.deepEqual(editable.lastCommand, { name: 'M3', parameters: { S: 12000 } })
  assert.equal(editable.nativeReopenVerified, true)
  assert.equal(editable.featurePythonExecutionBlocked, true)
})

test('FeaturePython Path::PropertyPath resource editing is explicit and non-executing', () => {
  const editable = report.fcstdRoundTrip.webEditableFeaturePythonPathProperty
  assert.equal(editable.objectName, 'PythonPath')
  assert.equal(editable.sourceCommandCount, 5)
  assert.equal(editable.editedCommandCount, 6)
  assert.deepEqual(editable.lastCommand, { name: 'M2', parameters: {} })
  assert.equal(editable.nativeReopenVerified, true)
  assert.equal(editable.scriptExecution, 'blocked')
  assert.equal(editable.optInRequired, true)
  assert.equal(report.claims.webEditableFeaturePythonPathResourceCodec, 'verified-safe-resource-only-opt-in')
})

test('Qt oracle records live CAM actions from the active workbench', () => {
  assert.equal(report.qtDynamicOracle.activeWorkbench, 'CAMWorkbench')
  assert.ok(report.qtDynamicOracle.camCommandCount >= 50)
  assert.ok(report.qtDynamicOracle.requiredCommands.every((command) => (
    command.selectedModel.registered
    && command.selectedModel.actionCount >= 1
    && command.selectedModel.text.length > 0
    && command.selectedModel.iconPresent
  )))
  assert.ok(report.qtDynamicOracle.requiredCommands.some((command) => command.selectionChangesEnabledState))
})

test('Qt oracle records CAM Task panel open, focus fields, and OK/Cancel close semantics', () => {
  const lifecycles = report.qtDynamicOracle.taskLifecycles
  assert.equal(report.qtDynamicOracle.taskLifecycleVerified, true)
  assert.ok(lifecycles.length >= 3)
  assert.ok(lifecycles.every((lifecycle) => lifecycle.dialogOpened && lifecycle.dialogClosed && lifecycle.buttonClicked))
  assert.ok(lifecycles.every((lifecycle) => lifecycle.active.buttonBoxCount === 1 && lifecycle.active.fieldCount >= 10))
  assert.ok(lifecycles.every((lifecycle) => lifecycle.active.buttonTexts.some((button) => button.text === 'OK') && lifecycle.active.buttonTexts.some((button) => button.text === 'Cancel')))
  assert.deepEqual(new Set(lifecycles.map((lifecycle) => lifecycle.action)), new Set(['accept', 'cancel']))
})
