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

const root = resolve(new URL('..', import.meta.url).pathname)
const oracle = JSON.parse(await readFile(resolve(root, 'config/freecad-xlink-relink-oracle.json'), 'utf8'))
const fail = (message) => { throw new Error(`FreeCAD XLink/relink oracle: ${message}`) }
const same = (actual, expected) => JSON.stringify(actual) === JSON.stringify(expected)
const near = (actual, expected) => typeof actual === 'number' && Math.abs(actual - expected) <= oracle.tolerance
const expectedTypes = ['App::PropertyXLink', 'App::PropertyXLinkSub', 'App::PropertyXLinkList', 'App::PropertyXLinkSubList']
const expectedTargets = {
  xLink: { document: 'XLinkSource', object: 'SourceBox' },
  xLinkSub: { document: 'XLinkSource', object: 'SourceBox', subElements: ['Face1'] },
  xLinkList: [{ document: 'XLinkSource', object: 'SourceBox' }, { document: 'XLinkSource', object: 'SecondBox' }],
  xLinkSubList: [{ document: 'XLinkSource', object: 'SourceBox', subElements: ['Face1'] }, { document: 'XLinkSource', object: 'SecondBox', subElements: ['Face1', 'Face2'] }],
}
const linked = (holder) => Object.entries(expectedTargets).every(([name, expected]) => same(holder?.[name], expected))
const validHolder = (holder) => linked(holder) && same(holder.state, ['Up-to-date']) && holder.status === 'Valid' && Object.values(holder.propertyStatus).every((status) => same(status, ['21']))
const validBinder = (binder, area) => same(binder?.support, [{ document: 'XLinkSource', object: 'SourceBox', subElements: ['Face1'] }]) && near(binder.area, area) && binder.shapeNull === false && same(binder.state, ['Up-to-date']) && binder.status === 'Valid'
const sameDimensions = (actual, expected) => Object.entries(expected).every(([name, value]) => Array.isArray(value) ? same(actual?.[name], value) : near(actual?.[name], value))

if (oracle.schemaVersion !== 1 || oracle.baselineId !== 'freecad-1.1.1-xlink-relink-oracle' || oracle.freecadVersion !== '1.1.1' || oracle.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || oracle.status !== 'pass' || oracle.tolerance !== 1e-7 || !same(oracle.propertyTypes, expectedTypes)) fail('baseline is invalid.')
if (!validHolder(oracle.initial?.holder) || !sameDimensions(oracle.initial.dimensions, { xLinkLength: 4, xLinkSubWidth: 5, xLinkListHeights: [6, 2], xLinkSubListAreas: [30, 14, 14] }) || !validBinder(oracle.initial.binder, 30)) fail('initial four-property XLink graph is invalid.')
if (oracle.sourceClosed?.sourceOpen !== false || linked(oracle.sourceClosed.holder) || oracle.sourceClosed.binder?.area !== 30 || oracle.sourceClosed.binder.shapeNull !== false) fail('closing the source document did not expose the native unresolved/cached state.')
if (oracle.relinkAfterClose?.sourceOpen !== true || !validHolder(oracle.relinkAfterClose.holder) || !validBinder(oracle.relinkAfterClose.binder, 30)) fail('same-session source reopen did not relink all references.')
if (oracle.missingSource?.sourceOpen !== false || linked(oracle.missingSource.holder) || oracle.missingSource.binder?.area !== 30 || oracle.missingSource.binder.shapeNull !== false) fail('missing source file did not preserve the native unresolved/cached state.')
if (oracle.relinkAfterMissing?.sourceOpen !== true || !validHolder(oracle.relinkAfterMissing.holder) || !validBinder(oracle.relinkAfterMissing.binder, 30)) fail('restoring the missing source path did not relink all references.')
const editedDimensions = { xLinkLength: 8, xLinkSubWidth: 9, xLinkListHeights: [6, 4], xLinkSubListAreas: [54, 28, 28] }
if (!validHolder(oracle.edited?.holder) || !sameDimensions(oracle.edited.dimensions, editedDimensions) || !validBinder(oracle.edited.binder, oracle.edited.sourceFaceArea) || !near(oracle.edited.sourceFaceArea, 54)) fail('post-relink source edit did not propagate through all link kinds and the binder.')
if (!validHolder(oracle.finalRoundtrip?.holder) || !sameDimensions(oracle.finalRoundtrip.dimensions, editedDimensions) || !validBinder(oracle.finalRoundtrip.binder, oracle.finalRoundtrip.sourceFaceArea) || !near(oracle.finalRoundtrip.sourceFaceArea, 54)) fail('final source/consumer FCStd round-trip drifted.')

console.log(JSON.stringify({ status: 'freecad-xlink-relink-oracle-pass', baselineId: oracle.baselineId, propertyTypes: oracle.propertyTypes, sourceCloseRelink: true, missingFileRelink: true, editPropagation: true, fcstdRoundtrip: true }, null, 2))
