import { readFile } from 'node:fs/promises'
import {
  FREECAD_PRIVATE_NAMING_ABI_VERSION,
  FREECAD_SKETCHER_CONSTRAINT_TYPES,
  FREECAD_SKETCHER_GEOMETRY_TYPES,
  FREECAD_SKETCHER_INTERNAL_ALIGNMENT_TYPES,
  PARTDESIGN_PARAMETER_SPACE,
  createFacadeRuntimeProfile,
  partDesignParameterSpaceCoverage,
} from '../src/facade/index.ts'

const report = JSON.parse(await readFile(new URL('../config/freecad-sketcher-partdesign-abi-contract.json', import.meta.url), 'utf8'))
const failures = []
const check = (condition, message) => { if (!condition) failures.push(message) }
const same = (actual, expected) => JSON.stringify(actual) === JSON.stringify(expected)

check(report.schemaVersion === 1, 'contract schemaVersion must be 1')
check(report.baseline?.version === '1.1.1', 'contract baseline must lock FreeCAD 1.1.1')
check(report.baseline?.sourceCommit === '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d', 'contract baseline source commit changed')
check(report.claim?.scope === 'supported-web-facade' && report.claim?.exactFreeCadParity === false, 'contract must remain scoped and fail-closed for system exact parity')
check(same(FREECAD_SKETCHER_GEOMETRY_TYPES, report.sketcher?.geometryTypes), 'Sketcher geometry type report is stale')
check(same(FREECAD_SKETCHER_CONSTRAINT_TYPES, report.sketcher?.constraintTypes), 'Sketcher constraint type report is stale')
check(same(FREECAD_SKETCHER_INTERNAL_ALIGNMENT_TYPES, report.sketcher?.internalAlignmentTypes), 'Sketcher internal alignment report is stale')

const coverage = partDesignParameterSpaceCoverage()
check(coverage.duplicateTypeIds === 0, 'PartDesign parameter space has duplicate TypeIds')
check(coverage.familiesWithoutPartitions.length === 0, 'every PartDesign family must have at least three semantic partitions')
check(coverage.families === report.partDesign?.families, 'PartDesign family count report is stale')
check(coverage.properties === report.partDesign?.propertySlots, 'PartDesign property-slot count report is stale')
check(coverage.partitions === report.partDesign?.semanticPartitions, 'PartDesign semantic-partition count report is stale')
check(same(PARTDESIGN_PARAMETER_SPACE.map((family) => family.typeId), report.partDesign?.typeIds), 'PartDesign TypeId report is stale')
for (const family of PARTDESIGN_PARAMETER_SPACE) {
  check(new Set(family.properties).size === family.properties.length, `${family.typeId} has duplicate property declarations`)
  check(new Set(family.partitions.map((partition) => partition.id)).size === family.partitions.length, `${family.typeId} has duplicate partition IDs`)
  for (const partition of family.partitions) for (const propertyName of Object.keys(partition.values)) check(family.properties.includes(propertyName), `${family.typeId}.${partition.id} uses undeclared property ${propertyName}`)
}

check(FREECAD_PRIVATE_NAMING_ABI_VERSION === report.privateNamingAbi?.abiVersion, 'FreeCAD private naming ABI version report is stale')
check(createFacadeRuntimeProfile('production').naming.nativeAbi === report.privateNamingAbi?.runtimeProfile, 'production runtime profile does not advertise the optional private ABI')
check(createFacadeRuntimeProfile('mock').naming.nativeAbi === 'not-exposed', 'mock runtime must not advertise the private naming ABI')
check(report.privateNamingAbi?.shippedWorkerImplementation === 'freecad-linked', 'the production contract must identify the verified FreeCAD-linked Worker')
check(report.privateNamingAbi?.syntheticTokenGeneration === 'forbidden', 'synthetic FreeCAD token generation must remain forbidden')

if (failures.length) {
  console.error('FreeCAD Sketcher/PartDesign/private-ABI contract failed:')
  failures.forEach((failure) => console.error(`- ${failure}`))
  process.exit(1)
}

console.log(`freecad-sketcher-partdesign-abi-contract-pass geometry=${FREECAD_SKETCHER_GEOMETRY_TYPES.length} constraints=${FREECAD_SKETCHER_CONSTRAINT_TYPES.length} alignments=${FREECAD_SKETCHER_INTERNAL_ALIGNMENT_TYPES.length} families=${coverage.families} properties=${coverage.properties} partitions=${coverage.partitions} nativeWorker=${report.privateNamingAbi.shippedWorkerImplementation}`)
