'use strict';

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

var _regenerator;

function _load_regenerator() {
  return _regenerator = _interopRequireDefault(require('babel-runtime/regenerator'));
}

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 _ramda;

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

var _util;

function _load_util() {
  return _util = require('util');
}

var _constants;

function _load_constants() {
  return _constants = require('../constants');
}

var _logger;

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

var _exceptions;

function _load_exceptions() {
  return _exceptions = _interopRequireWildcard(require('./exceptions'));
}

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

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

function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }

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

/* 
  * Setting up block level variable to store class state
  * set's to null by default.
*/
var instance = null;

/**
 * A class which manage all the hooks
 * This is a singelton class which expose getInstance method
 * This class used for register new hooks, actions for existing hooks and trigger hooks
 */

var HooksManager = function () {
  function HooksManager() {
    _classCallCheck(this, HooksManager);

    this.hooks = new Map();

    if (!instance) {
      instance = this;
    }

    return instance;
  }

  /**
   * Initialize the default hooks
   */


  _createClass(HooksManager, [{
    key: 'registerNewHook',


    /**
     * register new hook name
     * @param {string} hookName
     * @param {boolean} throwIfExist - whether to throw an error if the hook name already exists
     * @return {boolean} whether the hook has been registerd
     */
    value: function registerNewHook(hookName) {
      var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var throwIfExist = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;

      if (this.hooks.has(hookName)) {
        var contextMsg = context.extension ? `from ${context.extension}` : '';
        (_logger || _load_logger()).default.warn(`trying to register an already existing hook ${hookName} ${contextMsg}`);
        if (throwIfExist) {
          throw new (_exceptions || _load_exceptions()).HookAlreadyExists(hookName);
        }
        return false;
      }
      this.hooks.set(hookName, []);
      return true;
    }

    /**
     * Register action to an existing hook
     * @param {string} hookName - hook to register action to
     * @param {HookAction} hookAction - The action to register to the hook
     * @param {boolean} throwIfNotExist - whether to throw an exception in case the hook doesn't exists
     * @return {boolean} whether the action has been registerd successfully
     */

  }, {
    key: 'registerActionToHook',
    value: function registerActionToHook(hookName, hookAction) {
      var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      var throwIfNotExist = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;

      if (!this.hooks.has(hookName)) {
        var contextMsg = context.extension ? `from ${context.extension}` : '';
        (_logger || _load_logger()).default.warn(`trying to register to a non existing hook ${hookName} ${contextMsg}`);
        if (throwIfNotExist) {
          throw new (_exceptions || _load_exceptions()).HookNotExists(hookName);
        }
        return false;
      }
      this.hooks.get(hookName).push(hookAction);
      return true;
    }

    /**
     * Trigger a hook - run all the actions registerd to this hook
     * The actions will be run in parallel and the errors will be aggregated
     * @param {string} hookName - The hook name to trigger
     * @return {HookFailures} Aggregated errors of the actions failures
     */

  }, {
    key: 'triggerHook',
    value: function () {
      var _ref = _asyncToGenerator( /*#__PURE__*/(_regenerator || _load_regenerator()).default.mark(function _callee(hookName) {
        var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        var headers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
        var resultErrors, actions, actionsP;
        return (_regenerator || _load_regenerator()).default.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                resultErrors = [];

                if (this.hooks.has(hookName)) {
                  _context.next = 4;
                  break;
                }

                (_logger || _load_logger()).default.warn(`trying to trigger a non existing hook ${hookName}`);
                throw new (_exceptions || _load_exceptions()).HookNotExists(hookName);

              case 4:
                (_logger || _load_logger()).default.info(`triggering hook ${hookName} with args:\n ${_stringifyIfNeeded(_stripArgs(args))} \n and headers \n ${_stringifyIfNeeded(_stripHeaders(headers))}`);
                actions = this.hooks.get(hookName);
                actionsP = actions.map(function (action) {
                  // Catch errors in order to aggregate them
                  // Wrap in a promise in case the action doesn't return a promise
                  return Promise.resolve().then(function () {
                    (_logger || _load_logger()).default.info(`running action ${action.name} on hook ${hookName}`);
                    return action.run(args, headers);
                  }).catch(function (e) {
                    (_logger || _load_logger()).default.error(`running action ${action.name} on hook ${hookName} failed, err:`);
                    (_logger || _load_logger()).default.error(e);
                    resultErrors.push({ [action.name]: e });
                  });
                });
                _context.next = 9;
                return Promise.all(actionsP);

              case 9:
                return _context.abrupt('return', resultErrors);

              case 10:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

      function triggerHook(_x7) {
        return _ref.apply(this, arguments);
      }

      return triggerHook;
    }()
  }], [{
    key: 'init',
    value: function init() {
      var self = new HooksManager();
      (_constants || _load_constants()).HOOKS_NAMES.forEach(function (hookName) {
        return self.hooks.set(hookName, []);
      });
    }

    /**
     * Get the instance of the HooksManager
     * @return {HooksManager} instance of the HooksManager
     *
     */

  }, {
    key: 'getInstance',
    value: function getInstance() {
      return instance;
    }
  }]);

  return HooksManager;
}();

exports.default = HooksManager;


function _stringifyIfNeeded(val) {
  return typeof val === 'string' ? val : (0, (_util || _load_util()).inspect)(val, { depth: null });
}

/**
 * Remove some data from the logs (because it's too verbose or because it's sensitive)
 * @param {Object} args
 */
function _stripArgs(args) {
  // Create deep clone
  var res = (_ramda || _load_ramda()).default.clone(args);
  if (res.componentObjects) {
    res.componentObjects = res.componentObjects.length;
  }
  return res;
}

/**
 * Remove some data from the logs (because it's too verbose or because it's sensitive)
 * @param {Object} headers
 */
function _stripHeaders(headers) {
  // Create deep clone
  var res = (_ramda || _load_ramda()).default.clone(headers);
  if (res.context && res.context.pubSshKey) {
    var key = res.context.pubSshKey;
    res.context.pubSshKey = `last 70 characters: ${key.substr(key.length - 70)}`;
  }
  return res;
}