#! /usr/bin/env node

'use strict';

const commandLineArgs = require('command-line-args');
const fs = require('fs');
const path = require('path');
const s3 = require('s3');

const rootPath = __dirname.split('node_modules')[0];
const packagePath = rootPath + 'package.json';
const pkg = require(packagePath);
const shell = require('shelljs');
const inquirer = require('inquirer');

let bucket;



// Parse inline arguments
const optionDefinitions = [
  { name: 'bucket', alias: 'b', type: String },
  { name: 'dev', alias: 'd', type: Boolean },
  { name: 'player', type: Boolean},
  { name: 'prefix', alias: 'p', type: String },
  { name: 'root', alias: 'r', type: String },
  { name: 'target', alias: 't', type: String },
  { name: 'version', alias: 'v', type: String },
];
const options = commandLineArgs(optionDefinitions);

const destination = pkg.config.destination;
const version = pkg.version;


/**
 * Updates all instances of enplug.com found in index.html to enplug.in.
 */
function updateUrlsToStaging() {
  try {
    let indexFilePath = path.join(rootPath, 'dist/index.html');
    let offlineFilePath = path.join(rootPath, 'dist/enplug-offline-worker.js');

    fs.readFile(indexFilePath, 'utf8', (err, data) => {
      if (err) {
        return console.log('Unable to find index.html file at: ', indexFilePath);
      }
      data = data.replace(/enplug\.com/ig, 'enplug.in');
      fs.writeFile(indexFilePath, data, 'utf8', (err) => {
        if (err) {
          return console.log('Unable to replace enplug.com with enplug.in:', err);
        }
      });
    });

    fs.readFile(offlineFilePath, 'utf8', (err, data) => {
      if (err) {
        return console.log('Unable to find index.html file at: ', offlineFilePath);
      }
      data = data.replace(/enplug\.com/ig, 'enplug.in');
      fs.writeFile(offlineFilePath, data, 'utf8', (err) => {
        if (err) {
          return console.log('Unable to replace enplug.com with enplug.in:', err);
        }
      });
    });
  } catch (e) {
      console.error(e);
  }
}


/**
 * Asks user to which bucket the App should be released.
 */
function promptForBucket() {
  return inquirer.prompt({
    message: 'Which bucket do you want to use?',
    name: 's3Bucket',
    type: 'list',
    choices: pkg.config.aws.buckets
  });
}


function runBuild() {
  let command = 'enplug-build';
  if (options.dev) {
    command += ' --dev';
  }
  if (options.player) {
    command += ' --player';
  }
  console.log(`Running command: ${command}`);
  shell.exec(command, function (argument) {
    if (bucket.endsWith('enplug.in')) {
      updateUrlsToStaging();
    }
    if (!options.dev) {
      shell.exec('npm test');
    }
    let prefix = path.posix.join(destination, version);
    confirmDestination(bucket, prefix).then(function (response) {
      if (response.confirm) {
        syncDist(bucket, prefix);
      }
    });
  });
}


function confirmDestination(bucket, uploadDir) {
  let uploadPath = path.posix.join(bucket, uploadDir);
  return inquirer.prompt({
    message: 'Confirm destination: "' + uploadPath + '"',
    name: 'confirm',
    type: 'confirm'
  });
}


/**
 *
 */
function createS3Client() {
  let creds;
  const s3Options = Object.assign({}, pkg.config.aws.s3);

  try {
    creds = JSON.parse(fs.readFileSync(path.resolve(rootPath, '../aws.private.json'), 'utf8'));
  } catch (e) {
    console.error('Error finding/parsing aws.private.json');
    throw e;
  }

  if (creds.accessKeyId == null || creds.secretAccessKey == null) {
    throw new TypeError(
      'Error could not find accessKeyId or secretAccessKey in aws.private.json file.'
    );
  }

  Object.assign(s3Options, creds);

  // s3Options is what is used by the AWS SDK
  // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html#constructor-property

  return s3.createClient({
    // default options
    maxAsyncS3: 20,
    s3RetryCount: 3,
    s3RetryDelay: 1000,
    multipartUploadThreshold: 20971520,
    multipartUploadSize: 15728640,
    cacheTTL: 0,
    s3Options
  });
}


/**
 *
 * @param {*} uploader
 */
function promiseWrapUploader(uploader) {
  return new Promise(function (resolve, reject) {
    uploader.on('error', reject);
    uploader.on('end', resolve);
    uploader.on('fileUploadEnd', function (localFilePath, s3Key) {
      console.log(`Pushed ${localFilePath} to ${s3Key}`);
    });
  });
}


/**
 *
 * @param {*} bucket
 * @param {*} prefix
 */
function syncDist(bucket, prefix) {
  if (prefix.startsWith('/')) {
    prefix = prefix.substr(1);
  }


  let uploadOptions = {
    localDir: 'dist/',
    deleteRemoved: false, // keep older versions
    s3Params: {
      Bucket: bucket,
      Prefix: prefix,
      // can add options for putObject command
      // see: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
    }
  };

  let s3Client = createS3Client();

  return promiseWrapUploader(s3Client.uploadDir(uploadOptions))
    .catch(function (error) {
      console.error('There was an error during the release process!');
      console.error(error.stack);
      return null;
    })
    .then(function (result) {
      if (result == null) {
        return;
      }
      console.log('Release finished');
    });
}


if (!options['bucket']) {
  promptForBucket().then(function (answer) {
    bucket = answer.s3Bucket;
    runBuild();
  });
} else {
  runBuild();
}

