import { readdir, readFile } from "node:fs/promises";
import { join } from "node:path";

const EXPECTED_TRANSPORT_BY_DIR = new Map([
  ["dist", "tauri"],
  ["dist-web", "http"],
]);

async function readJavaScriptAssets(buildDir) {
  const assetsDir = join(process.cwd(), buildDir, "assets");
  let entries;
  try {
    entries = await readdir(assetsDir, { withFileTypes: true });
  } catch (error) {
    throw new Error(
      `Cannot read ${assetsDir}. Run npm run build:bundle before this check. ${error.message}`,
    );
  }

  const files = entries
    .filter((entry) => entry.isFile() && entry.name.endsWith(".js"))
    .map((entry) => join(assetsDir, entry.name));

  if (files.length === 0) {
    throw new Error(`No JavaScript assets found in ${assetsDir}.`);
  }

  return Promise.all(
    files.map(async (file) => ({
      file,
      source: await readFile(file, "utf8"),
    })),
  );
}

function transportMarkers(source) {
  return [...source.matchAll(/__IDEA_TRANSPORT__="([a-z]+)"/g)].map(
    (match) => match[1],
  );
}

async function assertBundleTransport(buildDir, expectedTransport) {
  const assets = await readJavaScriptAssets(buildDir);
  const markers = assets.flatMap(({ file, source }) =>
    transportMarkers(source).map((transport) => ({ file, transport })),
  );

  if (markers.length === 0) {
    throw new Error(
      `${buildDir} does not publish a __IDEA_TRANSPORT__ marker in its built JavaScript assets.`,
    );
  }

  const unexpected = markers.filter(
    ({ transport }) => transport !== expectedTransport,
  );
  if (unexpected.length > 0) {
    const details = unexpected
      .map(({ file, transport }) => `${file}: ${transport}`)
      .join("\n");
    throw new Error(
      `${buildDir} resolves the wrong transport; expected ${expectedTransport}.\n${details}`,
    );
  }

  console.log(
    `${buildDir}: __IDEA_TRANSPORT__="${expectedTransport}" (${markers.length} marker${markers.length === 1 ? "" : "s"})`,
  );
}

try {
  for (const [buildDir, expectedTransport] of EXPECTED_TRANSPORT_BY_DIR) {
    await assertBundleTransport(buildDir, expectedTransport);
  }
} catch (error) {
  console.error(error instanceof Error ? error.message : error);
  process.exitCode = 1;
}
