import { createId } from "@paralleldrive/cuid2";
import brotliPromise from "brotli-wasm";
import { spawn } from "bun";
import { dir } from "dir";
import { build } from "esbuild";
import { polyfillNode } from "esbuild-plugin-polyfill-node";
import { fdir } from "fdir";
import { statSync } from "fs";
import { listAsync, removeAsync, writeAsync } from "fs-jetpack";
import { platform } from "os";

const brotli = await brotliPromise;

await build({
  bundle: true,
  absWorkingDir: dir.path(""),
  entryPoints: [dir.path("app/web/src/nova/prod/main.tsx")],
  outdir: dir.path(`/app/srv/core`),
  splitting: true,
  format: "esm",
  jsx: "transform",
  minify: true,
  sourcemap: true,
  logLevel: "error",
  assetNames: `[name]`,
  loader: { ".woff": "file", ".ttf": "file", ".woff2": "file" },
  define: {
    "process.env.NODE_ENV": `"production"`,
  },
  plugins: [
    polyfillNode({
      polyfills: {
        buffer: true,
        _stream_duplex: false,
        _stream_passthrough: false,
        _stream_readable: false,
        _stream_transform: false,
        _stream_writable: false,
        assert: false,
        async_hooks: false,
        child_process: false,
        cluster: false,
        console: false,
        constants: false,
        crypto: false,
        dgram: false,
        diagnostics_channel: false,
        dns: false,
        domain: false,
        events: false,
        fs: false,
        http: false,
        http2: false,
        https: false,
        module: false,
        net: false,
        os: false,
        path: false,
        perf_hooks: false,
        process: false,
        punycode: false,
        querystring: false,
        readline: false,
        repl: false,
        stream: false,
        string_decoder: false,
        sys: false,
        timers: false,
        tls: false,
        tty: false,
        url: false,
        util: false,
        v8: false,
        vm: false,
        wasi: false,
        worker_threads: false,
        zlib: false,
        "assert/strict": false,
        "fs/promises": false,
        "timers/promises": false,
      },
    }),
  ],
});

const build_all =
  process.argv[process.argv.length - 1] === "main" ? false : true;
if (build_all) {
  await removeAsync(dir.path("app/web/.parcel-cache"));
  await removeAsync(dir.path("app/static"));

  await writeAsync(
    dir.path("app/web/timestamp.ts"),
    `export const version = "${createId().substring(0, 7)}";`
  );

  const args = [
    "node",
    dir.path(
      platform() === "win32"
        ? "node_modules/.bin/parcel.exe"
        : "node_modules/.bin/parcel"
    ),
    "build",
    "./src/index.html",
    // "--no-optimize",
    "--no-scope-hoist",
    "--dist-dir",
    dir.path(`app/static`),
  ];

  const parcel = spawn({
    cmd: args,
    cwd: dir.path("app/web"),
    stdio: ["ignore", "inherit", "inherit"],
  });
  await parcel.exited;

  // const public_br = dir.path("app/web/public-br");
  // await removeAsync(public_br);

  const api = new fdir().withRelativePaths().crawl(dir.path("app/web/public"));
  const public_files = api.sync();
  if (public_files) {
    await Promise.all(
      public_files.map(async (file) => {
        if (file !== "favicon.ico" && !file.startsWith("monaco")) {
          public_files.push(file);
        }

        const brfile = Bun.file(dir.path(`app/web/public-br/${file}`));
        if (!(await brfile.exists())) {
          const bfile = Bun.file(dir.path(`app/web/public/${file}`));
          const br = brotli.compress(
            new Uint8Array(await bfile.arrayBuffer()),
            {
              quality: 11,
            }
          );
          if (br) {
            console.log(`Compressing [public] ${file}`);
            await Bun.write(
              dir.path(`app/web/public-br/${file}`),
              Buffer.from(br)
            );
          }
        }
      })
    );
    await Bun.write(
      dir.path("app/web/public_files.ts"),
      `export const files = ${JSON.stringify(public_files, null, 2)}`
    );
  }

  const static_br = dir.path("app/static-br");
  await removeAsync(static_br);
  const static_files = await listAsync(dir.path("app/static"));
  if (static_files) {
    await Promise.all(
      static_files
        .filter((file) => statSync(dir.path(`app/static/${file}`)).isFile())
        .map(async (file) => {
          if (!(await Bun.file(dir.path(`app/static-br/${file}`)).exists())) {
            const br = brotli.compress(
              new Uint8Array(
                await Bun.file(dir.path(`app/static/${file}`)).arrayBuffer()
              ),
              { quality: 11 }
            );
            if (br) {
              console.log(`Compressing [static] ${file}`);
              await writeAsync(
                dir.path(`app/static-br/${file}`),
                Buffer.from(br)
              );
            }
          }
        })
    );
  }
}
