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-file-roundtrip.json'), 'utf8'))
const fail = (message) => { throw new Error(`FreeCAD PropertyFile roundtrip check failed: ${message}`) }
const initial = '.cache/freecad/FreeCAD/data/tests/mesh.obj'
const target = '.cache/freecad/property-file-roundtrip/alternate.obj'
if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-file-roundtrip' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyFile') fail('baseline is invalid')
const objectSet = [{ name: 'FileProbe', typeId: 'Mesh::Import' }]
for (const [phase, snapshot] of [['nativeInitial', report.nativeInitial], ['nativeReopened', report.nativeAfter?.reopened], ['nativeResaved', report.nativeAfter?.resaved]]) {
  if (snapshot?.object?.name !== 'FileProbe' || snapshot.object.typeId !== 'Mesh::Import' || snapshot.object.propertyTypeId !== 'App::PropertyFile' || !isDeepStrictEqual(snapshot.object.propertyStatus, []) || !isDeepStrictEqual(snapshot.object.editorMode, []) || !isDeepStrictEqual(snapshot.object.state, ['Up-to-date']) || snapshot.object.statusString !== 'Valid' || snapshot.object.hasShapeProperty !== false || snapshot.object.hasMeshProperty !== true || !isDeepStrictEqual(snapshot.objectSet, objectSet)) fail(`${phase} native object semantics are incomplete`)
  if (snapshot.object.asset?.exists !== true || !Number.isSafeInteger(snapshot.object.asset.bytes) || snapshot.object.asset.bytes <= 0 || !/^[0-9a-f]{64}$/.test(snapshot.object.asset.sha256) || snapshot.object.mesh?.isEmpty !== false || !Number.isSafeInteger(snapshot.object.mesh.facets) || snapshot.object.mesh.facets <= 0 || !Number.isSafeInteger(snapshot.object.mesh.points) || snapshot.object.mesh.points <= 0 || !Array.isArray(snapshot.object.mesh.bounds) || snapshot.object.mesh.bounds.length !== 6) fail(`${phase} native external asset or Mesh evidence is incomplete`)
}
if (report.nativeInitial.object.value !== initial || report.nativeAfter.reopened.object.value !== target || report.nativeAfter.resaved.object.value !== target) fail('native PropertyFile values drifted')
if (report.nativeInitial.object.asset.sha256 !== report.assets?.initial?.sha256 || report.nativeAfter.reopened.object.asset.sha256 !== report.assets?.target?.sha256 || report.nativeAfter.resaved.object.asset.sha256 !== report.assets?.target?.sha256) fail('native object did not consume the locked external assets')
if (isDeepStrictEqual(report.nativeInitial.object.mesh, report.nativeAfter.reopened.object.mesh) || !isDeepStrictEqual(report.nativeAfter.reopened.object.mesh, report.nativeAfter.resaved.object.mesh)) fail('native Mesh did not change and stabilize with the PropertyFile reference')
if (report.web.before?.typeId !== 'App::PropertyFile' || report.web.before?.element !== 'String' || report.web.before?.value !== initial || report.web.after?.typeId !== 'App::PropertyFile' || report.web.after?.element !== 'String' || report.web.after?.value !== target || report.web.targetMarkedTouched !== true || report.web.webOutputMatches !== true || report.web.semanticObjectsPreserved !== true || report.web.opaqueEntriesPreserved !== true || !Number.isSafeInteger(report.web.opaquePathsPreserved) || report.web.opaquePathsPreserved < 1) fail('Web edit did not preserve native archive semantics')
if (report.classification?.requestedValue !== target || report.classification.webValue !== target || report.classification.reopenedValue !== target || report.classification.resavedValue !== target || report.classification.nativeOutputMatches !== true || report.classification.objectSetPreserved !== true || report.classification.nativeMeshChanged !== true || report.classification.nativeMeshStable !== true || report.classification.externalResourceBoundary !== 'project-relative-path; asset bytes remain external to FCStd' || report.classification.unknownSemanticDrift !== false || report.classification.zeroUnknownDrift !== true) fail('round-trip classification is not exact for PropertyFile')
for (const artifact of [...Object.values(report.assets ?? {}), ...Object.values(report.archives ?? {})]) if (!artifact?.path || !Number.isSafeInteger(artifact.bytes) || artifact.bytes <= 0 || !/^[0-9a-f]{64}$/.test(artifact.sha256)) fail('asset or archive evidence is incomplete')
console.log(JSON.stringify({ status: 'freecad-property-file-roundtrip-pass', values: { nativeInitial: report.nativeInitial.object.value, webEdited: report.classification.webValue, nativeReopened: report.classification.reopenedValue, nativeResaved: report.classification.resavedValue }, mesh: { initial: report.nativeInitial.object.mesh, reopened: report.nativeAfter.reopened.object.mesh }, opaquePathsPreserved: report.web.opaquePathsPreserved, externalResourceBoundary: report.classification.externalResourceBoundary, zeroUnknownDrift: report.classification.zeroUnknownDrift }, null, 2))
