import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import {
  copyFile,
  lstat,
  mkdir,
  mkdtemp,
  readFile,
  readdir,
  realpath,
  rename,
  rm,
  writeFile,
} from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
  verifyChecksummedEnginePack,
  writeEngineChecksums,
} from "./checksummed-engine-pack.mjs";

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");

export const CPP_LOCK = Object.freeze({
  engineVersion: "Emscripten 6.0.4 libc++ std::wregex",
  emscriptenVersion: "6.0.4",
  emscriptenCommit: "fe5be6afdff43ad58860d821fcc8572a23f92d19",
  emsdkCommit: "224ec5f9f2f72f09f9ce0e26d66bae7dbd8b692f",
  releaseCommit: "b23272fac5d05617cd36dc451356dca0f79bf22d",
  llvmVersion: "24.0.0git",
  llvmCommit: "c050c487e9edb97ef44f53cb29fe1d8bcddb8f76",
  sourceDateEpoch: 1_785_110_400,
  bridgeAbi: 1,
  legalFiles: Object.freeze({
    "LICENSE-Emscripten.txt":
      "620a78084fc7ca97c0b5dea9abf891f3ffcadfdbf305276f099c9c4e12fc1d86",
    "LICENSE-compiler-rt.txt":
      "1a8f1058753f1ba890de984e48f0242a3a5c29a6a8f2ed9fd813f36985387e8d",
    "LICENSE-libcxx.txt":
      "539dd7aed86e8a4f12cbdd0e6c50c189c7d74847e4fecc64ce2c6ee3a01da38b",
    "LICENSE-libcxxabi.txt":
      "e2b35be49f7284a45b7baca8fc7b3ab7440e7902392b2528a457816b5bb2a15c",
    "LICENSE-libunwind.txt":
      "b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d",
    "LICENSE-musl.txt":
      "b870108ec5e7790e9f9919064f1b9421d62d5f9b0e6c230c6adf7ea2da62e97b",
  }),
  sourceEvidence: Object.freeze({
    "emscripten-revision.txt":
      "33d347ba6aa1de448bc1727d46912c1abcbddac9a32dcf4794d7098dea16e1ad",
    "emscripten-version.txt":
      "1ae2390b7da62b8488a9f596a39ed978b22356de0f267db9ae9dd43d0d3faa6e",
    "system/lib/compiler-rt/README.md":
      "50648d4da5c5c88468da064bd9ba29d68d47597607852330423da0a1cf987946",
    "system/lib/libc/README.md":
      "69aeb7c1991af58c547f8da100c57ff38f53c4015f4df579731c5abab84654b9",
    "system/lib/libcxx/README.md":
      "da90136512d04c06e2bd27ef18d71be34bfc64c06bc29b12ddcf66bb76ae193b",
    "system/lib/libcxxabi/README.md":
      "dcfec7b2c2987ce00e513cac89933f4d30329070570c4efdb4d1d00297182736",
    "system/lib/libunwind/README.md":
      "26b3b06b2640cee75b2cf4e01b0956a0fd6cc5ca2054998e18d66ff060762410",
    "system/lib/emmalloc.c":
      "00eb577cbef51a3359c309f45a7c99a814b8c6151ab75c9a3c78a56f0169146b",
  }),
  linkedArchives: Object.freeze([
    "libc++.a",
    "libc++abi.a",
    "libc.a",
    "libclang_rt.builtins.a",
    "libembind-rtti.a",
    "libemmalloc.a",
    "libnoexit.a",
    "libstubs.a",
  ]),
});

const SOURCE_FILES = Object.freeze([
  "engines/cpp/README.md",
  "engines/cpp/cpp_regex_bridge.cpp",
  "scripts/cpp-engine-pack.mjs",
]);

const PACK_FILES = Object.freeze(
  [
    ...Object.keys(CPP_LOCK.legalFiles),
    "SHA256SUMS",
    "SOURCE-MANIFEST.json",
    "cpp-regex.mjs",
    "cpp-regex.wasm",
    "engine-metadata.json",
  ].sort(),
);

const SOURCE_COMPONENTS = Object.freeze([
  Object.freeze({
    id: "emscripten-runtime",
    version: CPP_LOCK.emscriptenVersion,
    linked: true,
    linkedArchives: ["libembind-rtti.a", "libnoexit.a", "libstubs.a"],
    sourcePath: "src, system/lib and tools",
    sourceEvidencePath: "emscripten-revision.txt",
    license: "MIT OR NCSA",
    legalFiles: ["LICENSE-Emscripten.txt"],
    evidence:
      "Emscripten-generated JavaScript/runtime imports plus exact linker-trace archive members",
  }),
  Object.freeze({
    id: "musl",
    version: "1.2.6 with Emscripten changes",
    linked: true,
    linkedArchives: ["libc.a"],
    sourcePath: "system/lib/libc",
    sourceEvidencePath: "system/lib/libc/README.md",
    license: "MIT and file-level compatible terms",
    legalFiles: ["LICENSE-musl.txt"],
    evidence: "exact linker trace selects members of libc.a",
  }),
  Object.freeze({
    id: "emmalloc",
    version: `Emscripten ${CPP_LOCK.emscriptenVersion} snapshot`,
    linked: true,
    linkedArchives: ["libemmalloc.a"],
    sourcePath: "system/lib/emmalloc.c",
    sourceEvidencePath: "system/lib/emmalloc.c",
    license: "MIT OR NCSA",
    legalFiles: ["LICENSE-Emscripten.txt"],
    evidence:
      "the build fixes -sMALLOC=emmalloc and the exact linker trace selects emmalloc.o and sbrk.o",
  }),
  Object.freeze({
    id: "compiler-rt",
    version: "22.1.8 with Emscripten changes",
    linked: true,
    linkedArchives: ["libclang_rt.builtins.a"],
    sourcePath: "system/lib/compiler-rt",
    sourceEvidencePath: "system/lib/compiler-rt/README.md",
    license: "Apache-2.0 WITH LLVM-exception and legacy component terms",
    legalFiles: ["LICENSE-compiler-rt.txt"],
    evidence:
      "exact linker trace selects compiler-rt builtins, including Emscripten exception support",
  }),
  Object.freeze({
    id: "libc++",
    version: "21.1.8 with Emscripten changes",
    linked: true,
    linkedArchives: ["libc++.a"],
    sourcePath: "system/lib/libcxx",
    sourceEvidencePath: "system/lib/libcxx/README.md",
    license: "Apache-2.0 WITH LLVM-exception and legacy component terms",
    legalFiles: ["LICENSE-libcxx.txt"],
    evidence:
      "exact linker trace selects libc++ regex, locale, string, exception and support objects",
  }),
  Object.freeze({
    id: "libc++abi",
    version: "21.1.8 with Emscripten changes",
    linked: true,
    linkedArchives: ["libc++abi.a"],
    sourcePath: "system/lib/libcxxabi",
    sourceEvidencePath: "system/lib/libcxxabi/README.md",
    license: "Apache-2.0 WITH LLVM-exception and legacy component terms",
    legalFiles: ["LICENSE-libcxxabi.txt"],
    evidence:
      "exact linker trace selects the Emscripten C++ exception ABI and type-information objects",
  }),
  Object.freeze({
    id: "libunwind",
    version: "22.1.8 with Emscripten changes",
    linked: false,
    linkedArchives: [],
    consideredArchive: "libunwind.a",
    sourcePath: "system/lib/libunwind",
    sourceEvidencePath: "system/lib/libunwind/README.md",
    license: "Apache-2.0 WITH LLVM-exception and legacy component terms",
    legalFiles: ["LICENSE-libunwind.txt"],
    evidence:
      "audited exclusion: the exact JavaScript-exception linker trace selects no libunwind.a member; its legal text is retained to make that boundary reviewable",
  }),
]);

function sha256(value) {
  return createHash("sha256").update(value).digest("hex");
}

async function fileRecord(directory, name) {
  const value = await readFile(path.join(directory, name));
  return { path: name, sha256: sha256(value), bytes: value.byteLength };
}

async function run(command, arguments_, options = {}) {
  const child = spawn(command, arguments_, {
    cwd: options.cwd,
    env: options.env,
    stdio: ["ignore", "pipe", "pipe"],
  });
  const stdout = [];
  const stderr = [];
  child.stdout.on("data", (chunk) => stdout.push(chunk));
  child.stderr.on("data", (chunk) => stderr.push(chunk));
  const result = await new Promise((resolve, reject) => {
    child.once("error", reject);
    child.once("close", (code, signal) => resolve({ code, signal }));
  });
  const output = Buffer.concat(stdout).toString("utf8");
  const errorOutput = Buffer.concat(stderr).toString("utf8");
  if (result.code !== 0) {
    throw new Error(
      `${command} failed with ${
        result.signal ? `signal ${result.signal}` : `exit code ${result.code}`
      }:\n${[output, errorOutput].filter(Boolean).join("\n").trim()}`,
    );
  }
  return { stdout: output, stderr: errorOutput };
}

async function exactEmpp(candidate) {
  const details = await lstat(candidate).catch(() => null);
  if (!details?.isFile() || details.isSymbolicLink()) {
    throw new Error(`em++ must be a regular file: ${candidate}`);
  }
  const executable = await realpath(candidate);
  const version = await run(executable, ["--version"]);
  if (
    !version.stdout.startsWith(
      `emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) ${CPP_LOCK.emscriptenVersion} (${CPP_LOCK.emscriptenCommit})`,
    )
  ) {
    throw new Error(
      `The C++ pack requires Emscripten ${CPP_LOCK.emscriptenVersion} at commit ${CPP_LOCK.emscriptenCommit}.`,
    );
  }
  const upstreamRoot = path.dirname(path.dirname(executable));
  const sourceRoot = path.join(upstreamRoot, "emscripten");
  const legalFiles = {
    "LICENSE-Emscripten.txt": path.join(sourceRoot, "LICENSE"),
    "LICENSE-compiler-rt.txt": path.join(
      sourceRoot,
      "system",
      "lib",
      "compiler-rt",
      "LICENSE.TXT",
    ),
    "LICENSE-libcxx.txt": path.join(
      sourceRoot,
      "system",
      "lib",
      "libcxx",
      "LICENSE.TXT",
    ),
    "LICENSE-libcxxabi.txt": path.join(
      sourceRoot,
      "system",
      "lib",
      "libcxxabi",
      "LICENSE.TXT",
    ),
    "LICENSE-libunwind.txt": path.join(
      sourceRoot,
      "system",
      "lib",
      "libunwind",
      "LICENSE.TXT",
    ),
    "LICENSE-musl.txt": path.join(
      sourceRoot,
      "system",
      "lib",
      "libc",
      "musl",
      "COPYRIGHT",
    ),
  };
  for (const [name, file] of Object.entries(legalFiles)) {
    const licenseDetails = await lstat(file).catch(() => null);
    if (!licenseDetails?.isFile() || licenseDetails.isSymbolicLink()) {
      throw new Error(`Required toolchain licence is missing: ${file}`);
    }
    if (sha256(await readFile(file)) !== CPP_LOCK.legalFiles[name]) {
      throw new Error(
        `The pinned Emscripten toolchain legal file drifted: ${name}.`,
      );
    }
  }
  for (const [name, digest] of Object.entries(CPP_LOCK.sourceEvidence)) {
    const file = path.join(sourceRoot, name);
    const sourceDetails = await lstat(file).catch(() => null);
    if (!sourceDetails?.isFile() || sourceDetails.isSymbolicLink()) {
      throw new Error(`Required toolchain source evidence is missing: ${file}`);
    }
    if (sha256(await readFile(file)) !== digest) {
      throw new Error(
        `The pinned Emscripten source evidence drifted: ${name}.`,
      );
    }
  }
  const clang = await realpath(path.join(upstreamRoot, "bin", "clang++"));
  const clangDetails = await lstat(clang).catch(() => null);
  if (!clangDetails?.isFile()) {
    throw new Error(`Required pinned clang++ is missing: ${clang}`);
  }
  const clangVersion = await run(clang, ["--version"]);
  if (
    !clangVersion.stdout.startsWith(`clang version ${CPP_LOCK.llvmVersion} `) ||
    !clangVersion.stdout.includes(CPP_LOCK.llvmCommit)
  ) {
    throw new Error(
      `The C++ pack requires LLVM ${CPP_LOCK.llvmVersion} at commit ${CPP_LOCK.llvmCommit}.`,
    );
  }
  return { executable, legalFiles };
}

function buildEnvironment() {
  const environment = { ...process.env };
  for (const variable of [
    "CFLAGS",
    "CPPFLAGS",
    "CXXFLAGS",
    "EMCC_CFLAGS",
    "EM_CONFIG",
  ]) {
    delete environment[variable];
  }
  environment.LANG = "C";
  environment.LC_ALL = "C";
  environment.SOURCE_DATE_EPOCH = String(CPP_LOCK.sourceDateEpoch);
  environment.TZ = "UTC";
  return environment;
}

async function sourceRecords(projectRoot) {
  return Promise.all(
    SOURCE_FILES.map(async (name) => {
      const value = await readFile(path.join(projectRoot, name));
      return { path: name, sha256: sha256(value), bytes: value.byteLength };
    }),
  );
}

function linkedArchives(buildOutput) {
  const archives = new Set();
  for (const match of buildOutput.matchAll(
    /(?:^|\n)[^\n]*[\\/](lib[^/\\()\s]+\.a)\([^)\n]+\)/gu,
  )) {
    archives.add(match[1]);
  }
  return [...archives].sort();
}

function assertLinkedArchives(buildOutput) {
  const actual = linkedArchives(buildOutput);
  if (
    actual.length !== CPP_LOCK.linkedArchives.length ||
    actual.some((name, index) => name !== CPP_LOCK.linkedArchives[index]) ||
    /[\\/]libunwind\.a\(/u.test(buildOutput)
  ) {
    throw new Error(
      `C++ exact linker inventory drifted: ${actual.join(", ") || "(none)"}.`,
    );
  }
  return actual;
}

async function expectedSourceManifest(pack) {
  const wasm = await fileRecord(pack, "cpp-regex.wasm");
  const legalFiles = await Promise.all(
    Object.keys(CPP_LOCK.legalFiles).map((name) => fileRecord(pack, name)),
  );
  return {
    schemaVersion: 1,
    engine: "cpp",
    scope: {
      asset: wasm.path,
      assetSha256: wasm.sha256,
      assetBytes: wasm.bytes,
      purpose:
        "Complete linked-component, preferred-source and legal inventory for the exact C++ WebAssembly module",
    },
    sourceSnapshot: {
      project: "Emscripten",
      version: CPP_LOCK.emscriptenVersion,
      tag: CPP_LOCK.emscriptenVersion,
      commitSha1: CPP_LOCK.emscriptenCommit,
      repository: "https://github.com/emscripten-core/emscripten",
      preferredSourceUrl: `https://github.com/emscripten-core/emscripten/tree/${CPP_LOCK.emscriptenCommit}`,
      archiveUrl: `https://github.com/emscripten-core/emscripten/archive/${CPP_LOCK.emscriptenCommit}.tar.gz`,
      emsdk: {
        tag: CPP_LOCK.emscriptenVersion,
        commitSha1: CPP_LOCK.emsdkCommit,
        releaseCommitSha1: CPP_LOCK.releaseCommit,
        repository: "https://github.com/emscripten-core/emsdk",
      },
      llvmToolchain: {
        version: CPP_LOCK.llvmVersion,
        commitSha1: CPP_LOCK.llvmCommit,
        repository: "https://github.com/llvm/llvm-project",
      },
    },
    buildSelection: {
      exceptionMode: "Emscripten JavaScript exceptions (-fexceptions)",
      allocator: "emmalloc (-sMALLOC=emmalloc)",
      exactLinkedArchives: [...CPP_LOCK.linkedArchives],
      inventoryMethod:
        "the pack builder adds wasm-ld --trace, accepts this exact archive set and fails on any addition, removal or libunwind selection",
    },
    sourceEvidence: Object.entries(CPP_LOCK.sourceEvidence).map(
      ([sourcePath, sourceSha256]) => ({ sourcePath, sourceSha256 }),
    ),
    linkedComponents: SOURCE_COMPONENTS,
    legalFiles,
  };
}

async function writeSourceManifest(pack) {
  await writeFile(
    path.join(pack, "SOURCE-MANIFEST.json"),
    `${JSON.stringify(await expectedSourceManifest(pack), null, 2)}\n`,
    "utf8",
  );
}

async function writeMetadata(pack, projectRoot) {
  const metadata = {
    schemaVersion: 1,
    engine: "cpp",
    engineName: "C++ std::wregex (Emscripten libc++)",
    engineVersion: CPP_LOCK.engineVersion,
    semanticIdentity:
      "Emscripten 6.0.4 libc++ std::wregex; C++ modified ECMAScript grammar by default",
    status: "production-native-runtime",
    bridge: {
      abiVersion: CPP_LOCK.bridgeAbi,
      nativeOffsetUnit: "Unicode code points (32-bit wchar_t)",
      requestEncoding: "structured Embind values; no user source evaluation",
      dynamicExecutionDisabled: true,
      replacement: {
        semantics: "libc++ match_results::format default grammar",
        implementation:
          "bounded streaming expansion without materializing an unbounded native format result",
        paritySelfTest:
          "load-time fixtures compare the bounded expander with libc++ match_results::format",
      },
      supportedApplicationFlags: ["g", "i", "n", "o", "c"],
      grammars: ["ECMAScript", "basic", "extended", "awk", "grep", "egrep"],
      limits: {
        maximumPatternUtf16: 65_536,
        maximumSubjectBytes: 16_777_216,
        maximumReplacementUtf16: 65_536,
        maximumMatches: 10_000,
        maximumCaptureRows: 100_000,
        maximumCaptureGroups: 1_000,
        maximumOutputBytes: 67_108_864,
      },
      sourceFiles: await sourceRecords(projectRoot),
    },
    source: {
      project: "Emscripten system runtime and LLVM libc++",
      repository: "https://github.com/emscripten-core/emscripten",
      tag: CPP_LOCK.emscriptenVersion,
      commitSha1: CPP_LOCK.emscriptenCommit,
      preferredSourceUrl: `https://github.com/emscripten-core/emscripten/tree/${CPP_LOCK.emscriptenCommit}`,
      sourceManifest: "SOURCE-MANIFEST.json",
      license:
        "MIT OR NCSA AND MIT AND Apache-2.0 WITH LLVM-exception and legacy component terms",
    },
    toolchain: {
      version: CPP_LOCK.emscriptenVersion,
      commitSha1: CPP_LOCK.emscriptenCommit,
      emsdkCommitSha1: CPP_LOCK.emsdkCommit,
      releaseCommitSha1: CPP_LOCK.releaseCommit,
      llvmVersion: CPP_LOCK.llvmVersion,
      llvmCommitSha1: CPP_LOCK.llvmCommit,
      target: "wasm32-unknown-emscripten",
      linkedArchives: [...CPP_LOCK.linkedArchives],
      command:
        "em++ -std=c++20 -O3 -fexceptions --bind -sMODULARIZE=1 -sEXPORT_ES6=1 -sMALLOC=emmalloc -sDYNAMIC_EXECUTION=0 -Wl,--trace",
    },
    licensing: {
      sourceManifest: "SOURCE-MANIFEST.json",
      linkedComponentCount: SOURCE_COMPONENTS.filter(
        (component) => component.linked,
      ).length,
      auditedExclusions: ["libunwind"],
      independentlyPinnedLegalFiles: Object.keys(CPP_LOCK.legalFiles).length,
    },
    sourceDateEpoch: CPP_LOCK.sourceDateEpoch,
    files: await Promise.all(
      [
        ...Object.keys(CPP_LOCK.legalFiles),
        "SOURCE-MANIFEST.json",
        "cpp-regex.mjs",
        "cpp-regex.wasm",
      ].map((name) => fileRecord(pack, name)),
    ),
  };
  await writeFile(
    path.join(pack, "engine-metadata.json"),
    `${JSON.stringify(metadata, null, 2)}\n`,
    "utf8",
  );
  await writeEngineChecksums(pack);
  return metadata;
}

export async function verifyCppPack(pack, projectRoot = root) {
  const metadata = await verifyChecksummedEnginePack(pack, "cpp");
  const files = (await readdir(pack)).sort();
  const expectedFiles = [...PACK_FILES].sort();
  let sourceManifest;
  try {
    sourceManifest = JSON.parse(
      await readFile(path.join(pack, "SOURCE-MANIFEST.json"), "utf8"),
    );
  } catch (error) {
    throw new Error("C++ SOURCE-MANIFEST.json is missing or invalid.", {
      cause: error,
    });
  }
  if (
    files.length !== expectedFiles.length ||
    files.some((name, index) => name !== expectedFiles[index]) ||
    metadata.engineVersion !== CPP_LOCK.engineVersion ||
    metadata.toolchain?.version !== CPP_LOCK.emscriptenVersion ||
    metadata.toolchain?.commitSha1 !== CPP_LOCK.emscriptenCommit ||
    metadata.toolchain?.emsdkCommitSha1 !== CPP_LOCK.emsdkCommit ||
    metadata.toolchain?.releaseCommitSha1 !== CPP_LOCK.releaseCommit ||
    metadata.toolchain?.llvmVersion !== CPP_LOCK.llvmVersion ||
    metadata.toolchain?.llvmCommitSha1 !== CPP_LOCK.llvmCommit ||
    JSON.stringify(metadata.toolchain?.linkedArchives) !==
      JSON.stringify(CPP_LOCK.linkedArchives) ||
    metadata.bridge?.abiVersion !== CPP_LOCK.bridgeAbi ||
    metadata.bridge?.dynamicExecutionDisabled !== true ||
    metadata.bridge?.replacement?.semantics !==
      "libc++ match_results::format default grammar" ||
    metadata.bridge?.replacement?.implementation !==
      "bounded streaming expansion without materializing an unbounded native format result" ||
    metadata.bridge?.replacement?.paritySelfTest !==
      "load-time fixtures compare the bounded expander with libc++ match_results::format" ||
    JSON.stringify(metadata.bridge?.sourceFiles) !==
      JSON.stringify(await sourceRecords(projectRoot)) ||
    JSON.stringify(sourceManifest) !==
      JSON.stringify(await expectedSourceManifest(pack)) ||
    metadata.source?.sourceManifest !== "SOURCE-MANIFEST.json" ||
    metadata.licensing?.linkedComponentCount !==
      SOURCE_COMPONENTS.filter((component) => component.linked).length ||
    JSON.stringify(metadata.licensing?.auditedExclusions) !==
      JSON.stringify(["libunwind"]) ||
    metadata.licensing?.independentlyPinnedLegalFiles !==
      Object.keys(CPP_LOCK.legalFiles).length ||
    Object.entries(CPP_LOCK.legalFiles).some(
      ([name, digest]) =>
        metadata.files?.find((file) => file.path === name)?.sha256 !== digest,
    )
  ) {
    throw new Error("C++ engine metadata drifted from its pinned sources.");
  }
  const module = await readFile(path.join(pack, "cpp-regex.mjs"), "utf8");
  if (module.includes("new Function") || /\beval\s*\(/u.test(module)) {
    throw new Error("C++ engine module contains dynamic JavaScript execution.");
  }
  return metadata;
}

export async function buildCppPack(
  empp,
  output = path.join(root, ".engine-build", "cpp"),
  projectRoot = root,
) {
  const toolchain = await exactEmpp(empp);
  const outputParent = path.dirname(output);
  await mkdir(outputParent, { recursive: true });
  const staging = await mkdtemp(path.join(outputParent, ".cpp-build-"));
  try {
    const build = await run(
      toolchain.executable,
      [
        path.join(projectRoot, "engines", "cpp", "cpp_regex_bridge.cpp"),
        "-std=c++20",
        "-O3",
        "-fexceptions",
        "--bind",
        "-sMODULARIZE=1",
        "-sEXPORT_ES6=1",
        "-sENVIRONMENT=web,worker,node",
        "-sFILESYSTEM=0",
        "-sALLOW_MEMORY_GROWTH=1",
        "-sINITIAL_MEMORY=16777216",
        "-sMAXIMUM_MEMORY=268435456",
        "-sMALLOC=emmalloc",
        "-sASSERTIONS=0",
        "-sDYNAMIC_EXECUTION=0",
        "-sINCOMING_MODULE_JS_API=locateFile,print,printErr,wasmBinary",
        "-sEXPORT_NAME=createCppRegexModule",
        "-Wl,--trace",
        "-o",
        path.join(staging, "cpp-regex.mjs"),
      ],
      { cwd: projectRoot, env: buildEnvironment() },
    );
    assertLinkedArchives(`${build.stdout}\n${build.stderr}`);
    for (const [name, source] of Object.entries(toolchain.legalFiles)) {
      await copyFile(source, path.join(staging, name));
    }
    await writeSourceManifest(staging);
    await writeMetadata(staging, projectRoot);
    await verifyCppPack(staging, projectRoot);
    await rm(output, { recursive: true, force: true });
    await rename(staging, output);
    return { output, metadata: await verifyCppPack(output, projectRoot) };
  } catch (error) {
    await rm(staging, { recursive: true, force: true });
    throw error;
  }
}

export async function installCppPack(source, projectRoot = root) {
  await verifyCppPack(source, projectRoot);
  const destination = path.join(projectRoot, "public", "engines", "cpp");
  const parent = path.dirname(destination);
  const staging = await mkdtemp(path.join(parent, ".cpp-install-"));
  try {
    for (const name of PACK_FILES) {
      await copyFile(path.join(source, name), path.join(staging, name));
    }
    await verifyCppPack(staging, projectRoot);
    await rm(destination, { recursive: true, force: true });
    await rename(staging, destination);
  } catch (error) {
    await rm(staging, { recursive: true, force: true });
    throw error;
  }
  return { output: destination, metadata: await verifyCppPack(destination) };
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
  const arguments_ = process.argv.slice(2);
  if (arguments_.length !== 2 || arguments_[0] !== "--empp") {
    throw new Error(
      "Usage: node scripts/cpp-engine-pack.mjs --empp /absolute/path/to/em++",
    );
  }
  const result = await buildCppPack(arguments_[1]);
  console.log(`Built verified C++ engine pack at ${result.output}.`);
}
