import { encode } from "https://deno.land/std@0.103.0/encoding/base64.ts";
import Terser from "https://esm.sh/terser@4.8.0";
import * as lz4 from "https://deno.land/x/lz4@v0.1.2/mod.ts";

const name = "deno_swc";

const encoder = new TextEncoder();

async function requires(...executables) {
  const where = Deno.build.os === "windows" ? "where" : "which";

  for (const executable of executables) {
    const process = Deno.run({
      cmd: [where, executable],
      stderr: "null",
      stdin: "null",
      stdout: "null",
    });

    if (!(await process.status()).success) {
      err(`Could not find required build tool ${executable}`);
    }
  }
}

async function run(msg, cmd) {
  log(msg);

  const process = Deno.run({ cmd });

  if (!(await process.status()).success) {
    err(`${msg} failed`);
  }
}

function log(text) {
  console.log(`[log] ${text}`);
}

function err(text) {
  console.log(`[err] ${text}`);
  return Deno.exit(1);
}

await requires("rustup", "rustc", "cargo", "wasm-bindgen");

if (!(await Deno.stat("Cargo.toml")).isFile) {
  err(`the build script should be executed in the "${name}" root`);
}

await run("building wasm", ["cargo", "build", "--release", "--target", "wasm32-unknown-unknown"]);

await run(
  "building using wasm-pack",
  ["wasm-bindgen", "target/wasm32-unknown-unknown/release/deno_swc.wasm" , "--target", "deno", "--weak-refs", "--out-dir", "pkg/"],
);

const wasm = await Deno.readFile(`pkg/${name}_bg.wasm`);

const compressed = lz4.compress(wasm);
console.log(
  `compressed wasm using lz4      (reduction: ${wasm.length -
    compressed.length} bytes, size: ${compressed.length} bytes)`,
);

const encoded = encode(compressed);

log(
  `encoded wasm using base64, size increase: ${encoded.length -
    wasm.length} bytes`,
);

log("inlining wasm in js");
const source = `import * as lz4 from "https://deno.land/x/lz4@v0.1.2/mod.ts";export const source=lz4.decompress(Uint8Array.from(atob("${encoded}"),c=>c.charCodeAt(0)));`;

let init = await Deno.readTextFile(`pkg/${name}.js`);
let lines = init.split('\n');
// We want to replace this code.
for (let i = 1; i < 4; i++) lines.splice(-i);
init = lines.join('\n');
init += `\nconst wasmModule = new WebAssembly.Module(source);\nconst wasmInstance = new WebAssembly.Instance(wasmModule, imports);\nconst wasm = wasmInstance.exports;\n`;
console.log(init)

log("minifying js");
const output = Terser.minify(`${source}\n${init}`, {
  mangle: { module: true },
  output: {
    preamble: "//deno-fmt-ignore-file",
  },
});

if (output.error) {
  err(`encountered error when minifying: ${output.error}`);
}

const reduction = new Blob([(`${source}\n${init}`)]).size -
  new Blob([output.code]).size;
log(`minified js, size reduction: ${reduction} bytes`);

log(`writing output to file ("wasm.js")`);
await Deno.writeFile("wasm.js", encoder.encode(output.code));

const outputFile = await Deno.stat("wasm.js");
log(
  `output file ("wasm.js"), final size is: ${outputFile.size} bytes`,
);
