/**
 * Build plugins for inlining and externalizing Arcjet WebAssembly binaries.
 *
 * Ported behavior-preserving from the former `@arcjet/rollup-config`. These are
 * standard Rollup plugins, accepted by tsdown/rolldown via the `plugins`
 * option.
 *
 * @internal
 * @module
 */
import fs from "node:fs";
import path from "node:path";

/**
 * Generate the JavaScript module source for a Wasm binary inlined as a base64
 * Data URL.
 *
 * @internal
 * @param {Buffer} wasm
 *   Wasm binary contents.
 * @returns {string}
 *   JavaScript module source exposing an async `wasm()` function that decodes
 *   the Data URL into a `WebAssembly.Module`.
 */
function generateJs(wasm) {
  return `// @generated by wasm2module - DO NOT EDIT
/* eslint-disable */
// @ts-nocheck
/**
 * This file contains an Arcjet Wasm binary inlined as a base64 Data URL with
 * the application/wasm MIME type, then decoded into a WebAssembly.Module.
 */
const wasmBase64 = "data:application/wasm;base64,${wasm.toString("base64")}";
export async function wasm() {
  // fetch with no-store so files larger than 2mb don't fail to parse in the
  // Next.js App Router
  const wasmDecode = await fetch(wasmBase64, { cache: "no-store" });
  const buf = await wasmDecode.arrayBuffer();
  return WebAssembly.compile(buf);
}
`;
}

/**
 * Turn `*.wasm?js` imports into a virtual JS module exposing `wasm()`, which
 * decodes a base64 Data URL into a `WebAssembly.Module`.
 *
 * @internal
 * @returns {import("rolldown").Plugin}
 *   Rollup/rolldown plugin.
 */
export function base64Wasm() {
  /** @type {Map<string, string>} */
  const idToWasmPath = new Map();

  return {
    name: "base64-wasm",
    resolveId(source, importer) {
      if (source.endsWith(".wasm?js")) {
        const clean = source.slice(0, -3); // strip `?js` -> e.g. ./wasm/x.wasm
        const absWasm = importer
          ? path.resolve(path.dirname(importer), clean)
          : path.resolve(clean);
        // Keep the virtual id relative so the emitted chunk name does not leak
        // an absolute build path into published output. Strip the leading `./`
        // because rolldown emits it as a Windows-invalid `_.` path segment.
        const id = `\0${clean.replace(/^\.\//, "").replace(/\.wasm$/, ".js")}`;
        idToWasmPath.set(id, absWasm);
        return id;
      }
      return null;
    },
    async load(id) {
      const wasmPath = idToWasmPath.get(id);
      if (wasmPath) {
        return generateJs(await fs.promises.readFile(wasmPath));
      }
      return null;
    },
  };
}

/**
 * Leave `*.wasm` and `*.wasm?module` imports external so the consuming
 * platform's bundler (Vercel `.wasm?module`, Cloudflare `.wasm`) handles them.
 *
 * @internal
 * @returns {import("rolldown").Plugin}
 *   Rollup/rolldown plugin.
 */
export function externalizeWasm() {
  return {
    name: "externalize-wasm",
    resolveId(id) {
      if (id.endsWith(".wasm") || id.endsWith(".wasm?module")) {
        return { id, external: true };
      }
      return null;
    },
  };
}
