'use strict';

var parseArgs = require('./args');
var debug = require('debug')('clite');
var getSettings = require('./settings');
var path = require('path');
var findRoot = require('./find-root');
var read = require('./read-stdin');

module.exports = function (config, root) {
  var paths = (root ? [path.join(root, 'node_modules')] : module.parent.parent.paths).slice(0); // copy
  var settings = getSettings(config);
  var help = '';

  return Promise.all([parseArgs(process.argv, config), read(), findRoot(paths).then(function (res) {
    return root = res;
  })]).then(function (res) {
    var args = res[0].args;
    help = res[0].help;
    var body = res[1];
    if (!args.$_ && !settings.commands._ && !body) {
      var e = new Error('BAD_ARGS');
      e.code = 'BAD_ARGS';
      return Promise.reject(e);
    }

    return loadCommand(root, args, body, settings);
  }).then(function (res) {
    require('./update')({ root: root });
    /* istanbul ignore if */
    if (!settings.return && res != undefined) {
      // jshint ignore:line
      return console.log(res);
    }

    return res;
  }).catch(function (e) {
    var promise = Promise.reject(e);

    if (e.code === 'BAD_ARGS') {
      // show either the configured help or the automatically generated help
      promise = loadCommand(root, {
        $_: ':::./help', help: true
      }, null, settings).then(function (res) {
        var error = new Error(res); // based on loaded help
        error.code = 'BAD_ARGS';
        return Promise.reject(error);
      }).catch(function (error) {
        if (error.code === 'NO_HELP_CONFIGURED') {
          error.code = 'BAD_ARGS';
          error.message = help.trim();
        }

        if (error.code === 'BAD_ARGS' && e.message !== 'BAD_ARGS') {
          error.message = e.message + '\n\n' + error.message;
        }

        return Promise.reject(error);
      });
    }

    return promise.catch(function (e) {
      /* istanbul ignore if */
      if (!settings.return) {
        debug(e.stack);
        console.error(e.message);
        return process.exit(1);
      }
      return Promise.reject(e);
    });
  });
};

function loadCommand(root, args, body, settings) {
  var filename = args.$_;

  if (!filename) {
    var error = new Error('No command given');
    error.code = 'NO_COMMAND';
    return Promise.reject(error);
  }

  var internal = filename.indexOf(':::') === 0;
  if (internal) {
    filename = filename.slice(3);
    args.root = root;
    debug('loading internal module: %s', filename);
  } else {
    filename = path.resolve(root, filename);
    debug('loading %s', filename);
  }

  return new Promise(function (resolve) {
    resolve(require(filename));
  }).catch(function (e) {
    e.message = 'Fatal: command failed to load "' + filename + '"';
    return Promise.reject(e);
  }).then(function (res) {
    return res(args, settings, body);
  }).then(function (res) {
    /* istanbul ignore if */
    if (!settings.return && internal) {
      console.log(res);
      // internal commands always immediately exit
      return process.exit(0);
    }
    return res;
  });
}