/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

// This script is used by the release.yml GitHub Actions workflow to idempotently
// create a tagged GitHub release from the generated release manifest file.

const assert = require("assert");
const fs = require("fs");

const smithy_rs_repo = {
    owner: "awslabs",
    repo: "smithy-rs",
};

async function getExistingRelease(github, tag) {
    try {
        const response = await github.rest.repos.getReleaseByTag({ ...smithy_rs_repo, tag });
        return { name: response.data.name, body: response.data.body };
    } catch (error) {
        if (error.status === 404) {
            console.info(`No existing release found with tag '${tag}'`);
            return null;
        }
        throw error;
    }
}

function loadReleaseManifest(path) {
    const releaseManifest = JSON.parse(fs.readFileSync(path));
    console.info("Release manifest: ", releaseManifest);
    assert(releaseManifest.tagName !== undefined, "release manifest must have a `tagName` field");
    assert(releaseManifest.name !== undefined, "release manifest must have a `name` field");
    assert(releaseManifest.body !== undefined, "release manifest must have a `body` field");
    assert(releaseManifest.prerelease !== undefined, "release manifest must have a `prerelease` field");
    return releaseManifest;
}

module.exports = async ({
    // GitHub API (Octokit)
    github,
    // Boolean indicating if this is a dry-run release or not
    isDryRun,
    // Release manifest file path
    releaseManifestPath,
    // The commit-like reference that we want to release (e.g. a commit SHA or a branch name)
    releaseCommitish,
}) => {
    assert(github !== undefined, "The `github` argument is required");
    assert(isDryRun !== undefined, "The `isDryRun` argument is required");
    assert(releaseManifestPath !== undefined, "The `releaseManifestPath` argument is required");
    assert(releaseCommitish !== undefined, "The `releaseCommitish` argument is required");

    console.info(`Starting GitHub release creation with isDryRun: ${isDryRun}, and releaseManifestPath: '${releaseManifestPath}'`);

    // Load the release manifest generated by the `changelogger` tool during build
    const releaseManifest = loadReleaseManifest(releaseManifestPath);

    // Idempotency check: Look up an existing GitHub release for the tag in the release manifest
    const existingRelease = await getExistingRelease(github, releaseManifest.tagName);
    if (existingRelease) {
        console.info(`Found an existing release with tag '${releaseManifest.tagName}': `, existingRelease);
        if (existingRelease.name !== releaseManifest.name || existingRelease.body !== releaseManifest.body) {
            throw Error("FATAL: Existing release does not match the release manifest!");
        } else {
            console.info("SUCCESS: Existing release matches details in the release manifest. No work needs to be done!");
        }
    } else {
        // Explicitly comparing against `false` to avoid accidental publish in the event that
        // the `isDryRun` argument wasn't passed in correctly from the GitHub Actions workflow.
        if (isDryRun === false) {
            console.info("Not a dry-run; creating a new release...");
            const response = await github.rest.repos.createRelease({
                ...smithy_rs_repo,
                tag_name: releaseManifest.tagName,
                name: releaseManifest.name,
                body: releaseManifest.body,
                prerelease: releaseManifest.prerelease,
                target_commitish: releaseCommitish,
            });
            console.info(`SUCCESS: Created release with ID: ${response.data.id}, URL: ${response.data.html_url} `);
        } else {
            console.info("SUCCESS: Exiting early since this is a dry-run release.");
        }
    }
};
