'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

var _serializeError;

function _load_serializeError() {
  return _serializeError = _interopRequireDefault(require('serialize-error'));
}

var _ramda;

function _load_ramda() {
  return _ramda = _interopRequireDefault(require('ramda'));
}

var _commander;

function _load_commander() {
  return _commander = _interopRequireDefault(require('commander'));
}

var _chalk;

function _load_chalk() {
  return _chalk = _interopRequireDefault(require('chalk'));
}

var _consumer;

function _load_consumer() {
  return _consumer = require('../api/consumer');
}

var _defaultErrorHandler;

function _load_defaultErrorHandler() {
  return _defaultErrorHandler = _interopRequireDefault(require('./default-error-handler'));
}

var _utils;

function _load_utils() {
  return _utils = require('../utils');
}

var _loader;

function _load_loader() {
  return _loader = _interopRequireDefault(require('./loader'));
}

var _logger;

function _load_logger() {
  return _logger = _interopRequireDefault(require('../logger/logger'));
}

var _analytics;

function _load_analytics() {
  return _analytics = require('../analytics/analytics');
}

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function logAndExit(msg, commandName) {
  process.stdout.write(`${msg}\n`, function () {
    return (_logger || _load_logger()).default.exitAfterFlush(0, commandName);
  });
}

function logErrAndExit(msg, commandName) {
  if (msg.code) throw msg;
  console.error(msg); // eslint-disable-line
  (_logger || _load_logger()).default.exitAfterFlush(1, commandName);
}

function parseSubcommandFromArgs(args) {
  if (typeof (0, (_utils || _load_utils()).first)(args) === 'string') return (0, (_utils || _load_utils()).first)(args);
  return null;
}

function parseCommandName(commandName) {
  if (!commandName) return '';
  return (0, (_utils || _load_utils()).first)(commandName.split(' '));
}

function getOpts(c, opts) {
  var options = {};

  opts.forEach(function (_ref) {
    var _ref2 = _slicedToArray(_ref, 2),
        name = _ref2[1];

    var parsedName = parseCommandName(name);
    var camelCaseName = (0, (_utils || _load_utils()).camelCase)(parsedName);

    if (name.startsWith('no-')) {
      // from commander help: "Note that multi-word options starting with --no prefix negate the boolean value of the following word. For example, --no-sauce sets the value of program.sauce to false."
      // we don't want this feature, so we do the opposite action.
      options[camelCaseName] = !c[(0, (_utils || _load_utils()).camelCase)(parsedName.replace('no-', ''))];
    } else {
      options[camelCaseName] = c[camelCaseName];
    }
  });

  return options;
}

function execAction(command, concrete, args) {
  // $FlowFixMe
  var flags = getOpts(concrete, command.opts);
  var relevantArgs = args.slice(0, args.length - 1);
  var packageManagerArgs = concrete.parent.packageManagerArgs;
  (_analytics || _load_analytics()).Analytics.init(concrete.name(), flags, relevantArgs, concrete.parent._version);
  (_logger || _load_logger()).default.info(`[*] started a new command: "${command.name}" with the following data:`, {
    args: relevantArgs,
    flags,
    packageManagerArgs
  });
  if (command.loader) {
    (_loader || _load_loader()).default.on();
  }
  var migrateWrapper = function migrateWrapper(run) {
    if (run) {
      (_logger || _load_logger()).default.info('Checking if a migration is needed');
      return (0, (_consumer || _load_consumer()).migrate)(null, false);
    }
    return Promise.resolve();
  };

  migrateWrapper(command.migration).then(function () {
    return command.action(relevantArgs, flags, packageManagerArgs).then(function (data) {
      (_loader || _load_loader()).default.off();
      return logAndExit(command.report(data), command.name);
    });
  }).catch(function (err) {
    (_logger || _load_logger()).default.error(`got an error from command ${command.name}: ${err}. Error serialized: ${JSON.stringify(err, Object.getOwnPropertyNames(err))}`);
    (_loader || _load_loader()).default.off();
    var errorHandled = (0, (_defaultErrorHandler || _load_defaultErrorHandler()).default)(err) || command.handleError(err);

    if (command.private) return serializeErrAndExit(err, command.name);
    if (!command.private && errorHandled) return logErrAndExit(errorHandled, command.name);
    return logErrAndExit(err, command.name);
  });
}

function serializeErrAndExit(err, commandName) {
  process.stderr.write((0, (_utils || _load_utils()).packCommand)((0, (_utils || _load_utils()).buildCommandMessage)((0, (_serializeError || _load_serializeError()).default)(err)), false));
  var code = err.code && (0, (_utils || _load_utils()).isNumeric)(err.code) ? err.code : 1;
  return (_logger || _load_logger()).default.exitAfterFlush(code, commandName);
}

// @TODO add help for subcommands
function registerAction(command, concrete) {
  concrete.action(function () {
    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    if (!(0, (_utils || _load_utils()).empty)(command.commands)) {
      var subcommandName = parseSubcommandFromArgs(args);
      var subcommand = command.commands.find(function (cmd) {
        return subcommandName === (parseCommandName(cmd.name) || cmd.alias);
      });

      args.shift();
      if (subcommand) return execAction(subcommand, concrete, args);
    }

    return execAction(command, concrete, args);
  });
}

function createOptStr(alias, name) {
  if (alias) {
    return `-${alias}, --${name}`;
  }
  return `--${name}`;
}

function register(command, commanderCmd) {
  // $FlowFixMe
  var concrete = commanderCmd.command(command.name, null, { noHelp: command.private }).description(command.description).alias(command.alias);

  command.opts.forEach(function (_ref3) {
    var _ref4 = _slicedToArray(_ref3, 3),
        alias = _ref4[0],
        name = _ref4[1],
        description = _ref4[2];

    concrete.option(createOptStr(alias, name), description);
  });

  // attach skip-update to all commands
  concrete.option('--skip-update', 'Skips auto updates');

  if (command.commands) {
    command.commands.forEach(function (nestedCmd) {
      register(nestedCmd, concrete);
    });
  }

  return registerAction(command, concrete);
}

var CommandRegistrar = function () {
  _createClass(CommandRegistrar, [{
    key: 'registerBaseCommand',
    value: function registerBaseCommand() {
      (_commander || _load_commander()).default.version(this.version).usage(this.usage).option('--skip-update', 'Skips auto updates for a command').description(this.description);
    }
  }]);

  function CommandRegistrar(usage, description, version, commands, extensionsCommands) {
    _classCallCheck(this, CommandRegistrar);

    this.usage = usage;
    this.description = description;
    this.version = version;
    this.commands = commands;
    this.extensionsCommands = extensionsCommands;
  }

  _createClass(CommandRegistrar, [{
    key: 'registerExtenstionsCommands',
    value: function registerExtenstionsCommands() {
      this.extensionsCommands.forEach(function (cmd) {
        return register(cmd, (_commander || _load_commander()).default);
      });
    }
  }, {
    key: 'registerCommands',
    value: function registerCommands() {
      this.commands.forEach(function (cmd) {
        return register(cmd, (_commander || _load_commander()).default);
      });
    }
  }, {
    key: 'printHelp',
    value: function printHelp() {
      var helpTemplateGenerator = require('./templates/help');
      console.log(helpTemplateGenerator(this.extensionsCommands));
      return this;
    }
  }, {
    key: 'outputHelp',
    value: function outputHelp() {
      var args = process.argv.slice(2);
      if (!args.length) {
        // @TODO replace back to commander help and override help method
        // commander.help();
        this.printHelp();
        return this;
      }

      var subcommand = args[0];
      var cmdList = this.commands.map(function (cmd) {
        return (0, (_utils || _load_utils()).first)(cmd.name.split(' '));
      });
      var extensionsCmdList = this.extensionsCommands.map(function (cmd) {
        return (0, (_utils || _load_utils()).first)(cmd.name.split(' '));
      });
      var aliasList = this.commands.map(function (cmd) {
        return (0, (_utils || _load_utils()).first)(cmd.alias.split(' '));
      });

      if (cmdList.indexOf(subcommand) === -1 && extensionsCmdList.indexOf(subcommand) === -1 && aliasList.indexOf(subcommand) === -1 && subcommand !== '-V' && subcommand !== '--version') {
        process.stdout.write((_chalk || _load_chalk()).default.yellow(`warning: '${(_chalk || _load_chalk()).default.bold(subcommand)}' is not a valid command.\nsee 'bit --help' for additional information.\n`));
        return this;
      }

      return this;
    }
  }, {
    key: 'run',
    value: function run() {
      var args = process.argv.slice(2);
      if (args[0] && (args[0] === '--help' || args[0] === '-h')) {
        this.printHelp();
        return this;
      }

      var _R$splitWhen = (_ramda || _load_ramda()).default.splitWhen((_ramda || _load_ramda()).default.equals('--'), process.argv),
          _R$splitWhen2 = _slicedToArray(_R$splitWhen, 2),
          params = _R$splitWhen2[0],
          packageManagerArgs = _R$splitWhen2[1];

      packageManagerArgs.shift(); // the first item, '--', is not needed.
      this.registerBaseCommand();
      this.registerCommands();
      this.registerExtenstionsCommands();
      this.outputHelp();
      (_commander || _load_commander()).default.packageManagerArgs = packageManagerArgs; // it's a hack, I didn't find a better way to pass them
      (_commander || _load_commander()).default.parse(params);

      return this;
    }
  }]);

  return CommandRegistrar;
}();

exports.default = CommandRegistrar;