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-success.json'), 'utf8'))
const fail = (message) => { throw new Error(`FreeCAD PropertyBoolList success check failed: ${message}`) }
if (report.schemaVersion !== 1 || report.status !== 'pass' || report.baselineId !== 'freecad-1.1.1-property-boollist-success' || report.freecadVersion !== '1.1.1' || report.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || report.propertyType !== 'App::PropertyBoolList') fail('baseline is invalid')
if (report.caseCount !== 7 || !Array.isArray(report.cases) || report.cases.length !== 7 || new Set(report.cases.map(({ objectTypeId }) => objectTypeId)).size !== 7) fail('native success case inventory is incomplete')
const byType = new Map(report.cases.map((entry) => [entry.objectTypeId, entry]))
const directTypes = ['App::FeatureTest', 'App::FeatureTestException']
for (const typeId of directTypes) {
  const entry = byType.get(typeId)
  if (entry?.mode !== 'direct-native-property-setter' || entry.propertyName !== 'BoolList' || entry.propertyAssignmentsAccepted !== true || !isDeepStrictEqual(entry.defaultValue, [false])) fail(`${typeId} setter contract is invalid`)
  const expected = { default: [false], empty: [], singleton: [true], mixed: [true, false, true, false], restored: [false] }
  for (const [phase, value] of Object.entries(expected)) if (!isDeepStrictEqual(entry.phases?.[phase]?.value, value)) fail(`${typeId} ${phase} value changed`)
  const large = entry.phases?.large
  if (large?.value?.length !== 257 || !large.value.every((value, index) => value === (index % 3 === 0))) fail(`${typeId} large boundary changed`)
  for (const snapshot of Object.values(entry.phases)) if (snapshot.propertyTypeId !== 'App::PropertyBoolList' || !isDeepStrictEqual(snapshot.propertyStatus, []) || !isDeepStrictEqual(snapshot.editorMode, []) || snapshot.shape?.applicable !== false || snapshot.objectSet?.length !== 1 || snapshot.objectSet[0].typeId !== typeId) fail(`${typeId} native metadata is invalid`)
  if (typeId === 'App::FeatureTestException') {
    if (entry.hostExecution !== 'intrinsic-test-exception' || !Object.values(entry.phases).slice(1).every((snapshot) => isDeepStrictEqual(snapshot.objectState, ['Touched', 'Invalid']) && snapshot.statusString === 'FeatureTestException::execute(): Testexception  ;-)')) fail('FeatureTestException intrinsic host diagnostic changed')
  } else if (entry.hostExecution !== 'normal' || !Object.values(entry.phases).every((snapshot) => isDeepStrictEqual(snapshot.objectState, ['Up-to-date']) && snapshot.statusString === 'Valid')) fail('FeatureTest host state changed')
}

const surface = byType.get('Surface::GeomFillSurface')
if (surface?.mode !== 'direct-native-property-setter' || surface.propertyName !== 'ReversedList' || surface.propertyAssignmentsAccepted !== true || surface.hostExecution !== 'normal') fail('Surface BoolList contract is invalid')
const surfaceValues = { default: [false], allFalse: [false, false, false, false], oneReversed: [true, false, false, false], alternating: [true, false, true, false], allReversed: [true, true, true, true], restored: [false] }
for (const [phase, value] of Object.entries(surfaceValues)) {
  const snapshot = surface.phases?.[phase]
  if (!isDeepStrictEqual(snapshot?.value, value) || snapshot.propertyTypeId !== 'App::PropertyBoolList' || !isDeepStrictEqual(snapshot.propertyStatus, []) || !isDeepStrictEqual(snapshot.editorMode, []) || !isDeepStrictEqual(snapshot.objectState, ['Up-to-date']) || snapshot.statusString !== 'Valid' || snapshot.shape?.applicable !== true || snapshot.shape.isNull !== false || snapshot.shape.valid !== true || snapshot.shape.shapeType !== 'Face' || snapshot.shape.faces !== 1 || snapshot.shape.edges !== 4 || snapshot.shape.vertices !== 4 || snapshot.objectSet?.length !== 5) fail(`Surface ${phase} success evidence changed`)
}

const linkTypes = ['App::Link', 'App::LinkGroup', 'App::LinkGroupPython', 'App::LinkPython']
for (const typeId of linkTypes) {
  const entry = byType.get(typeId)
  const expectedSetup = typeId === 'App::Link' || typeId === 'App::LinkPython' ? 'LinkedObject' : 'ElementList'
  if (entry?.mode !== 'link-base-extension-element-visibility' || entry.propertyName !== 'VisibilityList' || entry.propertyAssignmentsAccepted !== true || entry.hostExecution !== 'normal' || entry.setupProperty !== expectedSetup || entry.hideResult !== 1 || entry.showResult !== 1) fail(`${typeId} LinkBaseExtension contract changed`)
  if (!isDeepStrictEqual(entry.phases?.linked?.value, [true, true]) || !isDeepStrictEqual(entry.phases?.hidden?.value, [false, true]) || !isDeepStrictEqual(entry.phases?.restored?.value, [true, true])) fail(`${typeId} visibility values changed`)
  for (const snapshot of Object.values(entry.phases)) if (snapshot.propertyTypeId !== 'App::PropertyBoolList' || !isDeepStrictEqual(snapshot.propertyStatus, ['Immutable', 'Hidden', 'LockDynamic']) || !isDeepStrictEqual(snapshot.editorMode, ['Hidden']) || !isDeepStrictEqual(snapshot.objectState, ['Up-to-date']) || snapshot.statusString !== 'Valid' || snapshot.shape?.applicable !== false || snapshot.objectSet?.length !== 4 || snapshot.objectSet[3].typeId !== typeId) fail(`${typeId} native metadata is invalid`)
}
console.log(JSON.stringify({ status: 'freecad-property-boollist-success-pass', cases: report.caseCount, directBoundaries: { empty: 0, singleton: 1, mixed: 4, large: 257 }, surface: { valid: true, shapeType: 'Face', faces: 1 }, linkVisibility: { linked: [true, true], hidden: [false, true], restored: [true, true] }, intrinsicHostDiagnostic: byType.get('App::FeatureTestException').hostExecution }, null, 2))
