#!/usr/bin/env -S deno run --allow-env --allow-read --allow-write --allow-run=git,cargo
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import $ from "https://deno.land/x/dax@0.32.0/mod.ts";

if (Deno.args.length === 0) {
  $.log(
    "Usage: build_bench [-v] [--profile release|debug] commit1 [commit2 [comment3...]]",
  );
  Deno.exit(1);
}

const args = Deno.args.slice();
let verbose = false;
if (args[0] == "-v") {
  args.shift();
  verbose = true;
}

let profile = "release";
if (args[0] == "--profile") {
  args.shift();
  profile = args.shift();
}

function exit(msg: string) {
  $.logError(msg);
  Deno.exit(1);
}

// Make sure the .git dir exists
const gitDir = Deno.cwd() + "/.git";
await Deno.stat(gitDir);

async function runCommand(human: string, cmd) {
  if (verbose) {
    const out = await cmd.noThrow();
    if (out.code != 0) {
      exit(human);
    }
  } else {
    const out = await cmd.stdout("piped").stderr("piped").noThrow();
    if (out.code != 0) {
      $.logLight("stdout");
      $.logGroup();
      $.log(out.stdout);
      $.logGroupEnd();
      $.logLight("stderr");
      $.logGroup();
      $.log(out.stderr);
      $.logGroupEnd();
      exit(human);
    }
  }
}

async function buildGitCommit(progress, commit) {
  const tempDir = $.path(await Deno.makeTempDir());

  const gitInfo =
    await $`git log --pretty=oneline --abbrev-commit -n1 ${commit}`.stdout(
      "piped",
    ).stderr("piped").noThrow();
  if (gitInfo.code != 0) {
    $.log(gitInfo.stdout);
    $.log(gitInfo.stderr);
    exit(`Failed to get git info for commit ${commit}`);
  }

  const hash = gitInfo.stdout.split(" ")[0];
  progress.message(`${commit} is ${hash}`);

  progress.message(`clone ${hash}`);
  await runCommand(
    `Failed to clone commit ${commit}`,
    $`git clone ${gitDir} ${tempDir}`,
  );

  progress.message(`reset ${hash}`);
  await runCommand(
    `Failed to reset commit ${commit}`,
    $`git reset --hard ${hash}`.cwd(tempDir),
  );

  progress.message(`build ${hash} (please wait)`);
  const now = Date.now();
  const interval = setInterval(() => {
    const elapsed = Math.round((Date.now() - now) / 1000);
    progress.message(`build ${hash} (${elapsed}s)`);
  }, 100);
  try {
    if (profile === "debug") {
      await runCommand(
        `Failed to build commit ${commit}`,
        $`cargo build`.cwd(tempDir),
      );
    } else {
      await runCommand(
        `Failed to build commit ${commit}`,
        $`cargo build --profile ${profile}`.cwd(tempDir),
      );
    }
  } finally {
    clearInterval(interval);
  }
  const elapsed = Math.round((Date.now() - now) / 1000);

  let file;
  if (profile === "release") {
    file = `deno-${hash}`;
  } else {
    file = `deno-${profile}-${hash}`;
  }
  progress.message(`copy ${hash}`);
  await tempDir.join("target").join(profile).join("deno").copyFile(file);

  progress.message(`cleanup ${hash}`);
  await tempDir.remove({ recursive: true });

  progress.message("done");
  $.log(`Built ./${file} (${commit}) in ${elapsed}s: ${gitInfo.stdout}`);
}

const promises = [];
for (const arg of args) {
  if (verbose) {
    promises.push(buildGitCommit({ message() {} }, arg));
  } else {
    const progress = $.progress(`${arg}`);
    promises.push(progress.with(async () => {
      await buildGitCommit(progress, arg);
    }));
  }
}

await Promise.all(promises);