import { spawnSync } from 'node:child_process'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { loadGoldenManifest, compareGoldenResult } from './freecad-golden-contract.mjs'

const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const args = new Set(process.argv.slice(2))
const scenarioArgument = process.argv.slice(2).find((argument) => argument.startsWith('--scenario='))
const manifestArgument = process.argv.slice(2).find((argument) => argument.startsWith('--manifest='))
const requestedScenario = scenarioArgument?.slice('--scenario='.length)
const jsonOutput = args.has('--json')
const allowMissing = args.has('--allow-missing')
const manifest = await loadGoldenManifest(resolve(root, manifestArgument?.slice('--manifest='.length) || 'fixtures/freecad-golden/manifest.json'))
const driver = resolve(root, manifest.driver)

const localOracle = resolve(root, '.cache/freecad/install-native/bin/FreeCADCmd')
const candidates = process.env.FREECAD_CMD
  ? [process.env.FREECAD_CMD]
  : [localOracle, 'FreeCADCmd', 'freecadcmd']
let command = null
for (const candidate of candidates) {
  const probe = spawnSync(candidate, ['--version'], { encoding: 'utf8', timeout: 15000 })
  if (!probe.error || probe.error.code !== 'ENOENT') { command = candidate; break }
}
if (!command) {
  const message = `FreeCADCmd 1.1.1 is unavailable. Set FREECAD_CMD to the locked desktop oracle executable.`
  if (allowMissing) { console.log(message); process.exit(0) }
  console.error(message)
  process.exit(2)
}

const scenarios = requestedScenario ? manifest.scenarios.filter((entry) => entry.id === requestedScenario) : manifest.scenarios
if (scenarios.length === 0) { console.error(`Unknown golden scenario: ${requestedScenario}`); process.exit(2) }
const reports = []
for (const entry of scenarios) {
  const execution = spawnSync(command, [driver], {
    cwd: root,
    encoding: 'utf8',
    timeout: 120000,
    maxBuffer: 10 * 1024 * 1024,
    env: { ...process.env, FREECAD_GOLDEN_SCENARIO: entry.file },
  })
  const output = `${execution.stdout || ''}\n${execution.stderr || ''}`
  const resultLine = output.split(/\r?\n/).find((line) => line.startsWith('FREECAD_GOLDEN_RESULT='))
  if (execution.error || execution.status !== 0 || !resultLine) {
    reports.push({ id: entry.id, passed: false, differences: [`FreeCADCmd execution failed with status ${execution.status}: ${execution.error?.message || output.trim()}`] })
    continue
  }
  let actual
  try { actual = JSON.parse(resultLine.slice('FREECAD_GOLDEN_RESULT='.length)) }
  catch (error) { reports.push({ id: entry.id, passed: false, differences: [`Invalid result JSON: ${error.message}`] }); continue }
  const differences = []
  if (actual.fixtureId !== entry.id) differences.push(`fixtureId: expected ${entry.id}, received ${actual.fixtureId}`)
  if (actual.freecadVersion !== '1.1.1') differences.push(`freecadVersion: expected 1.1.1, received ${actual.freecadVersion}`)
  differences.push(...compareGoldenResult(entry.scenario, actual))
  reports.push({ id: entry.id, passed: differences.length === 0, differences, actual })
}

if (jsonOutput) console.log(JSON.stringify({ schemaVersion: 1, baselineId: manifest.baselineId, command, reports }, null, 2))
else for (const report of reports) {
  console.log(`${report.passed ? 'PASS' : 'FAIL'} ${report.id}`)
  report.differences.forEach((difference) => console.log(`  ${difference}`))
}
if (reports.some((report) => !report.passed)) process.exit(1)
