#!/usr/bin/env node --redirect-warnings=/dev/null

const { execFileSync } = require("child_process");

main();

async function main() {
  let version = process.argv[2];
  let channel = process.argv[3];
  let parts = version.split(".");

  if (
    process.argv.length != 4 ||
    parts.length != 3 ||
    parts.find((part) => isNaN(part)) != null ||
    (channel != "stable" && channel != "preview")
  ) {
    console.log("Usage: draft-release-notes <version> {stable|preview}");
    process.exit(1);
  }

  let priorVersion = [parts[0], parts[1], parts[2] - 1].join(".");
  let suffix = "";

  if (channel == "preview") {
    suffix = "-pre";
    if (parts[2] == 0) {
      priorVersion = [parts[0], parts[1] - 1, 0].join(".");
    }
  } else if (!ensureTag(`v${priorVersion}`)) {
    console.log("Copy the release notes from preview.");
    process.exit(0);
  }

  let [tag, priorTag] = [`v${version}${suffix}`, `v${priorVersion}${suffix}`];

  if (!ensureTag(tag) || !ensureTag(priorTag)) {
    console.log("Could not draft release notes, missing a tag:", tag, priorTag);
    process.exit(0);
  }

  const newCommits = getCommits(priorTag, tag);

  let releaseNotes = [];
  let missing = [];
  let skipped = [];

  for (const commit of newCommits) {
    let link = "https://github.com/zed-industries/zed/pull/" + commit.pr;
    let notes = commit.releaseNotes;
    if (commit.pr == "") {
      link = "https://github.com/zed-industries/zed/commits/" + commit.hash;
    } else if (!notes.includes("zed-industries/zed/issues")) {
      notes = notes + " ([#" + commit.pr + "](" + link + "))";
    }

    if (commit.releaseNotes == "") {
      missing.push("- MISSING " + commit.firstLine + " " + link);
    } else if (commit.releaseNotes.startsWith("- N/A")) {
      skipped.push("- N/A " + commit.firstLine + " " + link);
    } else {
      releaseNotes.push(notes);
    }
  }

  console.log(releaseNotes.join("\n") + "\n");
  console.log("<!-- ");
  console.log(missing.join("\n"));
  console.log(skipped.join("\n"));
  console.log("-->");
}

function getCommits(oldTag, newTag) {
  const pullRequestNumbers = execFileSync(
    "git",
    ["log", `${oldTag}..${newTag}`, "--format=DIVIDER\n%H|||%B"],
    { encoding: "utf8" },
  )
    .replace(/\r\n/g, "\n")
    .split("DIVIDER\n")
    .filter((commit) => commit.length > 0)
    .map((commit) => {
      let [hash, firstLine] = commit.split("\n")[0].split("|||");
      let cherryPick = firstLine.match(/\(cherry-pick #([0-9]+)\)/)?.[1] || "";
      let pr = firstLine.match(/\(#(\d+)\)$/)?.[1] || "";
      let releaseNotes = (commit.split(/Release notes:.*\n/i)[1] || "")
        .split("\n\n")[0]
        .trim()
        .replace(/\n(?![\n-])/g, " ");

      if (releaseNotes.includes("<public_issue_number_if_exists>")) {
        releaseNotes = "";
      }

      return {
        hash,
        pr,
        cherryPick,
        releaseNotes,
        firstLine,
      };
    });

  return pullRequestNumbers;
}

function ensureTag(tag) {
  try {
    execFileSync("git", ["rev-parse", "--verify", tag]);
    return true;
  } catch (e) {
    try {
      execFileSync("git"[("fetch", "origin", "--shallow-exclude", tag)]);
      execFileSync("git"[("fetch", "origin", "--deepen", "1")]);
      return true;
    } catch (e) {
      return false;
    }
  }
}
