import { createHash } from 'node:crypto'
import { readFile, stat } from 'node:fs/promises'
import { resolve } from 'node:path'
import { isDeepStrictEqual } from 'node:util'

const root = resolve(new URL('..', import.meta.url).pathname)
const report = JSON.parse(await readFile(resolve(root, 'config/freecad-property-area-roundtrip.json'), 'utf8'))
const fail = (message) => { throw new Error(`FreeCAD PropertyArea roundtrip check failed: ${message}`) }
const same = isDeepStrictEqual

if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-area-roundtrip' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d') fail('baseline is invalid')
for (const phase of ['nativeInitial', 'nativeReopened', 'nativeResaved']) {
  const snapshot = phase === 'nativeInitial' ? report.nativeInitial : phase === 'nativeReopened' ? report.nativeAfter?.reopened : report.nativeAfter?.resaved
  if (!same(snapshot?.objectSet, [{ name: 'AreaBox', typeId: 'Part::Box' }, { name: 'AreaProbe', typeId: 'Measure::MeasureArea' }]) || snapshot.source?.valid !== true || snapshot.source.shapeType !== 'Solid' || snapshot.source.solids !== 1 || snapshot.source.faces !== 6 || snapshot.source.edges !== 12 || snapshot.source.vertices !== 8 || snapshot.source.volume !== 100 || snapshot.source.area !== 160 || !same(snapshot.source.bounds, [0, 0, 0, 10, 5, 2])) fail(`${phase} source geometry is invalid`)
  if (snapshot.measure?.typeId !== 'Measure::MeasureArea' || snapshot.measure.areaTypeId !== 'App::PropertyArea' || snapshot.measure.elementsTypeId !== 'App::PropertyLinkSubList' || !same(snapshot.measure.propertyStatus, ['24', '27']) || !same(snapshot.measure.editorMode, ['ReadOnly']) || snapshot.measure.shape?.applicable !== false || !same(snapshot.measure.state, ['Up-to-date']) || snapshot.measure.statusString !== 'Valid') fail(`${phase} native MeasureArea semantics are incomplete`)
}
if (report.nativeInitial.measure.area.numeric !== 10 || !same(report.nativeInitial.measure.elements, [{ object: 'AreaBox', typeId: 'Part::Box', subElements: ['Face1'] }])) fail('native initial measurement changed')
for (const snapshot of [report.nativeAfter.reopened, report.nativeAfter.resaved]) if (snapshot.measure.area.numeric !== 20 || !same(snapshot.measure.elements, [{ object: 'AreaBox', typeId: 'Part::Box', subElements: ['Face1', 'Face2'] }])) fail('FreeCAD did not recompute and preserve the Web-edited measurement')
if (!same(report.nativeInitial.source, report.nativeAfter.reopened.source) || !same(report.nativeInitial.source, report.nativeAfter.resaved.source)) fail('source Shape changed during measurement round-trip')

if (report.web.before?.area?.typeId !== 'App::PropertyArea' || report.web.before.area.element !== 'Float' || Number(report.web.before.area.value) !== 10 || report.web.before.elements?.typeId !== 'App::PropertyLinkSubList' || report.web.before.elements.element !== 'LinkSubList' || report.web.before.elements.linkSubs?.length !== 1) fail('Web could not decode the native initial properties')
if (report.web.after?.area?.typeId !== 'App::PropertyArea' || report.web.after.area.element !== 'Float' || Number(report.web.after.area.value) !== 20 || report.web.after.elements?.typeId !== 'App::PropertyLinkSubList' || report.web.after.elements.element !== 'LinkSubList' || !same(report.web.after.elements.linkSubs, [{ objectId: 'AreaBox', subElement: 'Face1' }, { objectId: 'AreaBox', subElement: 'Face2' }])) fail('Web edit did not encode the target properties')
if (report.web.objectSetPreserved !== 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 and resources')
if (report.classification?.webArea !== 20 || report.classification.webElementCount !== 2 || report.classification.reopenedArea !== 20 || report.classification.resavedArea !== 20 || report.classification.nativeOutputMatches !== true || report.classification.sourceGeometryPreserved !== true || report.classification.unknownSemanticDrift !== false || report.classification.zeroUnknownDrift !== true) fail('round-trip classification is not exact for PropertyArea')
for (const archive of Object.values(report.archives ?? {})) {
  const path = resolve(root, archive.path ?? '')
  const content = await readFile(path)
  if ((await stat(path)).size !== archive.bytes || createHash('sha256').update(content).digest('hex') !== archive.sha256) fail(`archive is stale: ${archive.path}`)
}
console.log(JSON.stringify({ status: 'freecad-property-area-roundtrip-pass', values: { nativeInitial: report.nativeInitial.measure.area.numeric, webEdited: report.classification.webArea, nativeReopened: report.classification.reopenedArea, nativeResaved: report.classification.resavedArea }, elements: { initial: 1, edited: report.classification.webElementCount }, opaquePathsPreserved: report.web.opaquePathsPreserved, sourceGeometryPreserved: report.classification.sourceGeometryPreserved, zeroUnknownDrift: report.classification.zeroUnknownDrift }, null, 2))
