{"size": 4628, "ext": "js", "max_stars_repo_path": "src/domain/promotions/useCases/upsertPromotion.js", "max_stars_repo_name": "Carrinho-Digital/web-api", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "src/domain/promotions/useCases/upsertPromotion.js", "max_issues_repo_name": "Carrinho-Digital/web-api", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "src/domain/promotions/useCases/upsertPromotion.js", "max_forks_repo_name": "Carrinho-Digital/web-api", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 25.8547486034, "max_line_length": 79, "alphanum_fraction": 0.6158167675}
const moment = require('moment');
const { Promotion } = require('../models/promotion');
const {
  General: GeneralException,
  PromotionsNotAvailable: PromotionsNotAvailableException,
  NotFound: NotFoundException,
} = require('../../../exceptions');

function buildUpsertPromotion(getProductById) {
  function savePromotion(_promotion) {
    const promotion = new Promotion(_promotion);
    return promotion.save();
  }

  async function updatePromotion(marketId, promotionId, _promotion) {
    const updated = await Promotion.findOneAndUpdate(
      {
        '_id': promotionId,
        'market': marketId,
      },
      _promotion,
      {
        new: true,
        upsert: false,
        rawResult: true,
      },
    );

    if (updated.value) {
      return updated.value;
    }

    throw new NotFoundException('Cannot found promotion to update');
  }

  async function isPromotionAvailable(promotionId, promotion) {
    let promotionQuery = {};

    if (promotion.undefinedTime === false) {
      promotionQuery = {
        startDate: {
          $lte: promotion.endDate,
        },
        endDate: {
          $gte: promotion.startDate,
        },
      };
    } else {
      promotionQuery = {
        endDate: {
          $gte: new Date(),
        },
      };
    }

    if (promotion.product != null) {
      promotionQuery = {
        ...promotionQuery,
        product: promotion.product.toString(),
      };
    }

    if (promotion.tags && promotion.tags.length > 0) {
      promotionQuery = {
        ...promotionQuery,
        tags: {
          $all: promotion.tags,
        },
      };
    }

    if (promotionId) {
      promotionQuery = {
        ...promotionQuery,
        _id: {
          $ne: promotionId,
        },
      };
    }

    return await Promotion.find(promotionQuery);
  }

  return async function upsertPromotion(promotionId, marketId, promotion) {
    if (typeof promotion.undefinedTime !== 'boolean') {
      throw new GeneralException('The field undefinedTime must be boolean');
    }

    if (promotion.productId) {
      if (typeof promotion.productId !== 'string') {
        throw new GeneralException('The field productId must be string');
      }
      const product = await getProductById(promotion.productId);
      const productMarketId = product.market.toString();
      const currentMarketId = marketId.toString();

      if (productMarketId !== currentMarketId) {
        throw new GeneralException(
          'You cannot create a promotion for this product');
      }

      promotion.tags = [];
      delete promotion.productId;

      promotion.product = product._id;
    }

    if (promotion.tags && promotion.tags.length > 0) {
      if (!Array.isArray(promotion.tags)) {
        throw new GeneralException('The field tags must be array');
      }

      promotion.product = null;
    }

    if (promotion.discountInPercent) {
      if (typeof promotion.discountInPercent !== 'number') {
        throw new GeneralException(
          'The field discountInPercent must be number');
      }
    }

    if (promotion.discountInPrice) {
      if (typeof promotion.discountInPrice !== 'number') {
        throw new GeneralException(
          'The field discountInPrice must be number');
      }
    }

    if (promotion.undefinedTime === false &&
        (!promotion.startDate || !promotion.endDate)) {
      throw new GeneralException('You must pass start and end date');
    }

    if (promotion.discountInPercent > 0 && promotion.discountInPrice > 0) {
      throw new GeneralException(
        'You cannot pass discountInPercent and discountInPrice at same time.');
    }

    if (promotion.undefinedTime === true) {
      promotion.startDate = null;
      promotion.endDate = null;
    }

    if (promotion.undefinedTime === false) {
      promotion.startDate = moment(promotion.startDate).utc().format();
      promotion.endDate = moment(promotion.endDate).utc().format();
    }

    if (promotion.discountInPercent > 0) {
      promotion.discountInPrice = 0;
    }

    if (promotion.discountInPrice > 0) {
      promotion.discountInPercent = 0;
    }

    const promotionIsAvailable = await isPromotionAvailable(
      promotionId, promotion);

    if (promotionIsAvailable.length > 0) {
      throw new PromotionsNotAvailableException(
        'Other promotions exists for the same time',
        promotionIsAvailable,
        422,
      );
    }

    if (!promotionId) {
      promotion.market = marketId;
      return savePromotion(promotion);
    }

    return updatePromotion(marketId, promotionId, promotion);
  };
}

module.exports = buildUpsertPromotion;
