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-attachment-mode-oracle.json'), 'utf8'))
const fail = (message) => { throw new Error(`FreeCAD attachment mode oracle: ${message}`) }
const near = (actual, expected) => typeof actual === 'number' && Math.abs(actual - expected) <= oracle.tolerance
const same = (actual, expected) => JSON.stringify(actual) === JSON.stringify(expected)

const modes = [
  'Deactivated', 'Translate', 'ObjectXY', 'ObjectXZ', 'ObjectYZ', 'FlatFace', 'TangentPlane', 'NormalToEdge',
  'FrenetNB', 'FrenetTN', 'FrenetTB', 'Concentric', 'SectionOfRevolution', 'ThreePointsPlane', 'ThreePointsNormal', 'Folding',
  'ObjectX', 'ObjectY', 'ObjectZ', 'AxisOfCurvature', 'Directrix1', 'Directrix2', 'Asymptote1', 'Asymptote2', 'Tangent', 'Normal',
  'Binormal', 'TangentU', 'TangentV', 'TwoPointLine', 'IntersectionLine', 'ProximityLine', 'ObjectOrigin', 'Focus1', 'Focus2',
  'OnEdge', 'CenterOfCurvature', 'CenterOfMass', 'IntersectionPoint', 'Vertex', 'ProximityPoint1', 'ProximityPoint2',
  'AxisOfInertia1', 'AxisOfInertia2', 'AxisOfInertia3', 'InertialCS', 'FaceNormal', 'OZX', 'OZY', 'OXY', 'OXZ', 'OYZ', 'OYX',
  'ParallelPlane', 'MidPoint',
]
const implemented = {
  plane: ['Translate', 'ObjectXY', 'ObjectXZ', 'ObjectYZ', 'FlatFace', 'TangentPlane', 'NormalToEdge', 'FrenetNB', 'FrenetTN', 'FrenetTB', 'Concentric', 'SectionOfRevolution', 'ThreePointsPlane', 'ThreePointsNormal', 'Folding', 'InertialCS', 'OZX', 'OZY', 'OXY', 'OXZ', 'OYZ', 'OYX', 'ParallelPlane'],
  line: ['ObjectX', 'ObjectY', 'ObjectZ', 'AxisOfCurvature', 'Directrix1', 'Directrix2', 'Asymptote1', 'Asymptote2', 'Tangent', 'Normal', 'Binormal', 'TwoPointLine', 'IntersectionLine', 'ProximityLine', 'AxisOfInertia1', 'AxisOfInertia2', 'AxisOfInertia3', 'FaceNormal'],
  point: ['ObjectOrigin', 'Focus1', 'Focus2', 'OnEdge', 'CenterOfCurvature', 'CenterOfMass', 'Vertex', 'ProximityPoint1', 'ProximityPoint2'],
}
implemented.sketch = implemented.plane
const engineTypes = { plane: 'Attacher::AttachEnginePlane', line: 'Attacher::AttachEngineLine', point: 'Attacher::AttachEnginePoint', sketch: 'Attacher::AttachEnginePlane' }
const objectTypes = { plane: 'PartDesign::Plane', line: 'PartDesign::Line', point: 'PartDesign::Point', sketch: 'Sketcher::SketchObject' }

if (oracle.schemaVersion !== 1 || oracle.baselineId !== 'freecad-1.1.1-attachment-mode-oracle' || oracle.freecadVersion !== '1.1.1' || oracle.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || oracle.status !== 'pass' || oracle.tolerance !== 1e-7) fail('baseline is invalid.')
if (oracle.registry?.modeCount !== 55 || !same(oracle.registry.modes, modes) || oracle.registry.implementedUnionCount !== 50 || !same(oracle.registry.unimplementedModes, ['Deactivated', 'TangentU', 'TangentV', 'IntersectionPoint', 'MidPoint'])) fail('persistent mode registry or implemented union changed.')

for (const name of Object.keys(engineTypes)) {
  const engine = oracle.engines?.[name]
  if (engine?.typeId !== objectTypes[name] || engine.attacherType !== engineTypes[name] || !same(engine.completeModeList, modes) || !same(engine.implementedModes, implemented[name])) fail(`${name} engine identity or implemented modes changed.`)
  for (const mode of implemented[name]) {
    const info = engine.modeInfo?.[mode]
    if (info?.modeIndex !== modes.indexOf(mode) || !Array.isArray(info.referenceCombinations) || info.referenceCombinations.length === 0) fail(`${name}.${mode} mode metadata is incomplete.`)
  }
}
if (!same(oracle.engines.plane.modeInfo.FlatFace.referenceCombinations, [['Plane']]) || !same(oracle.engines.line.modeInfo.TwoPointLine.referenceCombinations, [['Vertex', 'Vertex'], ['Line']]) || !same(oracle.engines.point.modeInfo.Vertex.referenceCombinations, [['Vertex'], ['Line']])) fail('representative reference combination contract changed.')

const expectedSuccess = {
  plane: { typeId: 'PartDesign::Plane', mode: 'FlatFace', refs: ['Plane'], support: ['Face1'] },
  line: { typeId: 'PartDesign::Line', mode: 'TwoPointLine', refs: ['Vertex', 'Vertex'], support: ['Vertex1', 'Vertex2'] },
  point: { typeId: 'PartDesign::Point', mode: 'Vertex', refs: ['Vertex'], support: ['Vertex1'] },
  sketch: { typeId: 'Sketcher::SketchObject', mode: 'FlatFace', refs: ['Plane'], support: ['Face1'] },
}
for (const phase of ['initial', 'edited', 'roundtrip']) {
  for (const [name, expected] of Object.entries(expectedSuccess)) {
    const result = oracle.success?.[phase]?.[name]
    if (result?.typeId !== expected.typeId || result.mapMode !== expected.mode || result.positionBySupport !== true || !same(result.state, ['Up-to-date']) || result.status !== 'Valid' || result.suggestion?.message !== 'OK' || !result.suggestion.allApplicableModes.includes(expected.mode) || !same(result.suggestion.referenceTypes, expected.refs) || !same(result.support?.[0]?.subElements, expected.support) || result.support?.[0]?.object !== 'SourceBox') fail(`${phase} ${name} success evidence is invalid.`)
  }
}
if (!near(oracle.success.initial.point.placement.position?.[2], 6) || !near(oracle.success.edited.point.placement.position?.[2], 9) || !near(oracle.success.roundtrip.point.placement.position?.[2], 9)) fail('source mutation did not move the attached point through edit and round-trip.')

const failureContract = {
  InvalidPlane: ['PartDesign::Plane', 'Vertex', 'Attachment mode Vertex is not implemented.'],
  InvalidLine: ['PartDesign::Line', 'FlatFace', 'Attachment mode FlatFace is not implemented.'],
  InvalidPoint: ['PartDesign::Point', 'FlatFace', 'Attachment mode FlatFace is not implemented.'],
}
for (const [name, [typeId, mode, status]] of Object.entries(failureContract)) {
  const initial = oracle.failures?.initial?.[name]
  const roundtrip = oracle.failures?.roundtrip?.[name]
  if (initial?.typeId !== typeId || initial.mapMode !== mode || !same(initial.state, ['Touched', 'Invalid']) || initial.status !== status || roundtrip?.mapMode !== mode || !same(roundtrip.state, ['Touched', 'Invalid']) || roundtrip.status !== status) fail(`${name} unsupported-mode failure did not survive FCStd round-trip.`)
}

console.log(JSON.stringify({ status: 'freecad-attachment-mode-oracle-pass', baselineId: oracle.baselineId, persistentModes: oracle.registry.modeCount, implementedModes: { plane: implemented.plane.length, line: implemented.line.length, point: implemented.point.length, sketch: implemented.sketch.length }, representativeSuccesses: Object.keys(expectedSuccess), unsupportedFailures: Object.keys(failureContract), pointMutationZ: [oracle.success.initial.point.placement.position[2], oracle.success.edited.point.placement.position[2]], fcstdRoundtrip: true }, null, 2))
