text: const { ActionTransport } = require('@microfleet/core');
  const Promise = require('bluebird');
  const Errors = require('common-errors');
  const merge = require('lodash/merge');
  const cloneDeep = require('lodash/cloneDeep');
  const reduce = require('lodash/reduce');
  const find = require('lodash/find');
  
  // helpers
  const key = require('../../redis-key');
  const { cleanupCache } = require('../../list-utils');
  const { PLANS_DATA, PLANS_INDEX, FREE_PLAN_ID } = require('../../constants');
  const {
    plan: { create: billingPlanCreate }, handleError, is, states: { active },
  } = require('../../utils/paypal');
  const { serialize } = require('../../utils/redis');
  const { createJoinPlans } = require('../../utils/plans');
  
  function sendRequest(config, message) {
    const defaultMerchantPreferences = {
      return_url: config.urls.plan_return,
      cancel_url: config.urls.plan_cancel,
      auto_bill_amount: 'YES',
      initial_fail_amount_action: 'CANCEL',
    };
  
    // setup default merchant preferences
    const { plan } = message;
    const { merchant_preferences: merchatPref, payment_definitions: paymentDefinitions } = plan;
    plan.merchant_preferences = merge(defaultMerchantPreferences, merchatPref || {});
  
    // divide single plan definition into as many as payment_definitions present
    const plans = Promise.map(paymentDefinitions, (definition) => {
      const partialPlan = {
        ...cloneDeep(plan),
        name: `${plan.name}-${definition.frequency.toLowerCase()}`,
        payment_definitions: [definition],
      };
  
      return billingPlanCreate(partialPlan, config.paypal).catch(handleError);
    });
  
    if (is.active(message.plan) === false) {
      return plans;
    }
  
    // activate the plan if we requested it
    return plans.map(async (planData) => {
      const { id } = planData;
      planData.state = active;
      await this.dispatch('plan.state', { params: { id, state: active } });
      return planData;
    });
  }
  
  function createSaveToRedis(redis, message) {
    return function saveToRedis(data) {
      const { plan, plans } = data;
      const { meta, level, title } = message;
      const aliasedId = message.alias || plan.id;
      const hidden = message.hidden || false;
  
      const pipeline = redis.pipeline();
      const planKey = key(PLANS_DATA, aliasedId);
      const plansData = reduce(plans, (a, p) => {
        const { frequency } = p.payment_definitions[0];
        a[frequency.toLowerCase()] = p.id;
        return a;
      }, {});
  
      pipeline.sadd(PLANS_INDEX, aliasedId);
  
      const subscriptions = message.subscriptions.map((subscription) => {
        subscription.definition = find(plan.payment_definitions, (item) => (
          item.frequency.toLowerCase() === subscription.name
        ));
        return subscription;
      });
  
      const saveDataFull = {
        plan: {
          ...plan,
          hidden,
        },
        id: plan.id,
        subs: subscriptions,
        type: plan.type,
        state: plan.state,
        name: plan.name,
        hidden,
        ...plansData,
        title,
        meta,
        level,
      };
  
      if (message.alias !== null && message.alias !== undefined) {
        saveDataFull.alias = message.alias;
      }
  
      const serializedData = serialize(saveDataFull);
      pipeline.hmset(planKey, serializedData);
  
      if (plan.id !== aliasedId) {
        pipeline.hmset(key(PLANS_DATA, plan.id), serializedData);
      }
  
      plans.forEach((planData) => {
        const saveData = {
          plan: {
            ...planData,
            hidden,
          },
          id: planData.id,
          subs: [find(subscriptions, { name: planData.payment_definitions[0].frequency.toLowerCase() })],
          type: planData.type,
          state: planData.state,
          name: planData.name,
          hidden,
          title,
          meta,
          level,
        };
  
        if (message.alias) {
          saveData.alias = message.alias;
        }
  
        pipeline.hmset(key(PLANS_DATA, planData.id), serialize(saveData));
      });
  
      return pipeline.exec().return(saveDataFull);
    };
  }
  
  /**
   * @api {amqp} <prefix>.plan.create Create plan
   * @apiVersion 1.0.0
   * @apiName planCreate
   * @apiGroup Plan
   *
   * @apiDescription Creates new plan
   *
   * @apiSchema {jsonschema=plan/create.json} apiRequest
   * @apiSchema {jsonschema=response/plan/create.json} apiResponse
   */
  /**
   * Creates paypal plan with a special case for a free plan
   * @param  {Object} message
   * @return {Promise}
   */
  function planCreate({ params: message }) {
    const { config, redis } = this;
    const { alias } = message;
    const saveToRedis = createSaveToRedis(redis, message);
    let promise = Promise.bind(this);
  
    if (alias && alias !== FREE_PLAN_ID) {
      promise = promise.then(() => {
        return redis.sismember(PLANS_INDEX, alias).then((isMember) => {
          if (isMember === 1) {
            throw new Errors.HttpStatusError(409, `plan ${alias} already exists`);
          }
  
          return null;
        });
      });
    }
  
    // this is a free plan, don't put it on paypal
    if (alias === FREE_PLAN_ID) {
      message.plan.id = alias;
      promise = promise.return({ plan: message.plan, plans: [] });
    } else {
      promise = promise.return([config, message]).spread(sendRequest).then(createJoinPlans(message));
    }
  
    return promise
      .then(saveToRedis)
      .tap(() => cleanupCache.call(this, PLANS_INDEX));
  }
  
  planCreate.transports = [ActionTransport.amqp];
  
  module.exports = planCreate;
  