"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

function _serializeError() {
  const data = _interopRequireDefault(require("serialize-error"));

  _serializeError = function () {
    return data;
  };

  return data;
}

function _ramda() {
  const data = _interopRequireDefault(require("ramda"));

  _ramda = function () {
    return data;
  };

  return data;
}

function _commander() {
  const data = _interopRequireDefault(require("commander"));

  _commander = function () {
    return data;
  };

  return data;
}

function _chalk() {
  const data = _interopRequireDefault(require("chalk"));

  _chalk = function () {
    return data;
  };

  return data;
}

function _consumer() {
  const data = require("../api/consumer");

  _consumer = function () {
    return data;
  };

  return data;
}

function _defaultErrorHandler() {
  const data = _interopRequireDefault(require("./default-error-handler"));

  _defaultErrorHandler = function () {
    return data;
  };

  return data;
}

function _utils() {
  const data = require("../utils");

  _utils = function () {
    return data;
  };

  return data;
}

function _loader() {
  const data = _interopRequireDefault(require("./loader"));

  _loader = function () {
    return data;
  };

  return data;
}

function _logger() {
  const data = _interopRequireDefault(require("../logger/logger"));

  _logger = function () {
    return data;
  };

  return data;
}

function _analytics() {
  const data = require("../analytics/analytics");

  _analytics = function () {
    return data;
  };

  return data;
}

function logAndExit(msg, commandName, code = 0) {
  process.stdout.write(`${msg}\n`, () => _logger().default.exitAfterFlush(code, commandName));
}

function logErrAndExit(msg, commandName) {
  if (msg.code) throw msg;
  console.error(msg); // eslint-disable-line

  _logger().default.exitAfterFlush(1, commandName);
}

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

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

function getOpts(c, opts) {
  const options = {};
  opts.forEach(([, name]) => {
    const parsedName = parseCommandName(name);
    const camelCaseName = (0, _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().camelCase)(parsedName.replace('no-', ''))];
    } else {
      options[camelCaseName] = c[camelCaseName];
    }
  });
  return options;
}

function execAction(command, concrete, args) {
  // $FlowFixMe
  const flags = getOpts(concrete, command.opts);
  const relevantArgs = args.slice(0, args.length - 1);
  const packageManagerArgs = concrete.parent.packageManagerArgs;

  _analytics().Analytics.init(concrete.name(), flags, relevantArgs, concrete.parent._version);

  _logger().default.info(`[*] started a new command: "${command.name}" with the following data:`, {
    args: relevantArgs,
    flags,
    packageManagerArgs
  });

  if (command.loader) {
    _loader().default.on();
  }

  const migrateWrapper = run => {
    if (run) {
      _logger().default.debug('Checking if a migration is needed');

      return (0, _consumer().migrate)(null, false);
    }

    return Promise.resolve();
  };

  migrateWrapper(command.migration).then(() => {
    return command.action(relevantArgs, flags, packageManagerArgs).then(res => {
      _loader().default.off();

      let data = res;
      let code = 0;

      if (res && res.__code !== undefined) {
        data = res.data;
        code = res.__code;
      }

      return logAndExit(command.report(data, relevantArgs, flags), command.name, code);
    });
  }).catch(err => {
    _logger().default.error(`got an error from command ${command.name}: ${err}. Error serialized: ${JSON.stringify(err, Object.getOwnPropertyNames(err))}`);

    _loader().default.off();

    const errorHandled = (0, _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().packCommand)((0, _utils().buildCommandMessage)((0, _serializeError().default)(err)), false));
  const code = err.code && (0, _utils().isNumeric)(err.code) ? err.code : 1;
  return _logger().default.exitAfterFlush(code, commandName);
} // @TODO add help for subcommands


function registerAction(command, concrete) {
  concrete.action((...args) => {
    if (!(0, _utils().empty)(command.commands)) {
      const subcommandName = parseSubcommandFromArgs(args);
      const subcommand = command.commands.find(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
  const concrete = commanderCmd.command(command.name, null, {
    noHelp: command.private
  }).description(command.description).alias(command.alias);
  command.opts.forEach(([alias, name, description]) => {
    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(nestedCmd => {
      register(nestedCmd, concrete);
    });
  }

  return registerAction(command, concrete);
}

class CommandRegistrar {
  registerBaseCommand() {
    _commander().default.version(this.version).usage(this.usage).option('--skip-update', 'Skips auto updates for a command').description(this.description);
  }

  constructor(usage, description, version, commands, extensionsCommands) {
    this.usage = usage;
    this.description = description;
    this.version = version;
    this.commands = commands;
    this.extensionsCommands = extensionsCommands;
  }

  registerExtenstionsCommands() {
    this.extensionsCommands.forEach(cmd => register(cmd, _commander().default));
  }

  registerCommands() {
    this.commands.forEach(cmd => register(cmd, _commander().default));
  }

  printHelp() {
    const helpTemplateGenerator = require('./templates/help');

    console.log(helpTemplateGenerator(this.extensionsCommands));
    return this;
  }

  outputHelp() {
    const args = process.argv.slice(2);

    if (!args.length) {
      // @TODO replace back to commander help and override help method
      // commander.help();
      this.printHelp();
      return this;
    }

    const subcommand = args[0];
    const cmdList = this.commands.map(cmd => (0, _utils().first)(cmd.name.split(' ')));
    const extensionsCmdList = this.extensionsCommands.map(cmd => (0, _utils().first)(cmd.name.split(' ')));
    const aliasList = this.commands.map(cmd => (0, _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().default.yellow(`warning: '${_chalk().default.bold(subcommand)}' is not a valid command.\nsee 'bit --help' for additional information.\n`));
      return this;
    }

    return this;
  }

  run() {
    const args = process.argv.slice(2);

    if (args[0] && (args[0] === '--help' || args[0] === '-h')) {
      this.printHelp();
      return this;
    }

    const [params, packageManagerArgs] = _ramda().default.splitWhen(_ramda().default.equals('--'), process.argv);

    packageManagerArgs.shift(); // the first item, '--', is not needed.

    this.registerBaseCommand();
    this.registerCommands();
    this.registerExtenstionsCommands();
    this.outputHelp();
    _commander().default.packageManagerArgs = packageManagerArgs; // it's a hack, I didn't find a better way to pass them

    _commander().default.parse(params);

    return this;
  }

}

exports.default = CommandRegistrar;