const path = require("path");
const { attestProvenance } = require("@actions/attest");
const { execSync } = require("child_process");
const ghRelease = require("gh-release");
const packageMap = require("./changeset-packagemap");

// This script is used to publish the artifacts generated by the changeset workflow.
// It is triggered by the changeset action and is responsible for creating releases to the repository.
// We do this manually because we want specific/scoped assets to be uploaded to a corresponding SCOPED releases.
// e.g. nsis artifacts should be uploaded to the nsis release, not in a zstd release.
// JSON format: https://github.com/changesets/action?tab=readme-ov-file#outputs
const publishedPackages = process.env.PUBLISHED_PACKAGES;
if (!publishedPackages) {
  console.error("PUBLISHED_PACKAGES environment variable is not set. See script for documentation.");
  process.exit(1);
}

async function run() {
  const releases = JSON.parse(publishedPackages);

  console.log("Release candidates:", releases);

  const releaseOptions = {
    draft: false,
    prerelease: false,
    yes: true,
  };

  const isCi = !!process.env.CI;
  if (!isCi) {
    console.log("CI not detected, blocking remote release. Only logging release config to console...");
  }

  for (const release of releases) {
    const { name, version } = release;
    const artifactsToUpload = packageMap[name];
    if (!artifactsToUpload) {
      throw new Error(`No artifacts found for ${name}`);
    }
    const releaseName = `${name}@${version}`;
    const artifactPath = (artifact) => path.resolve(__dirname, "../artifacts", artifact);

    const checksums = artifactsToUpload.map((name) => {
      const checksum512 = execSync(`shasum -a 512 "${artifactPath(name)}" | xxd -r -p | base64`)
        .toString()
        .trim();
      const digest256 = execSync(`shasum -a 256 "${artifactPath(name)}" | cut -d ' ' -f 1`)
        .toString()
        .trim();
      return { name, checksum512, digest256 };
    });

    const bodyText = checksums
      .map(({ name, checksum512 }) => `\`${name}\`: \`${checksum512}\``)
      .sort()
      .join("\n");
    const options = {
      ...releaseOptions,
      name: releaseName,
      tag_name: releaseName,
      body: `*checksums*\n\n${bodyText}`,
      assets: artifactsToUpload.map((artifact) => path.join("artifacts", artifact)),
    };

    if (!isCi) {
      console.log("\n\nRelease options:", options);
      continue;
    }
    // If CI, validate token is present
    const token = process.env.GITHUB_TOKEN;
    if (!token) {
      console.error("GITHUB_TOKEN environment variable is not set.");
      process.exit(1);
    }

    console.log(`Attesting artifacts for ${releaseName}...`);
    const subjects = checksums.map(({ name, digest256 }) => ({ name, digest: { sha256: digest256 } }));
    await attestProvenance({ token, subjects });
    console.log("Attestation successful for artifacts:", subjects);

    console.log(`Uploading artifacts for ${releaseName}...`);
    await new Promise((resolve, reject) =>
      ghRelease({ ...options, auth: { token } }, (err) => {
        if (err) {
          return reject(err);
        }
        console.log(`Artifacts for ${releaseName} uploaded successfully.`);
        resolve();
      })
    );
  }
}

run();
