'use strict';

const fs = require('fs');
const os = require('os');
const path = require('path');
const glob = require('glob');
const spawnSync = require('../lib/spawn-sync');
const publishRelease = require('publish-release');
const releaseNotes = require('./lib/release-notes');
const uploadToS3 = require('./lib/upload-to-s3');
const uploadLinuxPackages = require('./lib/upload-linux-packages');

const CONFIG = require('../config');

const yargs = require('yargs');
const argv = yargs
  .usage('Usage: $0 [options]')
  .help('help')
  .describe(
    'assets-path',
    'Path to the folder where all release assets are stored'
  )
  .describe(
    's3-path',
    'Indicates the S3 path in which the assets should be uploaded'
  )
  .describe(
    'create-github-release',
    'Creates a GitHub release for this build, draft if release branch or public if Nightly'
  )
  .describe(
    'linux-repo-name',
    'If specified, uploads Linux packages to the given repo name on packagecloud'
  )
  .wrap(yargs.terminalWidth()).argv;

const releaseVersion = CONFIG.computedAppVersion;
const isNightlyRelease = CONFIG.channel === 'nightly';
const assetsPath = argv.assetsPath || CONFIG.buildOutputPath;
const assetsPattern =
  '/**/*(*.exe|*.zip|*.nupkg|*.tar.gz|*.rpm|*.deb|RELEASES*|atom-api.json)';
const assets = glob.sync(assetsPattern, { root: assetsPath, nodir: true });
const bucketPath = argv.s3Path || `releases/v${releaseVersion}/`;

if (!assets || assets.length === 0) {
  console.error(`No assets found under specified path: ${assetsPath}`);
  process.exit(1);
}

async function uploadArtifacts() {
  let releaseForVersion = await releaseNotes.getRelease(
    releaseVersion,
    process.env.GITHUB_TOKEN
  );

  if (releaseForVersion.exists && !releaseForVersion.isDraft) {
    console.log(
      `Published release already exists for ${releaseVersion}, skipping upload.`
    );
    return;
  }

  console.log(
    `Uploading ${
      assets.length
    } release assets for ${releaseVersion} to S3 under '${bucketPath}'`
  );

  await uploadToS3(
    process.env.ATOM_RELEASES_S3_KEY,
    process.env.ATOM_RELEASES_S3_SECRET,
    process.env.ATOM_RELEASES_S3_BUCKET,
    bucketPath,
    assets
  );

  if (argv.linuxRepoName) {
    await uploadLinuxPackages(
      argv.linuxRepoName,
      process.env.PACKAGE_CLOUD_API_KEY,
      releaseVersion,
      assets
    );
  } else {
    console.log(
      '\nNo Linux package repo name specified, skipping Linux package upload.'
    );
  }

  const oldReleaseNotes = releaseForVersion.releaseNotes;
  if (oldReleaseNotes) {
    const oldReleaseNotesPath = path.resolve(
      os.tmpdir(),
      'OLD_RELEASE_NOTES.md'
    );
    console.log(
      `Saving existing ${releaseVersion} release notes to ${oldReleaseNotesPath}`
    );
    fs.writeFileSync(oldReleaseNotesPath, oldReleaseNotes, 'utf8');

    // This line instructs VSTS to upload the file as an artifact
    console.log(
      `##vso[artifact.upload containerfolder=OldReleaseNotes;artifactname=OldReleaseNotes;]${oldReleaseNotesPath}`
    );
  }

  if (argv.createGithubRelease) {
    console.log(`\nGenerating new release notes for ${releaseVersion}`);
    let newReleaseNotes = '';
    if (isNightlyRelease) {
      newReleaseNotes = await releaseNotes.generateForNightly(
        releaseVersion,
        process.env.GITHUB_TOKEN,
        oldReleaseNotes
      );
    } else {
      newReleaseNotes = await releaseNotes.generateForVersion(
        releaseVersion,
        process.env.GITHUB_TOKEN,
        oldReleaseNotes
      );
    }

    console.log(`New release notes:\n\n${newReleaseNotes}`);

    const releaseSha = !isNightlyRelease
      ? spawnSync('git', ['rev-parse', 'HEAD'])
          .stdout.toString()
          .trimEnd()
      : 'master'; // Nightly tags are created in atom/atom-nightly-releases so the SHA is irrelevant

    console.log(`Creating GitHub release v${releaseVersion}`);
    const release = await publishReleaseAsync({
      token: process.env.GITHUB_TOKEN,
      owner: 'atom',
      repo: !isNightlyRelease ? 'atom' : 'atom-nightly-releases',
      name: CONFIG.computedAppVersion,
      notes: newReleaseNotes,
      target_commitish: releaseSha,
      tag: `v${CONFIG.computedAppVersion}`,
      draft: !isNightlyRelease,
      prerelease: CONFIG.channel !== 'stable',
      editRelease: true,
      reuseRelease: true,
      skipIfPublished: true,
      assets
    });

    console.log('Release published successfully: ', release.html_url);
  } else {
    console.log('Skipping GitHub release creation');
  }
}

async function publishReleaseAsync(options) {
  return new Promise((resolve, reject) => {
    publishRelease(options, (err, release) => {
      if (err) {
        reject(err);
      } else {
        resolve(release);
      }
    });
  });
}

// Wrap the call the async function and catch errors from its promise because
// Node.js doesn't yet allow use of await at the script scope
uploadArtifacts().catch(err => {
  console.error('An error occurred while uploading the release:\n\n', err);
  process.exit(1);
});
