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

const root = resolve(new URL('..', import.meta.url).pathname)
const sourceRoot = resolve(root, process.env.FREECAD_SOURCE_DIR || '.cache/freecad/FreeCAD')
const outputPath = resolve(root, process.env.FREECAD_ENTRYPOINT_INVENTORY || 'config/freecad-entrypoint-inventory.json')
const sourceInventory = JSON.parse(await readFile(resolve(root, 'config/freecad-source-inventory.json'), 'utf8'))
const exists = async (relativePath) => access(resolve(sourceRoot, relativePath)).then(() => true).catch(() => false)

const formats = [
  ['FCStd', 'project', ['App', 'Gui'], ['src/App/Document.cpp', 'src/Gui/Document.cpp'], 'native-parser', 'read-only'],
  ['BREP', 'geometry', ['Part'], ['src/Mod/Part/App/FeaturePartImportBrep.cpp'], 'occt-worker', 'experimental'],
  ['STEP', 'geometry', ['Part', 'Import'], ['src/Mod/Part/App/FeaturePartImportStep.cpp', 'src/Mod/Import/App/WriterStep.cpp'], 'occt-worker', 'experimental'],
  ['IGES', 'geometry', ['Part', 'Import'], ['src/Mod/Part/App/FeaturePartImportIges.cpp', 'src/Mod/Import/App/WriterIges.cpp'], 'occt-worker', 'experimental'],
  ['STL', 'mesh', ['Mesh', 'MeshPart'], ['src/Mod/Mesh/App/Importer.cpp', 'src/Mod/Mesh/App/Exporter.cpp'], 'worker-adapter', 'experimental'],
  ['OBJ', 'mesh', ['Mesh'], ['src/Mod/Mesh/App/Importer.cpp', 'src/Mod/Mesh/App/Exporter.cpp'], 'worker-adapter', 'experimental'],
  ['PLY', 'mesh', ['Mesh'], ['src/Mod/Mesh/App/Importer.cpp', 'src/Mod/Mesh/App/Exporter.cpp'], 'worker-adapter', 'experimental'],
  ['DXF', '2d', ['Draft', 'Import'], ['src/Mod/Draft/importDXF.py', 'src/Mod/Import/App/dxf/ImpExpDxf.cpp'], 'worker-adapter', 'read-only'],
  ['SVG', 'drawing', ['Draft', 'TechDraw'], ['src/Mod/Draft/importSVG.py', 'src/Mod/TechDraw/App/TechDrawExport.cpp'], 'sanitized-parser', 'read-only'],
  ['PDF', 'drawing', ['TechDraw'], ['src/Mod/TechDraw/App/TechDrawExport.cpp'], 'server-export', 'read-only'],
  ['IFC', 'bim', ['BIM'], ['src/Mod/BIM/importers/importIFC.py'], 'sandboxed-adapter', 'unsupported'],
  ['CSV', 'data', ['Spreadsheet'], ['src/Mod/Spreadsheet/App/SheetPyImp.cpp'], 'facade-parser', 'experimental'],
  ['GCODE', 'manufacturing', ['CAM'], ['src/Mod/CAM/PathCommands.py'], 'sandboxed-adapter', 'unsupported'],
  ['JT', 'geometry', ['JtReader'], ['src/Mod/JtReader/CMakeLists.txt'], 'proxy-resource', 'unsupported'],
  ['IDF', 'electronics', ['Idf'], ['src/Mod/Idf/Idf.py'], 'proxy-resource', 'unsupported'],
]

const entrypoints = [
  ['python-api', 'scripting', ['App', 'Gui'], ['src/App/ApplicationPy.cpp', 'src/Gui/ApplicationPy.cpp'], 'sandboxed-interpreter', 'unsupported'],
  ['macro-fcmacro', 'scripting', ['Gui'], ['src/Gui/CommandMacro.cpp'], 'blocked-resource', 'unsupported'],
  ['addon-package', 'extension', ['AddonManager'], ['src/Mod/AddonManager'], 'signed-catalog', 'unsupported'],
  ['preferences', 'configuration', ['App', 'Gui'], ['src/Base/Parameter.cpp', 'src/Gui/PreferencePages'], 'namespaced-settings', 'experimental'],
  ['clipboard', 'desktop', ['Gui'], ['src/Gui/MainWindow.cpp'], 'browser-clipboard-permission', 'unsupported'],
  ['print-job', 'desktop', ['Gui', 'TechDraw'], ['src/Gui/MainWindow.cpp', 'src/Mod/TechDraw/Gui/PagePrinter.cpp'], 'server-export', 'unsupported'],
]

const validateEvidence = async (items) => {
  for (const item of items) for (const evidence of item[3]) if (!(await exists(evidence))) throw new Error(`Missing source evidence: ${evidence}`)
}
await validateEvidence(formats)
await validateEvidence(entrypoints)

const formatRecords = formats.map(([id, category, modules, sourceEvidence, webStrategy, compatibility]) => ({ id, category, modules, sourceEvidence, webStrategy, compatibility, roundTrip: compatibility === 'experimental', status: 'inventory-recorded' }))
const entrypointRecords = entrypoints.map(([id, category, modules, sourceEvidence, webStrategy, compatibility]) => ({ id, category, modules, sourceEvidence, webStrategy, compatibility, execution: false, status: 'inventory-recorded' }))
const coveredModules = new Set([...formatRecords, ...entrypointRecords].flatMap((entry) => entry.modules))
const moduleCoverage = sourceInventory.modules.map((module) => ({ name: module.name, sourcePath: module.sourcePath, coveredBy: [...formatRecords, ...entrypointRecords].filter((entry) => entry.modules.includes(module.name)).map((entry) => entry.id), status: coveredModules.has(module.name) ? 'entrypoint-covered' : 'scope-only' }))

const inventory = {
  schemaVersion: 1,
  baseline: { freecadVersion: '1.1.1', commit: '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' },
  generatedBy: 'scripts/generate-freecad-entrypoint-inventory.mjs',
  sourceInventory: 'config/freecad-source-inventory.json',
  formats: formatRecords,
  entrypoints: entrypointRecords,
  moduleCoverage,
  policy: {
    unsupportedExecution: ['Python API', 'FCMacro', 'addon package', 'clipboard', 'print job'],
    unknownOrUnlistedEntries: 'fail-inventory-check',
    untrustedContent: 'never-execute',
  },
}
await writeFile(outputPath, `${JSON.stringify(inventory, null, 2)}\n`, 'utf8')
console.log(JSON.stringify({ status: 'entrypoint-inventory-generated', output: outputPath, formatCount: formatRecords.length, entrypointCount: entrypointRecords.length, moduleCount: moduleCoverage.length }, null, 2))
