import _ from 'underscore';

/*
* Callback hooks provide an easy way to add extra steps to common operations.
* @namespace RocketChat.promises
*/

RocketChat.promises = {};


/*
* Callback priorities
*/

RocketChat.promises.priority = {
	HIGH: -1000,
	MEDIUM: 0,
	LOW: 1000,
};

const getHook = (hookName) => RocketChat.promises[hookName] || [];

/*
* Add a callback function to a hook
* @param {String} hook - The name of the hook
* @param {Function} callback - The callback function
*/

RocketChat.promises.add = function(hook, callback, p = RocketChat.promises.priority.MEDIUM, id) {
	callback.priority = _.isNumber(p) ? p : RocketChat.promises.priority.MEDIUM;
	callback.id = id || Random.id();
	RocketChat.promises[hook] = getHook(hook);
	if (RocketChat.promises[hook].find((cb) => cb.id === callback.id)) {
		return;
	}
	RocketChat.promises[hook].push(callback);
	RocketChat.promises[hook] = _.sortBy(RocketChat.promises[hook], (callback) => callback.priority || RocketChat.promises.priority.MEDIUM);
};


/*
* Remove a callback from a hook
* @param {string} hook - The name of the hook
* @param {string} id - The callback's id
*/

RocketChat.promises.remove = function(hook, id) {
	RocketChat.promises[hook] = getHook(hook).filter((callback) => callback.id !== id);
};


/*
* Successively run all of a hook's callbacks on an item
* @param {String} hook - The name of the hook
* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks
* @param {Object} [constant] - An optional constant that will be passed along to each callback
* @returns {Object} Returns the item after it's been through all the callbacks for this hook
*/

RocketChat.promises.run = function(hook, item, constant) {
	const callbacks = RocketChat.promises[hook];
	if (callbacks == null || callbacks.length === 0) {
		return Promise.resolve(item);
	}
	return callbacks.reduce((previousPromise, callback) => previousPromise.then((result) => callback(result, constant)), Promise.resolve(item));
};


/*
* Successively run all of a hook's callbacks on an item, in async mode (only works on server)
* @param {String} hook - The name of the hook
* @param {Object} item - The post, comment, modifier, etc. on which to run the callbacks
* @param {Object} [constant] - An optional constant that will be passed along to each callback
*/

RocketChat.promises.runAsync = function(hook, item, constant) {
	const callbacks = RocketChat.promises[hook];
	if (!Meteor.isServer || callbacks == null || callbacks.length === 0) {
		return item;
	}
	Meteor.defer(() => callbacks.forEach((callback) => callback(item, constant)));
};
