import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { basename, join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";

const manifest = JSON.parse(
  await readFile(new URL("../contracts/manifest.json", import.meta.url), "utf8"),
);
const run = promisify(execFile);
const projectRoot = fileURLToPath(new URL("../", import.meta.url));
const generator = join(
  projectRoot,
  "node_modules",
  "openapi-typescript",
  "bin",
  "cli.js",
);
const temporaryDirectory = await mkdtemp(
  join(tmpdir(), "tjwater-api-contracts-"),
);

try {
  for (const [name, contract] of Object.entries(manifest.contracts)) {
    const contractPath = fileURLToPath(
      new URL(`../contracts/${contract.file}`, import.meta.url),
    );
    const payload = await readFile(contractPath);
    const actual = createHash("sha256").update(payload).digest("hex");
    if (actual !== contract.sha256) {
      throw new Error(
        `${name} contract hash mismatch: expected ${contract.sha256}, got ${actual}`,
      );
    }

    const generatedName = `${name}Api.ts`;
    const temporaryOutput = join(temporaryDirectory, generatedName);
    await run(process.execPath, [generator, contractPath, "-o", temporaryOutput]);
    const expectedOutput = await readFile(
      new URL(`../src/generated/${generatedName}`, import.meta.url),
    );
    const generatedOutput = await readFile(temporaryOutput);
    if (!expectedOutput.equals(generatedOutput)) {
      throw new Error(
        `${basename(contract.file)} generated type is stale: run \`npm run api:generate\``,
      );
    }
  }
} finally {
  await rm(temporaryDirectory, { recursive: true, force: true });
}

console.log(
  `validated API contract mirrors and generated types for ${manifest.contract_version}`,
);
