const Promise = require('bluebird');
const _ = require('lodash');

function Topics(mediator) {
  this.mediator = mediator;
  this.subscriptions = {};
}

/**
 * Sets the prefix configuration for this instance, which will be part of the name
 * of the handled topics.
 * @param  {String} prefix
 * @return {Topics}        returns self for chaining
 */
Topics.prototype.prefix = function(prefix) {
  this.prefix = prefix;
  return this;
};

/**
 * Sets the entity configuration for this instance, which will be part of the name
 * of the handled topics.
 * This property is present as a convenience so it can be accessed by handlers via `this.entity`
 *
 * @param  {String} entity
 * @return {Topics}        returns self for chaining
 */
Topics.prototype.entity = function(entity) {
  this.entity = entity;
  return this;
};

/**
 * Internal function to add a subscription to the internal collection
 * @param {string}   topic topic id
 * @param {Function} fn    handler for the topic
 */
Topics.prototype.addSubscription = function(topic, fn) {
  this.subscriptions[topic] = this.mediator.subscribe(topic, fn);
};

/**
 * Builds a topic name out of the configured {@link prefix} and {@link entity}
 * @param  {String} topicName The name of the sub-topic to build
 * @param  {String} prefix    An optional prefix to the final topic, i.e. 'done'
 * @return {String}           The complete topic name,
 *                              i.e. {prefix}:{this.prefix}:{this.entity}:{topicName}
 */
Topics.prototype.getTopic = function(topicName, prefix) {
  // create, done => done:wfm:user:create
  var parts = _.compact([this.prefix, this.entity, topicName]);
  if (prefix) {
    parts.unshift(prefix);
  }
  return parts.join(':');
};

/**
 * Internal function to wrap a `on` handler in a promise that will publish to the
 * related 'done:' and 'error:' topics
 * @param  {Topics}   self   The instance, receive as a param to avoid exposing this function
 *                           in the prototype
 * @param  {String}   method The base topic to publish results to
 * @param  {Function} fn     Handler to wrap, can return a value or a Promise, will be invoked bound to self
 * @return {Function}        Wrapped handler
 */
function wrapInMediatorPromise(self, method, fn) {
  function publishDone(result) {
    if (_.isUndefined(result)) {
      return;
    }

    var topic = self.getTopic(method, 'done');
    if (_.has(result, 'id')) {
      topic = [topic, result.id].join(':');
    } else if (typeof result === 'string') {
      topic = [topic, result].join(':');
    }
    self.mediator.publish(topic, result);
    return result
  }

  function publishError(error) {
    var topic = self.getTopic(method, 'error');
    if (_.has(error, 'id')) {
      topic = [topic, error.id].join(':');
    }
    self.mediator.publish(topic, error);
  }

  return function() {
    return Promise.resolve(fn.apply(self, arguments))
      .then(publishDone)
      .catch(publishError);
  };
}

/**
 * Setup a handler for a namespaced topic, if this handler returns a value or throws an Error,
 * it will get published to 'done' and 'error'-predixed topics as per convention.
 *
 * @param  {String}   method Topic name inside the namespace
 * @param  {Function} fn     Handler that can optionally return a value or a Promise
 *                             that will be treated as the result of a `request`
 * @return {Topics}          Returns self for chaining
 */
Topics.prototype.on = function(method, fn) {
  var topic = this.getTopic(method);
  this.addSubscription(topic, wrapInMediatorPromise(this, method, fn));
  return this;
};

/**
 * Setup a handler for a namespaced topic, with the 'done:' prefix
 * @param  {String}   method Topic name inside the namespace
 * @param  {Function} fn     Handler function for the topic
 * @return {Topics}          Returns self for chaining
 */
Topics.prototype.onDone = function(method, fn) {
  var topic = this.getTopic(method, 'done');
  this.addSubscription(topic, fn.bind(this));
  return this;
};

/**
 * Setup a handler for a namespaced topic, with the 'done:' prefix
 * @param  {String}   method Topic name inside the namespace
 * @param  {Function} fn     Handler function for the topic
 * @return {Topics}          Returns self for chaining
 */
Topics.prototype.onError = function(method, fn) {
  var topic = this.getTopic(method, 'error');
  this.addSubscription(topic, fn.bind(this));
  return this;
};

/**
 * Modifies mediator to unsubscribe to all topics configured through this instance
 */
Topics.prototype.unsubscribeAll = function() {
  var subId;
  for (var topic in this.subscriptions) {
    if (this.subscriptions.hasOwnProperty(topic)) {
      subId = this.subscriptions[topic].id;
      this.mediator.remove(topic, subId);
    }
  }
};

/**
 * Does a {@link Mediator.request} in the context of the namespaced topics
 * @param  {String} topic   Base topic inside the configured namespace
 * @param  {Any} params     Data for the `request`
 * @param  {Object} options Options for the `request`
 * @return {Promise}        The result of the `request`
 */
Topics.prototype.request = function(topic, params, options) {
  return this.mediator.request(this.getTopic(topic), params, options);
};

module.exports = Topics;
@@ -2621,16 +2621,17 @@
 n result
+;
 %0A  %7D%0A%0A  

fix failing build