import { lstat, readFile, writeFile } from "node:fs/promises";
import { dirname, join, relative } from "node:path";
import { fileURLToPath } from "node:url";
import { format } from "prettier";

const root = join(dirname(fileURLToPath(import.meta.url)), "..");
const sourcePath = join(root, "src", "toolbox", "manifest.source.json");
const outputPath = join(root, "public", "toolbox-app.json");
const packagePath = join(root, "package.json");
const applicationVersionPath = join(root, "src", "version.ts");
const publicPath = join(root, "public");
const checkOnly = process.argv.includes("--check");

const source = JSON.parse(await readFile(sourcePath, "utf8"));
const packageJson = JSON.parse(await readFile(packagePath, "utf8"));
const applicationVersionSource = await readFile(applicationVersionPath, "utf8");
const applicationVersion =
  /^export const APPLICATION_VERSION = "([^"]+)";$/mu.exec(
    applicationVersionSource,
  )?.[1];

if (
  source.version !== packageJson.version ||
  applicationVersion !== packageJson.version
) {
  throw new Error(
    `Version drift: manifest ${source.version}, application ${String(applicationVersion)}, package ${packageJson.version}`,
  );
}

if (
  source.source?.repository !== "https://git.add-ideas.de/zemion/regex-tools" ||
  source.source?.license !== "GPL-3.0-or-later"
) {
  throw new Error("Manifest source identity is incomplete or inconsistent");
}

if (!Array.isArray(source.assets)) {
  throw new Error("Manifest assets must be an array");
}
for (const asset of source.assets) {
  if (
    typeof asset !== "string" ||
    !asset.startsWith("./") ||
    asset.includes("\\") ||
    asset.split("/").includes("..")
  ) {
    throw new Error(`Unsafe manifest asset path: ${JSON.stringify(asset)}`);
  }
  const candidate = join(publicPath, asset.slice(2));
  const details = await lstat(candidate).catch(() => null);
  if (!details?.isFile() || details.isSymbolicLink()) {
    throw new Error(`Manifest asset is missing or unsafe: ${asset}`);
  }
}

const serialized = await format(JSON.stringify(source), {
  filepath: outputPath,
});
if (checkOnly) {
  const current = await readFile(outputPath, "utf8").catch(() => "");
  if (current !== serialized) {
    throw new Error(
      `${relative(root, outputPath)} is stale; run npm run manifest:generate`,
    );
  }
  console.log("Toolbox manifest is synchronized");
} else {
  await writeFile(outputPath, serialized);
  console.log(`Generated ${relative(root, outputPath)}`);
}
