const fs = require("fs");
const util = require("util");
const exec = util.promisify(require("child_process").exec);

const excalidrawDir = `${__dirname}/../packages/excalidraw`;
const excalidrawPackage = `${excalidrawDir}/package.json`;
const pkg = require(excalidrawPackage);
const lastVersion = pkg.version;
const existingChangeLog = fs.readFileSync(
  `${excalidrawDir}/CHANGELOG.md`,
  "utf8",
);

const supportedTypes = ["feat", "fix", "style", "refactor", "perf", "build"];
const headerForType = {
  feat: "Features",
  fix: "Fixes",
  style: "Styles",
  refactor: " Refactor",
  perf: "Performance",
  build: "Build",
};
const badCommits = [];
const getCommitHashForLastVersion = async () => {
  try {
    const commitMessage = `"release @excalidraw/excalidraw@${lastVersion}"`;
    const { stdout } = await exec(
      `git log --format=format:"%H" --grep=${commitMessage}`,
    );
    return stdout;
  } catch (error) {
    console.error(error);
  }
};

const getLibraryCommitsSinceLastRelease = async () => {
  const commitHash = await getCommitHashForLastVersion();
  const { stdout } = await exec(
    `git log --pretty=format:%s ${commitHash}...master`,
  );
  const commitsSinceLastRelease = stdout.split("\n");
  const commitList = {};
  supportedTypes.forEach((type) => {
    commitList[type] = [];
  });

  commitsSinceLastRelease.forEach((commit) => {
    const indexOfColon = commit.indexOf(":");
    const type = commit.slice(0, indexOfColon);
    if (!supportedTypes.includes(type)) {
      return;
    }
    const messageWithoutType = commit.slice(indexOfColon + 1).trim();
    const messageWithCapitalizeFirst =
      messageWithoutType.charAt(0).toUpperCase() + messageWithoutType.slice(1);
    const prMatch = commit.match(/\(#([0-9]*)\)/);
    if (prMatch) {
      const prNumber = prMatch[1];

      // return if the changelog already contains the pr number which would happen for package updates
      if (existingChangeLog.includes(prNumber)) {
        return;
      }
      const prMarkdown = `[#${prNumber}](https://github.com/excalidraw/excalidraw/pull/${prNumber})`;
      const messageWithPRLink = messageWithCapitalizeFirst.replace(
        /\(#[0-9]*\)/,
        prMarkdown,
      );
      commitList[type].push(messageWithPRLink);
    } else {
      badCommits.push(commit);
      commitList[type].push(messageWithCapitalizeFirst);
    }
  });
  console.info("Bad commits:", badCommits);
  return commitList;
};

const updateChangelog = async (nextVersion) => {
  const commitList = await getLibraryCommitsSinceLastRelease();
  let changelogForLibrary =
    "## Excalidraw Library\n\n**_This section lists the updates made to the excalidraw library and will not affect the integration._**\n\n";
  supportedTypes.forEach((type) => {
    if (commitList[type].length) {
      changelogForLibrary += `### ${headerForType[type]}\n\n`;
      const commits = commitList[type];
      commits.forEach((commit) => {
        changelogForLibrary += `- ${commit}\n\n`;
      });
    }
  });
  changelogForLibrary += "---\n";
  const lastVersionIndex = existingChangeLog.indexOf(`## ${lastVersion}`);
  let updatedContent =
    existingChangeLog.slice(0, lastVersionIndex) +
    changelogForLibrary +
    existingChangeLog.slice(lastVersionIndex);
  const currentDate = new Date().toISOString().slice(0, 10);
  const newVersion = `## ${nextVersion} (${currentDate})`;
  updatedContent = updatedContent.replace(`## Unreleased`, newVersion);
  fs.writeFileSync(`${excalidrawDir}/CHANGELOG.md`, updatedContent, "utf8");
};

module.exports = updateChangelog;
