import { isDeepStrictEqual } from 'node:util'
import { readFile } from 'node:fs/promises'
import { resolve } from 'node:path'

const root = resolve(new URL('..', import.meta.url).pathname)
const report = JSON.parse(await readFile(resolve(root, 'config/freecad-property-boollist-mutation.json'), 'utf8'))
const fail = (message) => { throw new Error(`FreeCAD PropertyBoolList mutation check failed: ${message}`) }
if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-boollist-mutation' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyBoolList') fail('baseline is invalid')
if (report.caseCount !== 7 || report.cases?.length !== 7 || new Set(report.cases.map(({ objectTypeId }) => objectTypeId)).size !== 7) fail('mutation case inventory is incomplete')
const byType = new Map(report.cases.map((entry) => [entry.objectTypeId, entry]))
for (const entry of report.cases) {
  if (entry.valueRestored !== true || entry.shapeRestored !== true || entry.objectsRestored !== true || entry.editRecompute !== true || entry.restoreRecompute !== true || !isDeepStrictEqual(entry.before.objectSet, entry.restored.objectSet)) fail(`${entry.objectTypeId} did not restore value, shape and objects`)
  for (const snapshot of [entry.before, entry.touchedAfterEdit, entry.edited, entry.touchedAfterRestore, entry.restored]) if (snapshot.propertyTypeId !== 'App::PropertyBoolList') fail(`${entry.objectTypeId} property TypeId changed`)
}

for (const typeId of ['App::FeatureTest', 'App::FeatureTestException']) {
  const entry = byType.get(typeId)
  if (entry?.mode !== 'direct-native-property-setter' || entry.propertyName !== 'BoolList' || !isDeepStrictEqual(entry.before.value, [false]) || !isDeepStrictEqual(entry.touchedAfterEdit.value, [true, false, true]) || !isDeepStrictEqual(entry.edited.value, [true, false, true]) || !isDeepStrictEqual(entry.touchedAfterRestore.value, [false]) || !isDeepStrictEqual(entry.restored.value, [false])) fail(`${typeId} mutation values changed`)
  for (const snapshot of [entry.before, entry.touchedAfterEdit, entry.edited, entry.touchedAfterRestore, entry.restored]) if (!isDeepStrictEqual(snapshot.propertyStatus, []) || !isDeepStrictEqual(snapshot.editorMode, []) || snapshot.shape?.applicable !== false || snapshot.mustExecute !== false || snapshot.objectSet?.length !== 1) fail(`${typeId} mutation metadata changed`)
}
const feature = byType.get('App::FeatureTest')
if (feature.hostExecution !== 'normal' || !isDeepStrictEqual(feature.before.objectState, ['Up-to-date']) || !isDeepStrictEqual(feature.touchedAfterEdit.objectState, ['Touched']) || !isDeepStrictEqual(feature.edited.objectState, ['Up-to-date']) || !isDeepStrictEqual(feature.touchedAfterRestore.objectState, ['Touched']) || !isDeepStrictEqual(feature.restored.objectState, ['Up-to-date']) || feature.before.statusString !== 'Valid' || feature.edited.statusString !== 'Valid' || feature.restored.statusString !== 'Valid') fail('FeatureTest state trajectory changed')
const exception = byType.get('App::FeatureTestException')
if (exception.hostExecution !== 'intrinsic-test-exception' || !isDeepStrictEqual(exception.before.objectState, ['Up-to-date']) || !isDeepStrictEqual(exception.touchedAfterEdit.objectState, ['Touched']) || !isDeepStrictEqual(exception.edited.objectState, ['Touched', 'Invalid']) || !isDeepStrictEqual(exception.touchedAfterRestore.objectState, ['Touched', 'Invalid']) || !isDeepStrictEqual(exception.restored.objectState, ['Touched', 'Invalid']) || exception.edited.statusString !== 'FeatureTestException::execute(): Testexception  ;-)' || exception.restored.statusString !== exception.edited.statusString) fail('FeatureTestException intrinsic trajectory changed')

const surface = byType.get('Surface::GeomFillSurface')
if (surface?.mode !== 'direct-native-property-setter' || surface.propertyName !== 'ReversedList' || !isDeepStrictEqual(surface.before.value, [false]) || !isDeepStrictEqual(surface.edited.value, [true, false, false, false]) || !isDeepStrictEqual(surface.restored.value, [false])) fail('Surface mutation values changed')
for (const snapshot of [surface.before, surface.touchedAfterEdit, surface.edited, surface.touchedAfterRestore, surface.restored]) {
  const shape = snapshot.shape
  if (!isDeepStrictEqual(snapshot.propertyStatus, []) || !isDeepStrictEqual(snapshot.editorMode, []) || shape?.applicable !== true || shape.isNull !== false || shape.valid !== true || shape.shapeType !== 'Face' || shape.faces !== 1 || shape.edges !== 4 || shape.vertices !== 4 || shape.area !== 100 || shape.volume !== 0 || !isDeepStrictEqual(shape.bounds, [0, 0, 0, 10, 10, 0]) || snapshot.objectSet?.length !== 5) fail('Surface geometry evidence changed')
}
if (surface.before.shape.brepSha256 === surface.edited.shape.brepSha256 || surface.before.shape.brepSha256 !== surface.restored.shape.brepSha256 || !isDeepStrictEqual(surface.before.objectState, ['Up-to-date']) || !isDeepStrictEqual(surface.touchedAfterEdit.objectState, ['Touched']) || surface.touchedAfterEdit.mustExecute !== true || !isDeepStrictEqual(surface.edited.objectState, ['Up-to-date']) || !isDeepStrictEqual(surface.touchedAfterRestore.objectState, ['Touched']) || surface.touchedAfterRestore.mustExecute !== true || !isDeepStrictEqual(surface.restored.objectState, ['Up-to-date'])) fail('Surface reversal did not mutate and restore exactly')

for (const typeId of ['App::Link', 'App::LinkGroup', 'App::LinkGroupPython', 'App::LinkPython']) {
  const entry = byType.get(typeId)
  const pythonLink = typeId.endsWith('Python')
  const expectedSetup = typeId === 'App::Link' || typeId === 'App::LinkPython' ? 'LinkedObject' : 'ElementList'
  if (entry?.mode !== 'link-base-extension-element-visibility' || entry.propertyName !== 'VisibilityList' || entry.setupProperty !== expectedSetup || entry.hideResult !== 1 || entry.showResult !== 1 || !isDeepStrictEqual(entry.before.value, [true, true]) || !isDeepStrictEqual(entry.touchedAfterEdit.value, [false, true]) || !isDeepStrictEqual(entry.edited.value, [false, true]) || !isDeepStrictEqual(entry.touchedAfterRestore.value, [true, true]) || !isDeepStrictEqual(entry.restored.value, [true, true])) fail(`${typeId} mutation values changed`)
  for (const snapshot of [entry.before, entry.touchedAfterEdit, entry.edited, entry.touchedAfterRestore, entry.restored]) if (!isDeepStrictEqual(snapshot.propertyStatus, ['Immutable', 'Hidden', 'LockDynamic']) || !isDeepStrictEqual(snapshot.editorMode, ['Hidden']) || snapshot.shape?.applicable !== false || snapshot.objectSet?.length !== 4) fail(`${typeId} mutation metadata changed`)
  if (!isDeepStrictEqual(entry.before.objectState, ['Up-to-date']) || !isDeepStrictEqual(entry.touchedAfterEdit.objectState, ['Touched']) || entry.touchedAfterEdit.mustExecute !== pythonLink || !isDeepStrictEqual(entry.edited.objectState, ['Up-to-date']) || !isDeepStrictEqual(entry.touchedAfterRestore.objectState, ['Touched']) || entry.touchedAfterRestore.mustExecute !== pythonLink || !isDeepStrictEqual(entry.restored.objectState, ['Up-to-date']) || entry.before.statusString !== 'Valid' || entry.edited.statusString !== 'Valid' || entry.restored.statusString !== 'Valid') fail(`${typeId} state trajectory changed`)
}
console.log(JSON.stringify({ status: 'freecad-property-boollist-mutation-pass', cases: report.caseCount, surface: { beforeBrep: surface.before.shape.brepSha256, editedBrep: surface.edited.shape.brepSha256, restoredBrep: surface.restored.shape.brepSha256, restoredExactly: surface.shapeRestored }, linkVisibilityRestored: ['App::Link', 'App::LinkGroup', 'App::LinkGroupPython', 'App::LinkPython'].every((typeId) => byType.get(typeId).valueRestored), intrinsicHostDiagnostic: exception.hostExecution }, null, 2))
