"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.HybrisConnector = void 0;

var hybris = _interopRequireWildcard(require("hybris-occ-client"));

var errors = _interopRequireWildcard(require("../errors"));

var _superagent = _interopRequireDefault(require("superagent"));

var _urlParse = _interopRequireDefault(require("url-parse"));

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

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)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; return newObj; } }

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }

function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var clone = function clone(x) {
  return JSON.parse(JSON.stringify(x));
};
/**
 * A Commerce Integrations connector for SAP Hybris Commerce backends.
 *
 * @implements {module:@mobify/commerce-integrations/dist/connectors/interfaces.CommerceConnector}
 * @implements {module:@mobify/commerce-integrations/dist/connectors/interfaces.ParserHooks}
 */


var HybrisConnector =
/*#__PURE__*/
function () {
  function HybrisConnector(_ref) {
    var client = _ref.client,
        catalogId = _ref.catalogId,
        catalogVersionId = _ref.catalogVersionId,
        authentication = _ref.authentication;

    _classCallCheck(this, HybrisConnector);

    this.client = client;
    this.catalogId = catalogId;
    this.catalogVersionId = catalogVersionId;
    this.authentication = authentication;
  }

  _createClass(HybrisConnector, [{
    key: "absoluteURL",
    value: function absoluteURL(path) {
      return new _urlParse["default"](path, this.client.basePath || '').href;
    }
    /**
     * Register a new customer account and login.
     *
     * @param {module:@mobify/commerce-integrations/dist/types.HybrisCustomerRegistration} data
     * @return {Promise<module:@mobify/commerce-integrations/dist/types.Customer>}
     */

  }, {
    key: "registerCustomer",
    value: function registerCustomer(data) {
      var _this = this;

      var api = new hybris.UsersApi(this.client);
      return api.postUsers({
        firstName: data.firstName,
        lastName: data.lastName,
        password: data.password,
        titleCode: data.titleCode,
        uid: data.email // Must be an email

      }).then(function () {
        return _this.login(data.email, data.password);
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "login",
    value: function login(username, password) {
      var _this2 = this;

      var isRegisteredUser = username && password;
      var auth = this.authentication;
      var body = {
        client_id: auth.clientId,
        client_secret: auth.clientSecret,
        grant_type: 'client_credentials'
      };

      if (isRegisteredUser) {
        Object.assign(body, {
          grant_type: 'password',
          username: username,
          password: password
        });
      }

      return _superagent["default"].post(auth.authorizationUrl).set('Content-Type', 'application/x-www-form-urlencoded').send(body).then(function (res) {
        var updatedAuth = {
          accessToken: res.body.access_token,
          expiresIn: res.body.expires_in,
          refreshToken: res.body.refresh_token
        };
        Object.assign(_this2.client.authentications.auth, updatedAuth);
        return isRegisteredUser ? _this2.getCustomer('current') : _this2.getCustomer('anonymous');
      })["catch"](function () {
        throw new errors.ForbiddenError('Login failed');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "logout",
    value: function logout() {
      var invalidatedAuth = {
        accessToken: undefined,
        expiresIn: undefined,
        refreshToken: undefined
      };
      Object.assign(this.client.authentications.auth, invalidatedAuth);
      return Promise.resolve(undefined);
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "refreshSession",
    value: function refreshSession() {
      var _this3 = this;

      var refreshToken = this.client.authentications.auth.refreshToken;
      var grantType = refreshToken ? 'refresh_token' : 'client_credentials';
      var auth = this.authentication;
      var body = {
        client_id: auth.clientId,
        client_secret: auth.clientSecret,
        grant_type: grantType,
        refresh_token: refreshToken
      };
      return _superagent["default"].post(auth.authorizationUrl).set('Content-Type', 'application/x-www-form-urlencoded').send(body).then(function (res) {
        var newAuth = {
          accessToken: res.body.access_token,
          expiresIn: res.body.expires_in,
          refreshToken: res.body.refresh_token
        };
        Object.assign(_this3.client.authentications.auth, newAuth);
        return refreshToken ? _this3.getCustomer('current') : _this3.getCustomer('anonymous');
      })["catch"](function () {
        throw new errors.ForbiddenError('Failed To Refresh Session');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getDefaultHeaders",
    value: function getDefaultHeaders() {
      return Object.assign({}, this.client.defaultHeaders);
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "setDefaultHeaders",
    value: function setDefaultHeaders(headers) {
      this.client.defaultHeaders = Object.assign({}, headers);
    }
    /**
     * Get the current authentications object, which can be used to
     * save and restore auth state.
     *
     * @return {Object}
     */

  }, {
    key: "getAuthentications",
    value: function getAuthentications() {
      return clone(this.client.authentications);
    }
    /**
     * Set the current authentications object, as returned from getAuthentications.
     * Use to save and restore auth state.
     *
     * @param {Object} authentications
     */

  }, {
    key: "setAuthentications",
    value: function setAuthentications(authentications) {
      this.client.authentications = clone(authentications);
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getCustomer",
    value: function getCustomer(id) {
      var _this4 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var api = new hybris.UsersApi(this.client);
      return api.getUser(id, _objectSpread({
        fields: 'FULL'
      }, opts)).then(function (res) {
        return _this4.parseCustomer(res);
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "parseCustomer",
    value: function parseCustomer(data) {
      // Hybris validates that uid is an email for registered users.
      var d = data;
      var isAnon = d.uid === 'anonymous';
      var anonEmail = d.defaultAddress ? d.defaultAddress.email : undefined;
      return {
        id: isAnon ? 'anonymous' : 'current',
        firstName: d.firstName,
        lastName: d.lastName,
        email: isAnon ? anonEmail : d.uid
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "searchProducts",
    value: function searchProducts(query) {
      var _this5 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var searchRequest = this.transformSearchParams(query);
      var api = new hybris.ProductsApi(this.client);
      return api.getProductSearch(searchRequest, opts).then(function (data) {
        return _this5.parseSearchProducts(data, query);
      });
    }
  }, {
    key: "transformSearchParams",
    value: function transformSearchParams(params) {
      var _ref2;

      var query = params.query,
          _params$count = params.count,
          count = _params$count === void 0 ? 20 : _params$count,
          _params$start = params.start,
          start = _params$start === void 0 ? 0 : _params$start,
          _params$sort = params.sort,
          sort = _params$sort === void 0 ? 'relevance' : _params$sort,
          _params$filters = params.filters,
          filters = _params$filters === void 0 ? {} : _params$filters; // Please refer to Hybris documentation for notes on the shape of the request object.
      // https://help.hybris.com/6.4.0/api/ycommercewebservices/main.html#_products_search_get

      return {
        query: (_ref2 = []).concat.apply(_ref2, [query ? ["".concat(query, ":")] : [], ['', sort], Object.keys(filters).filter(function (key) {
          return !['categoryId', 'query'].includes(key);
        }).filter(function (key) {
          return Boolean(filters[key]);
        }).map(function (key) {
          return "".concat(key, ":").concat(filters[key]);
        }), filters.categoryId ? ["allCategories:".concat(filters.categoryId)] : []]).join(':'),
        pageSize: count,
        currentPage: start / count,
        sort: sort,
        fields: 'FULL'
      };
    }
  }, {
    key: "parseFilterValue",
    value: function parseFilterValue(_ref3) {
      var count = _ref3.count,
          name = _ref3.name,
          query = _ref3.query;
      // Parse query string to get filter value.
      var value = query.query.value.split(':').slice(-1)[0];
      return {
        count: count,
        label: name,
        value: value
      };
    }
  }, {
    key: "parseFilter",
    value: function parseFilter(_ref4) {
      var name = _ref4.name,
          values = _ref4.values;
      // Get the filter identifier from the first values query string. There should
      // always be a value or else the filter wouldn't be listed, but to be sure
      // return undefiend if there isn't one.
      var filterId;
      var firstValue = values[0];

      if (firstValue) {
        // Parse query string to get filter identifier.
        filterId = firstValue.query.query.value.split(':').slice(-2)[0];
      }

      return {
        id: filterId,
        label: name,
        values: values.map(this.parseFilterValue.bind(this))
      };
    }
  }, {
    key: "parseSearchProducts",
    value: function parseSearchProducts(data) {
      var _this6 = this;

      var _data$products = data.products,
          products = _data$products === void 0 ? [] : _data$products,
          _data$sorts = data.sorts,
          sorts = _data$sorts === void 0 ? [] : _data$sorts,
          pagination = data.pagination,
          freeTextSearch = data.freeTextSearch,
          _data$facets = data.facets,
          facets = _data$facets === void 0 ? [] : _data$facets,
          _data$breadcrumbs = data.breadcrumbs,
          breadcrumbs = _data$breadcrumbs === void 0 ? [] : _data$breadcrumbs;

      var getFilterCrumbs = function getFilterCrumbs() {
        var _ref5, _ref6;

        var selectedFilterVals = (_ref5 = []).concat.apply(_ref5, _toConsumableArray(facets.map(function (filter) {
          return filter.values.filter(function (f) {
            return f.selected;
          });
        })));

        var selectedBreadcrumbs = (_ref6 = []).concat.apply(_ref6, _toConsumableArray(selectedFilterVals.map(function (fv) {
          return breadcrumbs.filter(function (bc) {
            return bc.facetValueName === fv.name;
          });
        })));

        var allCatsCrumb = breadcrumbs.find(function (bc) {
          return bc.facetCode === 'allCategories';
        });
        allCatsCrumb && selectedBreadcrumbs.push(allCatsCrumb);
        return selectedBreadcrumbs;
      };

      return {
        results: products.map(function (_ref7) {
          var _ref7$stock = _ref7.stock,
              stock = _ref7$stock === void 0 ? {} : _ref7$stock,
              _ref7$images = _ref7.images,
              images = _ref7$images === void 0 ? [] : _ref7$images,
              product = _objectWithoutProperties(_ref7, ["stock", "images"]);

          return {
            available: stock.stockLevelStatus === 'inStock',
            productId: product.code,
            productName: product.name,
            link: product.url,
            defaultImage: images[0] && {
              alt: product.name,
              src: _this6.absoluteURL(images[0].url)
            },
            price: product.price.value
          };
        }),
        query: freeTextSearch,
        count: pagination.pageSize,
        start: pagination.currentPage * pagination.pageSize,
        total: pagination.totalResults,
        filters: facets.map(this.parseFilter.bind(this)),
        selectedFilters: getFilterCrumbs().reduce(function (obj, curr) {
          obj[curr.facetCode] = curr.facetValueCode;
          return obj;
        }, {}),
        sortingOptions: sorts.map(function (_ref8) {
          var code = _ref8.code,
              name = _ref8.name;
          return {
            id: code,
            label: name
          };
        }),
        selectedSortingOption: pagination.sort
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getCategory",
    value: function getCategory(id) {
      var _this7 = this;

      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var api = new hybris.CatalogsApi(this.client);
      return api.getCategory(this.catalogId, this.catalogVersionId, id, options).then(function (data) {
        return _this7.parseCategory(data);
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getCategories",
    value: function getCategories(ids) {
      var _this8 = this;

      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var getCategoryPromises = ids.map(function (id) {
        return _this8.getCategory(id, options)["catch"](function () {
          return null;
        });
      });
      return Promise.all(getCategoryPromises).then(function (categories) {
        var validCategories = categories.filter(function (category) {
          return !!category;
        });
        return {
          data: validCategories,
          count: Object.keys(categories).length,
          total: ids.length
        };
      });
    }
    /**
     * Takes an array of OCC API Category objects and parses it into an
     * commerce-integrations Category type.
     * @param {Object[]} categories an array of OCC API {@link https://mobify.github.io/hybris-occ-client/module-models_CategoryHierarchy.html|Category} document
     *
     * @returns {Promise<module:@mobify/commerce-integrations/dist/types.Category>}
     */

    /* eslint-disable camelcase */

  }, {
    key: "parseCategories",
    value: function parseCategories(categories) {
      var _this9 = this;

      var parsedCategories = [];
      categories.forEach(function (category) {
        return parsedCategories.push(_this9.parseCategory(category));
      });
      return parsedCategories;
    }
    /**
     * Takes an OCC API Category objects and parses it into an
     * commerce-integrations Category type.
     * @param {Object[]} categories An OCC API {@link https://mobify.github.io/hybris-occ-client/module-models_CategoryHierarchy.html|Category} document
     *
     * @returns {Promise<module:@mobify/commerce-integrations/dist/types.Category>}
     */

  }, {
    key: "parseCategory",
    value: function parseCategory(_ref9) {
      var id = _ref9.id,
          name = _ref9.name,
          subcategories = _ref9.subcategories;
      var category = {
        id: id,
        name: name
      };

      if (subcategories) {
        category.categories = this.parseCategories(subcategories);
      }

      return category;
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getProduct",
    value: function getProduct(id) {
      var _this10 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var options = {};
      var api = new hybris.ProductsApi(this.client);
      var defaultOptions = {
        fields: 'FULL' // Assign options to new object. This allows the user to override
        // the defaults we set.

      };
      options = _objectSpread({}, defaultOptions, {}, opts); // Get the base product by creating a promise chain recursively.

      var productCache = [];

      var getBaseProduct = function getBaseProduct(id, options) {
        return api.getProduct(id, options).then(function (product) {
          var baseProduct = product.baseProduct; // Maintain a cache of all products requested in this command.
          // It will be used lated to de-dup any fetches.

          productCache.push(product);
          return baseProduct ? getBaseProduct(baseProduct, options) : product;
        });
      };

      return getBaseProduct(id, options).then(function (baseProduct) {
        // Overwrite the base product id with the id we initiality requested so it's parsed
        // out correctly later on.
        baseProduct.code = id;
        return Promise.all((baseProduct.variantOptions || []).map(function (variantOption) {
          // Search product cache and return products already fetched.
          var cachedProduct = productCache.find(function (_ref10) {
            var code = _ref10.code;
            return code === variantOption.code;
          });
          return cachedProduct || api.getProduct(variantOption.code, options);
        })).then(function (variations) {
          return {
            baseProduct: baseProduct,
            variations: variations
          };
        });
      }).then(function (_ref11) {
        var baseProduct = _ref11.baseProduct,
            variations = _ref11.variations;
        return _this10.parseProduct(baseProduct, variations);
      }).then(function (parsedProduct) {
        // Get the variation values from the first product requested.
        var _productCache$0$baseO = productCache[0].baseOptions,
            baseOptions = _productCache$0$baseO === void 0 ? [] : _productCache$0$baseO;

        var _ref12 = baseOptions[0] || {},
            _ref12$selected = _ref12.selected,
            selected = _ref12$selected === void 0 ? {} : _ref12$selected;

        var _selected$variantOpti = selected.variantOptionQualifiers,
            variantOptionQualifiers = _selected$variantOpti === void 0 ? [] : _selected$variantOpti;
        var variationValues = variantOptionQualifiers.reduce(function (acc, _ref13) {
          var qualifier = _ref13.qualifier,
              value = _ref13.value;
          return _objectSpread({}, acc, _defineProperty({}, qualifier, value));
        }, {}); // Set the products values.

        parsedProduct.variationValues = variationValues;
        return parsedProduct;
      })["catch"](function () {
        throw new errors.NotFoundError('Product Not Found');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getProducts",
    value: function getProducts(ids) {
      var _this11 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      if (!ids || ids.length <= 0) {
        throw new Error('Please specify list of product ids to get.');
      }

      var getProductPromises = ids.map(function (id) {
        return _this11.getProduct(id, opts)["catch"](function () {
          return null;
        });
      });
      return Promise.all(getProductPromises).then(function (products) {
        var validProducts = products.filter(function (product) {
          return !!product;
        });
        return {
          count: validProducts.length,
          data: validProducts,
          total: validProducts.length
        };
      });
    }
    /**
     * Takes an OCC API ImageGroup object and parses it into a commerce-integrations ImageSet type.
     * @param {Object} data An OCC API {@link https://help.sap.com/doc/0e9065bfffdf4877a5aac1bf4ff9c32c/1811/en-US/ycommercewebservices/index.html#_productwsdto} document
     *
     * @returns {Promise<module:@mobify/commerce-integrations/dist/types.ImageSet>}
     */

    /* eslint-disable camelcase */

  }, {
    key: "parseImageSets",
    value: function parseImageSets(variation) {
      var _this12 = this;

      var images = variation.images,
          variantOptions = variation.variantOptions;
      var variantOptionQualifiers = variantOptions[0].variantOptionQualifiers;
      var variationProperties = variantOptionQualifiers.map(function (_ref14) {
        var qualifier = _ref14.qualifier,
            name = _ref14.name,
            value = _ref14.value;
        return {
          id: qualifier,
          label: name,
          values: [{
            name: value,
            value: value
          }]
        };
      });
      return ['zoom', 'product', 'thumbnail'].map(function (type) {
        return {
          images: images.filter(function (image) {
            return image.format === type;
          }).map(function (_ref15) {
            var altText = _ref15.altText,
                url = _ref15.url;
            return {
              alt: altText,
              description: altText,
              src: _this12.absoluteURL(url),
              title: altText
            };
          }),
          variationProperties: variationProperties,
          sizeType: type
        };
      });
    }
    /* eslint-enable camelcase */

    /**
     * Takes an OCC Product object and parses it into a commerce-integrations Product type.
     * @param {Object} data An OCC product document
     *
     * @returns {Promise<module:@mobify/commerce-integrations/dist/types.Product>}
     */

    /* eslint-disable camelcase */

  }, {
    key: "parseProduct",
    value: function parseProduct(_ref16) {
      var _this13 = this;

      var code = _ref16.code,
          name = _ref16.name,
          description = _ref16.description,
          categories = _ref16.categories,
          price = _ref16.price,
          variantOptions = _ref16.variantOptions;
      var variations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
      // Parse all the image sets. We'll use these in our return value as well
      // as for parsing property value images.
      var allImageSets = [];
      variations.forEach(function (variation) {
        allImageSets = [].concat(_toConsumableArray(allImageSets), _toConsumableArray(_this13.parseImageSets(variation)));
      });
      var allVariantOptions = [].concat(_toConsumableArray(variantOptions), _toConsumableArray(variations.reduce(function (acc, _ref17) {
        var variantOptions = _ref17.variantOptions;
        return [].concat(_toConsumableArray(acc), _toConsumableArray(variantOptions));
      }, [])));
      var variationProperties = allVariantOptions.reduce(function (acc, variantOption) {
        return [].concat(_toConsumableArray(acc), _toConsumableArray(variantOption.variantOptionQualifiers));
      }, []).reduce(function (acc, _ref18) {
        var qualifier = _ref18.qualifier,
            name = _ref18.name,
            value = _ref18.value;
        var variationProperty = acc.find(function (variationOptionQualifier) {
          return variationOptionQualifier.id === qualifier;
        }); // Lets get the first image of the correct type from the image sets.

        var variationImageSets = allImageSets.filter(function (_ref19) {
          var variationProperties = _ref19.variationProperties;
          return variationProperties.find(function (_ref20) {
            var id = _ref20.id,
                values = _ref20.values;
            return id === qualifier && values[0].value === value;
          });
        });
        var filteredProductImageSets = variationImageSets.filter(function (_ref21) {
          var sizeType = _ref21.sizeType;
          return sizeType === 'product';
        });
        var filteredThumbnailImageSets = variationImageSets.filter(function (_ref22) {
          var sizeType = _ref22.sizeType;
          return sizeType === 'thumbnail';
        });

        if (variationProperty) {
          var isNew = !variationProperty.values.find(function (v) {
            return v.value === value;
          });

          if (isNew) {
            variationProperty.values.push({
              name: value,
              value: value,
              mainImage: filteredProductImageSets.length && filteredProductImageSets[0].images[0],
              swatchImage: filteredThumbnailImageSets.length && filteredThumbnailImageSets[0].images[0]
            });
          }
        } else {
          acc.push({
            id: qualifier,
            label: name,
            values: [{
              name: value,
              value: value,
              mainImage: filteredProductImageSets.length && filteredProductImageSets[0].images[0],
              swatchImage: filteredThumbnailImageSets.length && filteredThumbnailImageSets[0].images[0]
            }]
          });
        }

        return acc;
      }, []);
      var allVariations = variations.map(function (_ref23) {
        var variantOptions = _ref23.variantOptions;
        return variantOptions.map(function (_ref24) {
          var code = _ref24.code,
              priceData = _ref24.priceData,
              stock = _ref24.stock,
              variantOptionQualifiers = _ref24.variantOptionQualifiers;
          return {
            id: code,
            price: priceData.value,
            orderable: stock.stockLevelStatus === 'inStock',
            values: variantOptionQualifiers.reduce(function (acc, curr) {
              acc[curr.qualifier] = curr.value;
              return acc;
            }, {})
          };
        });
      }).reduce(function (acc, curr) {
        return [].concat(_toConsumableArray(acc), _toConsumableArray(curr));
      }, []);
      return {
        id: code,
        name: name,
        imageSets: allImageSets,
        description: description,
        categoryId: categories.length ? categories.pop().code : undefined,
        minimumOrderQuantity: 1,
        stepQuantity: 1,
        price: price.value,
        variations: allVariations,
        variationProperties: variationProperties
      };
    }
    /* eslint-enable camelcase */

    /**
     * @inheritDoc
     */

  }, {
    key: "getStore",
    value: function getStore(id) {
      var _this14 = this;

      var api = new hybris.StoresApi(this.client);
      return api.getStore(id, {
        fields: 'FULL'
      }).then(function (data) {
        return _this14.parseGetStore(_objectSpread({}, data, {
          id: id
        }));
      });
    }
    /**
     * Takes an OCC API WeekdayOpeningDay object and parses it into a commerce-integrations Store.hours type.
     * @param {Object} hours An OCC API {@link https://help.hybris.com/6.7.0/api/ycommercewebservices/index.html#_weekdayopeningdaywsdto|WeekdayOpneningDay} document
     *
     * @returns {Promise<module:@mobify/commerce-integrations/dist/types.Store.hours>}
     */

  }, {
    key: "parseOpeningHours",
    value: function parseOpeningHours(hours) {
      var parsedHours = {};
      hours.forEach(function (h) {
        parsedHours[h.weekDay] = {
          openingTime: h.openingTime ? h.openingTime.formattedHour : undefined,
          closingTime: h.closingTime ? h.closingTime.formattedHour : undefined
        };
      });
      return parsedHours;
    }
    /**
     * Takes an OCC API PointOfService object and parses it into a commerce-integrations Store type.
     * @param {Object} data An OCC API {@link https://help.hybris.com/6.7.0/api/ycommercewebservices/index.html#_pointofservicewsdto|PointOfService} document
     *
     * @returns {Promise<module:@mobify/commerce-integrations/dist/types.Store>}
     */

  }, {
    key: "parseGetStore",
    value: function parseGetStore(data) {
      var _this15 = this;

      var d = data;
      var address = d.address;
      return {
        addressLine1: address ? address.line1 : undefined,
        addressLine2: address ? address.line2 : undefined,
        city: address ? address.town : undefined,
        country: address && address.country ? address.country.isocode : undefined,
        description: d.description,
        features: d.features ? d.features.entry.map(function (feature) {
          return feature.value;
        }) : undefined,
        email: address ? address.email : undefined,
        name: d.name,
        phone: address && address.phone ? address.phone.replace(new RegExp('[(]|[)]|[.]|[ ]|[-]|[#]|[x]', 'g'), '') : undefined,
        postalCode: address ? address.postalCode : undefined,
        hours: this.parseOpeningHours(d.openingHours.weekDayOpeningList),
        images: d.storeImages ? d.storeImages.map(function (image) {
          return {
            src: _this15.absoluteURL(image.url),
            alt: d.name
          };
        }) : undefined,
        id: d.id
      };
    }
  }, {
    key: "transformSearchStoreParams",
    value: function transformSearchStoreParams(searchParams) {
      var sp = searchParams;
      var count = sp.count > 0 ? sp.count : 20;
      var start = sp.start > 0 ? sp.start : 0;
      return {
        pageSize: count.toString(),
        currentPage: start.toString(),
        sort: 'relevance',
        latitude: !isNaN(sp.latlon.latitude) ? sp.latlon.latitude.toString() : undefined,
        longitude: !isNaN(sp.latlon.longitude) ? sp.latlon.longitude.toString() : undefined,
        fields: 'FULL'
      };
    }
    /**
     * @inheritDoc
     */
    // eslint-disable-next-line no-unused-vars

  }, {
    key: "searchStores",
    value: function searchStores(storeSearchRequest) {
      var _this16 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      if (isNaN(storeSearchRequest.latlon.latitude) || isNaN(storeSearchRequest.latlon.longitude)) {
        throw new Error('Provide Latitude and Longitude coordinates');
      }

      var api = new hybris.StoresApi(this.client);
      var searchRequest = this.transformSearchStoreParams(storeSearchRequest);
      return api.getStores(searchRequest).then(function (data) {
        if (data.stores.length <= 0) {
          throw new Error('No stores found');
        }

        return _this16.parseSearchStores(data);
      });
    }
  }, {
    key: "parseSearchStores",
    value: function parseSearchStores(data) {
      var _this17 = this;

      var d = data;
      var count;
      var start = d.pagination.currentPage;
      var total = d.pagination.totalResults;
      var pageSize = d.pagination.pageSize;
      var totalPages = d.pagination.totalPages;

      if (totalPages === start + 1) {
        // on last page
        if (total > pageSize) {
          var remainder = total % pageSize;
          count = remainder !== 0 ? remainder : pageSize;
        } else {
          count = total;
        }
      } else {
        // middle pages
        count = pageSize;
      }

      return {
        count: count,
        start: start,
        total: total,
        stores: d.stores.length > 0 ? d.stores.map(function (store) {
          return _this17.parseGetStore(_objectSpread({}, store, {
            id: store.name
          }));
        }) : undefined
      };
    }
  }, {
    key: "isLoggedIn",
    value: function isLoggedIn() {
      return this.getAuthentications().auth !== undefined;
    }
    /**
     * Returns customer id 'current' if a refresh token is set in the auth
     * Otherwise, returns customer id 'anonymous'
     *
     * @private
     */

  }, {
    key: "getCustomerIdFromAuth",
    value: function getCustomerIdFromAuth() {
      if (!this.isLoggedIn()) {
        throw new errors.NotAuthenticatedError();
      }

      return this.getAuthentications().auth.refreshToken === undefined ? 'anonymous' : 'current';
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "createCart",
    value: function createCart(oldCart) {
      var _this18 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var api = new hybris.UsersApi(this.client);
      var customerId = this.getCustomerIdFromAuth();
      return api.postCart(customerId, _objectSpread({
        fields: 'FULL'
      }, opts)).then(function (newCart) {
        if (customerId === 'current' && oldCart !== undefined) {
          return api.postCart(customerId, {
            // postCart will merge and delete the old cart
            fields: 'FULL',
            toMergeCartGuid: newCart.guid,
            oldCartId: oldCart.id
          })["catch"](function () {
            throw new errors.ServerError('Could not merge old cart');
          });
        }

        return newCart;
      }).then(function (cart) {
        return _this18.parseCart(cart);
      })["catch"](function () {
        throw new errors.ServerError('Could not create cart');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getCart",
    value: function getCart(cartId) {
      var _this19 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var api = new hybris.UsersApi(this.client);

      if (!cartId) {
        throw new errors.InvalidArgumentError("Parameter 'cartId' is required");
      }

      return api.getCart(this.getCustomerIdFromAuth(), cartId, _objectSpread({
        fields: 'FULL'
      }, opts)).then(function (data) {
        return _this19.parseCart(data);
      })["catch"](function () {
        throw new errors.ServerError('Could not get cart');
      });
    }
    /**
     * @inheritDoc
     */
    // eslint-disable-next-line no-unused-vars

  }, {
    key: "deleteCart",
    value: function deleteCart(cartId) {
      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      if (!cartId) {
        throw new errors.InvalidArgumentError("Parameter 'cartId' is required");
      }

      var api = new hybris.UsersApi(this.client);
      return api.deleteCart(this.getCustomerIdFromAuth(), cartId)["catch"](function () {
        throw new errors.ServerError('Could not delete cart');
      });
    }
    /**
     * Parse a coupon item
     */

  }, {
    key: "parseCouponEntry",
    value: function parseCouponEntry(data) {
      return {
        id: data.code,
        code: data.code
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "parseCart",
    value: function parseCart(data) {
      var d = data;

      if (!d.guid && !d.code) {
        throw new Error('Error parsing cart');
      }

      var customerId = this.getCustomerIdFromAuth();
      var cartId = customerId === 'anonymous' ? d.guid : d.code;

      var parseNumberValue = function parseNumberValue(price) {
        return price ? price.value : undefined;
      };

      var parsedDiscounts = parseNumberValue(d.totalDiscounts);
      var discounts = parsedDiscounts === 0 ? parsedDiscounts : Math.abs(parsedDiscounts) * -1; // Our interface requires a negative value

      return {
        id: cartId,
        couponEntries: d.appliedVouchers ? d.appliedVouchers.map(this.parseCouponEntry) : [],
        customerInfo: {
          id: customerId,
          email: d.email
        },
        items: d.entries ? d.entries.map(this.parseCartItem) : [],
        shippingAddress: d.deliveryAddress ? this.parseOrderAddress(d.deliveryAddress) : undefined,
        billingAddress: d.paymentInfo && d.paymentInfo.billingAddress ? this.parseOrderAddress(d.paymentInfo.billingAddress) : undefined,
        selectedShippingMethodId: d.deliveryMode ? d.deliveryMode.code : undefined,
        payments: d.paymentInfo ? [this.parsePayment(_objectSpread({}, d.paymentInfo, {
          amount: d.totalPriceWithTax.value
        }))] : [],
        subtotal: parseNumberValue(d.subTotal),
        shipping: parseNumberValue(d.deliveryCost),
        discounts: discounts,
        tax: parseNumberValue(d.totalTax),
        total: parseNumberValue(d.totalPriceWithTax)
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "parseCartItem",
    value: function parseCartItem(data) {
      var d = data;
      var quantity = d.quantity || 0;
      return {
        id: "".concat(d.entryNumber),
        productId: d.product ? d.product.code : undefined,
        productName: d.product ? d.product.name : undefined,
        quantity: quantity,
        baseItemPrice: d.basePrice ? d.basePrice.value : 0,
        baseLinePrice: d.basePrice ? d.basePrice.value * quantity : 0,
        itemPrice: d.totalPrice && quantity !== 0 ? d.totalPrice.value / quantity : 0,
        linePrice: d.totalPrice ? d.totalPrice.value : 0
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "parseShippingMethod",
    value: function parseShippingMethod(data) {
      return {
        id: data.code,
        name: data.name,
        cost: data.deliveryCost || 0
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "parseOrderAddress",
    value: function parseOrderAddress(data) {
      var d = data;
      return {
        id: d.id,
        titleCode: d.titleCode,
        firstName: d.firstName,
        lastName: d.lastName,
        phone: d.phone,
        // BUG: fixed in hybris 6.5, phone field is not saved and returned
        addressLine1: d.line1,
        addressLine2: d.line2,
        countryCode: d.country ? d.country.isocode : undefined,
        stateCode: d.region ? d.region.isocode : undefined,
        city: d.town,
        postalCode: d.postalCode
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "parsePayment",
    value: function parsePayment(data) {
      var d = data;
      return {
        id: d.id,
        amount: d.amount,
        // amount is always cart total for now.
        methodId: 'CREDIT_CARD',
        // only 1 type of payment method, we'll call it 'CREDIT_CARD'
        details: {
          type: d.cardType.code,
          holderName: d.accountHolderName,
          number: undefined,
          // to add/update payment, provide full number.
          username: undefined,
          maskedNumber: d.cardNumber,
          // hybris will give back masked number.
          expiryMonth: parseInt(d.expiryMonth),
          expiryYear: parseInt(d.expiryYear),
          ccv: undefined // hybris doesnt have ccv?

        }
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "parsePaymentMethod",
    value: function parsePaymentMethod(data) {
      return {
        id: data.id,
        name: data.name,
        types: data.types.map(function (types) {
          return {
            id: types.code,
            name: types.name
          };
        })
      };
    }
    /**
     * Transform an orderAddress into hybris' models: billingAddress or shippingAddress
     */

  }, {
    key: "transformOrderAddress",
    value: function transformOrderAddress(orderAddress) {
      return {
        country: {
          isocode: orderAddress.countryCode
        },
        firstName: orderAddress.firstName,
        lastName: orderAddress.lastName,
        id: orderAddress.id,
        line1: orderAddress.addressLine1,
        line2: orderAddress.addressLine2,
        phone: orderAddress.phone,
        postalCode: orderAddress.postalCode,
        region: {
          isocode: orderAddress.stateCode
        },
        titleCode: orderAddress.titleCode,
        town: orderAddress.city
      };
    }
    /**
     * Transform a shippingMethod into hybris' model deliveryMethod.
     */

  }, {
    key: "transformShippingMethod",
    value: function transformShippingMethod(shippingMethod) {
      return {
        code: shippingMethod.id,
        name: shippingMethod.name,
        deliveryCost: shippingMethod.cost
      };
    }
    /**
     * Transform a cartItem into hybris' model OrderEntryOCC.
     */

  }, {
    key: "transformCartItem",
    value: function transformCartItem(cartItem) {
      return {
        product: {
          code: cartItem.productId
        },
        quantity: cartItem.quantity
      };
    }
    /**
     * Transform a payment into hybris' model paymentDetail.
     */

  }, {
    key: "transformPayment",
    value: function transformPayment(payment, billingAddress) {
      var details = payment.details;
      return {
        id: payment.id,
        accountHolderName: details ? details.holderName : undefined,
        billingAddress: this.transformOrderAddress(billingAddress),
        cardNumber: details ? details.number : undefined,
        cardType: {
          code: details ? details.type : undefined
        },
        expiryMonth: details ? details.expiryMonth : undefined,
        expiryYear: details ? details.expiryYear : undefined,
        subscriptionId: details ? details.username : undefined
      };
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "addCartItem",
    value: function addCartItem(cart, cartItem) {
      var _this20 = this;

      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!cartItem) {
        throw new errors.InvalidArgumentError("Parameter 'cartItem' is required");
      }

      var api = new hybris.UsersApi(this.client);
      var entryOrder = this.transformCartItem(cartItem, opts);
      return api.postCartEntries(cart.customerInfo.id, cart.id, entryOrder, _objectSpread({}, opts, {
        fields: 'FULL'
      })).then(function () {
        return api.getCart(cart.customerInfo.id, cart.id, _objectSpread({}, opts, {
          fields: 'FULL'
        }));
      }).then(function (data) {
        var updatedCart = _this20.parseCart(_objectSpread({}, data, {
          email: cart.customerInfo.email
        })); // BUG: does not save billing address region when sending billing address through a POST cart payment detail.
        // So lets persist this field on the incoming cart parameter to the returned order.


        if (updatedCart.billingAddress && !updatedCart.billingAddress.stateCode && cart.billingAddress.stateCode) {
          updatedCart.billingAddress.stateCode = cart.billingAddress.stateCode;
        }

        return updatedCart;
      })["catch"](function () {
        throw new errors.ServerError('Could not add item to cart');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "removeCartItem",
    value: function removeCartItem(cart, cartItemId) {
      var _this21 = this;

      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!cartItemId) {
        throw new errors.InvalidArgumentError("Parameter 'cartItemId' is required");
      }

      var api = new hybris.UsersApi(this.client);
      return api.deleteCartEntry(cart.customerInfo.id, cart.id, cartItemId).then(function () {
        return api.getCart(cart.customerInfo.id, cart.id, _objectSpread({}, opts, {
          fields: 'FULL'
        }));
      }).then(function (data) {
        var updatedCart = _this21.parseCart(_objectSpread({}, data, {
          email: cart.customerInfo.email
        })); // BUG: does not save billing address region when sending billing address through a POST cart payment detail.
        // So lets persist this field on the incoming cart parameter to the returned order.


        if (updatedCart.billingAddress && !updatedCart.billingAddress.stateCode && cart.billingAddress.stateCode) {
          updatedCart.billingAddress.stateCode = cart.billingAddress.stateCode;
        }

        return updatedCart;
      })["catch"](function () {
        throw new errors.ServerError('Could not delete cart item');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "updateCartItem",
    value: function updateCartItem(cart, cartItem) {
      var _this22 = this;

      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!cartItem) {
        throw new errors.InvalidArgumentError("Parameter 'cartItem' is required");
      }

      var api = new hybris.UsersApi(this.client);
      var entryOrder = this.transformCartItem(cartItem, opts);
      return api.patchCartEntry(cart.customerInfo.id, cart.id, cartItem.id, entryOrder, {
        fields: 'FULL'
      }).then(function () {
        return api.getCart(cart.customerInfo.id, cart.id, {
          fields: 'FULL'
        });
      }).then(function (data) {
        var updatedCart = _this22.parseCart(_objectSpread({}, data, {
          email: cart.customerInfo.email
        })); // BUG: does not save billing address region when sending billing address through a POST cart payment detail.
        // So lets persist this field on the incoming cart parameter to the returned order.


        if (updatedCart.billingAddress && !updatedCart.billingAddress.stateCode && cart.billingAddress.stateCode) {
          updatedCart.billingAddress.stateCode = cart.billingAddress.stateCode;
        }

        return updatedCart;
      })["catch"](function () {
        throw new errors.ServerError('Could not update cart item');
      });
    }
    /**
     * Add a supported payment type to the cart
     * @param {module:@mobify/commerce-integrations/dist/types.Cart} cart The cart to add the payment to
     * @param {module:@mobify/commerce-integrations/dist/types.Payment} payment The payment to add
     * @param {Object} opts
     * @return {Promise<module:@mobify/commerce-integrations/dist/types.Cart>}
     *
     * Need to set a billing address before setting a payment.
     * Must call setCustomerInformation() to add an email before setting payment for an anonymous cart.user.
     * cart.paymentDetails.number will be returned as undefined and set as a masked number in cart.paymentDetails.maskedNumber.
     */

  }, {
    key: "setPayment",
    value: function setPayment(cart, payment) {
      var _this23 = this;

      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!payment) {
        throw new errors.InvalidArgumentError("Parameter 'payment' is required");
      }

      var api = new hybris.UsersApi(this.client);

      if (cart.customerInfo.id === 'anonymous' && cart.customerInfo.email === undefined) {
        throw new errors.InvalidArgumentError('Please set email for guest user cart');
      }

      if (!cart.billingAddress) {
        throw new errors.InvalidArgumentError('Please set billing address first');
      }

      var isMissingFields = !payment.details.type || !payment.details.expiryMonth || !payment.details.expiryYear || !payment.details.holderName || !payment.details.number;

      if (isMissingFields) {
        throw new errors.InvalidArgumentError('Missing hybris required fields');
      }

      var paymentDetails = this.transformPayment(payment, cart.billingAddress); // If the customer is a registered user and a payment already exists on the cart,
      // then the payment is has also been added to the user account. So, update the
      // corresponding payment on the user account to see the reflected changes on the cart.

      var apiCommand = cart.payments.length > 0 && cart.customerInfo.id === 'current' ? api.putPaymentDetail(cart.customerInfo.id, cart.payments[0].id, paymentDetails).then(function () {
        return _this23.getCart(cart.id);
      }) : api.postCartPaymentDetail(cart.customerInfo.id, cart.id, paymentDetails, _objectSpread({}, opts, {
        fields: 'FULL'
      })).then(function (data) {
        cart.payments[0] = _this23.parsePayment(_objectSpread({}, data, {
          amount: cart.total
        }));
        return cart;
      });
      return apiCommand["catch"](function () {
        throw new errors.ServerError('Could not set payment');
      });
    }
    /**
     * Set the customer information.
     * @param {module:@mobify/commerce-integrations/dist/types.Cart} cart The customer's cart
     * @param {module:@mobify/commerce-integrations/dist/types.CustomerInformation} customerInformation The new or modified customer information
     * @param {Object} opts
     * @return {Promise<module:@mobify/commerce-integrations/dist/types.Cart>}
     *
     * Email can be set for a guest user but not a registered user.
     */

  }, {
    key: "setCustomerInformation",
    value: function setCustomerInformation(cart, customerInfo) {
      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!customerInfo) {
        throw new errors.InvalidArgumentError("Parameter 'customerInformation' is required");
      }

      var api = new hybris.UsersApi(this.client);
      var customerId = cart.customerInfo.id;
      return api.putCartEmail(customerId, cart.id, _objectSpread({}, opts, {
        email: customerInfo.email
      })).then(function () {
        cart.customerInfo = _objectSpread({}, customerInfo, {
          id: customerId
        });
        return cart;
      })["catch"](function () {
        throw new errors.ServerError('Could not set customer information');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getShippingMethods",
    value: function getShippingMethods(cart) {
      var _this24 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      var api = new hybris.UsersApi(this.client);
      return api.getCartDeliveryModes(cart.customerInfo.id, cart.id, _objectSpread({}, opts, {
        fields: 'FULL'
      })).then(function (_ref25) {
        var deliveryModes = _ref25.deliveryModes;
        return deliveryModes.map(_this24.parseShippingMethod.bind(_this24));
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "setShippingMethod",
    value: function setShippingMethod(cart, shippingMethod) {
      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!shippingMethod) {
        throw new errors.InvalidArgumentError("Parameter 'shippingMethod' is required");
      }

      var api = new hybris.UsersApi(this.client);
      return api.putCartDeliveryMode(cart.customerInfo.id, cart.id, shippingMethod.id).then(function () {
        cart.selectedShippingMethodId = shippingMethod.id;
        return cart;
      })["catch"](function () {
        throw new errors.ServerError('Could not set shipping method');
      });
    }
    /**
     * Set the billing address on the cart.
     *
     * Billing Address model : this order address needs to have a titleCode defined.
     * The default titleCodes are: ['mr', 'ms', 'miss', 'mrs', 'dr', 'rev']
     * But these may change depending on your backend, please refer to your backend instance to confirm valid titleCodes.
     *
     * If the billing address is set before adding a payment, the billing address will not yet be saved in the hybris instance.
     * Instead, the billing address will be saved in the hybris instance upon setting a payment.
     * @param {module:@mobify/commerce-integrations/dist/types.Cart} cart The cart to update the billing address for
     * @param {module:@mobify/commerce-integrations/dist/types.OrderAddress} billingAddress The new or modified address
     * @param {Object} opts
     * @return {Promise<module:@mobify/commerce-integrations/dist/types.Cart>}
     */

  }, {
    key: "setBillingAddress",
    value: function setBillingAddress(cart, billingAddress) {
      var _this25 = this;

      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!billingAddress) {
        throw new errors.InvalidArgumentError("Parameter 'billingAddress' is required");
      }

      var isMissingFields = !billingAddress.countryCode || !billingAddress.addressLine1 || !billingAddress.stateCode || !billingAddress.titleCode || !billingAddress.postalCode || !billingAddress.city || !billingAddress.firstName || !billingAddress.lastName;

      if (isMissingFields) {
        throw new errors.InvalidArgumentError('Missing hybris required fields');
      } // if the cart does not have a payment yet, give back a cart with the billing address set.
      // else, there a payment already exists and it's billing address needs to be updated


      if (cart.payments.length <= 0) {
        cart.billingAddress = billingAddress;
        return Promise.resolve(cart);
      }

      var api = new hybris.UsersApi(this.client); // For a registered user, the existing payment can be patched to update it's billing address

      if (cart.customerInfo.id === 'current') {
        return api.patchPaymentDetail(cart.customerInfo.id, cart.payments[0].id, {
          billingAddress: this.transformOrderAddress(billingAddress)
        }).then(function () {
          cart.billingAddress = billingAddress;
          return cart;
        })["catch"](function () {
          throw new errors.ServerError('Could not set billing address');
        });
      } // For a guest user, the existing payment can only be updated if the payment detail number is re-injected.


      if (cart.payments[0].details.number === undefined) {
        throw new errors.InvalidArgumentError('As a guest user, please re-inject payment details number before updating billing address');
      }

      return api.postCartPaymentDetail(cart.customerInfo.id, cart.id, this.transformPayment(cart.payments[0], billingAddress), _objectSpread({}, opts, {
        fields: 'FULL'
      })).then(function (data) {
        cart.billingAddress = billingAddress;
        cart.payments[0] = _this25.parsePayment(_objectSpread({}, data, {
          amount: cart.total
        }));
        return cart;
      })["catch"](function () {
        throw new errors.ServerError('Could not set billing address');
      });
    }
    /**
     * Set the shipping method for the cart.
     * @param {module:@mobify/commerce-integrations/dist/types.Cart} cart The cart to set the shipping method for
     * @param {module:@mobify/commerce-integrations/dist/types.ShippingMethod} shippingMethod The shipping method to setid
     * @param {Object} opts
     * @return {Promise<module:@mobify/commerce-integrations/dist/types.Cart>}
     *
     * Must call setCustomerInformation() to add an email before setting payment for a guest user.
     */

  }, {
    key: "setShippingAddress",
    value: function setShippingAddress(cart, shippingAddress) {
      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!shippingAddress) {
        throw new errors.InvalidArgumentError("Parameter 'shippingAddress' is required");
      }

      if (cart.customerInfo.id === 'anonymous' && cart.customerInfo.email === undefined) {
        throw new errors.InvalidArgumentError('Please set email for guest user cart');
      }

      var isMissingFields = !shippingAddress.firstName || !shippingAddress.lastName || !shippingAddress.addressLine1 || !shippingAddress.city || !shippingAddress.countryCode || !shippingAddress.stateCode || !shippingAddress.postalCode;

      if (isMissingFields) {
        throw new errors.InvalidArgumentError('Missing hybris required fields');
      }

      var api = new hybris.UsersApi(this.client); // For a registered user, api.postCartDeliveryAddress() returns an unauthorized error (unknown reason)
      // A workaround: post or put (if already exists a shipping address) the user address and then put that address to the cart by its id.

      var apiCommand;

      if (cart.customerInfo.id === 'current') {
        var userAddressApiCommand = cart.shippingAddress === undefined ? api.postUserAddress(cart.customerInfo.id, this.transformOrderAddress(shippingAddress), {
          fields: 'FULL'
        }) : api.putUserAddress(cart.customerInfo.id, shippingAddress.id, _objectSpread({}, this.transformOrderAddress(shippingAddress), {
          id: shippingAddress.id
        }));
        apiCommand = userAddressApiCommand.then(function (data) {
          return api.putCartDeliveryAddress(cart.customerInfo.id, cart.id, data.id);
        });
      } else {
        apiCommand = api.postCartDeliveryAddress(cart.customerInfo.id, cart.id, this.transformOrderAddress(shippingAddress), _objectSpread({}, opts, {
          fields: 'FULL'
        }));
      }

      return apiCommand.then(function () {
        cart.shippingAddress = shippingAddress;
        return cart;
      })["catch"](function () {
        throw new errors.ServerError('Could not set shipping address');
      });
    }
    /**
     * Get the available payment methods for the cart
     * @param {module:@mobify/commerce-integrations/dist/types.Cart} cart The customer's cart
     * @param {Object} opts
     * @return {Promise<Array.module:@mobify/commerce-integrations/dist/types.PaymentMethod>}
     *
     * Hybris only supports payments by credit card attained from their cardtypes endpoint
     * so this command will return only one payment method with the id 'CREDIT_CARD', name 'Credit Card'
     * and all of it's valid credit card types.
     */

  }, {
    key: "getPaymentMethods",
    value: function getPaymentMethods(cart) {
      var _this26 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var api = new hybris.CardtypesApi(this.client);
      return api.getCardTypes(_objectSpread({}, opts, {
        fields: 'FULL'
      })).then(function (data) {
        return [_this26.parsePaymentMethod({
          id: 'CREDIT_CARD',
          name: 'Credit Card',
          types: data.cardTypes
        })];
      })["catch"](function () {
        throw new errors.ServerError('Could not get payment methods');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "parseOrder",
    value: function parseOrder(data) {
      var order = this.parseCart(data);
      return _objectSpread({}, order, {
        id: data.code,
        creationDate: new Date(data.created),
        status: data.statusDisplay,
        customerInfo: {
          id: data.guestCustomer ? 'anonymous' : 'current',
          email: data.paymentInfo.billingAddress.email
        }
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getOrder",
    value: function getOrder(id) {
      var _this27 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      if (!id) {
        throw new errors.InvalidArgumentError("Parameter 'id' is required");
      }

      var getOrderPromise; // BUG: does not save billing address region when sending billing address through a POST cart payment detail.
      // So when retrieving an order by id, region (stateCode field) may be missing in the returned order.

      if (this.getCustomerIdFromAuth() === 'current') {
        var api = new hybris.UsersApi(this.client);
        getOrderPromise = api.getOrder('current', id, _objectSpread({}, opts, {
          fields: 'FULL'
        }));
      } else {
        var _api = new hybris.OrdersApi(this.client);

        getOrderPromise = _api.getOrder(id, _objectSpread({}, opts, {
          fields: 'FULL'
        }));
      }

      return getOrderPromise.then(function (data) {
        return _this27.parseOrder(data);
      })["catch"](function () {
        throw new errors.ServerError('Order not found');
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "getOrders",
    value: function getOrders(ids) {
      var _this28 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      if (!ids || ids.length <= 0) {
        throw new errors.InvalidArgumentError('Please specify list of order ids to get.');
      }

      var getOrderPromises = ids.map(function (id) {
        return _this28.getOrder(id, opts)["catch"](function () {
          return null;
        });
      });
      return Promise.all(getOrderPromises).then(function (orders) {
        var validOrders = orders.filter(function (order) {
          return !!order;
        });
        return {
          count: validOrders.length,
          data: validOrders,
          total: validOrders.length
        };
      });
    }
    /**
     * @inheritDoc
     */

  }, {
    key: "createOrder",
    value: function createOrder(cart) {
      var _this29 = this;

      var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var api = new hybris.UsersApi(this.client);
      return api.postOrder(cart.customerInfo.id, cart.id, _objectSpread({}, opts, {
        fields: 'FULL'
      })).then(function (data) {
        var order = _this29.parseOrder(data); // BUG: does not save billing address region when sending billing address through a POST cart payment detail.
        // So lets persist this field on the incoming cart parameter to the returned order.


        if (order.billingAddress && !order.billingAddress.stateCode) {
          order.billingAddress.stateCode = cart.billingAddress.stateCode;
        }

        return order;
      })["catch"](function () {
        throw new errors.ServerError('Could not create order');
      });
    }
    /**
     * @inheritDoc
     */
    // eslint-disable-next-line no-unused-vars

  }, {
    key: "addCouponEntry",
    value: function addCouponEntry(cart, couponCode) {
      var _this30 = this;

      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!couponCode) {
        throw new errors.InvalidArgumentError("Parameter 'couponCode' is required");
      }

      var api = new hybris.UsersApi(this.client);
      return api.postCartVoucher(cart.customerInfo.id, cart.id, couponCode).then(function () {
        return _this30.getCart(cart.id);
      })["catch"](function () {
        throw new errors.ServerError('Could Not Add Coupon');
      });
    }
    /**
     * @inheritDoc
     */
    // eslint-disable-next-line no-unused-vars

  }, {
    key: "removeCouponEntry",
    value: function removeCouponEntry(cart, couponEntryId) {
      var _this31 = this;

      var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

      if (!cart) {
        throw new errors.InvalidArgumentError("Parameter 'cart' is required");
      }

      if (!couponEntryId) {
        throw new errors.InvalidArgumentError("Parameter 'couponEntryId' is required");
      }

      var api = new hybris.UsersApi(this.client);
      return api.deleteCartVouchers(cart.customerInfo.id, cart.id, couponEntryId).then(function () {
        return _this31.getCart(cart.id);
      })["catch"](function () {
        throw new errors.ServerError('Could Not Remove Coupon');
      });
    }
  }], [{
    key: "fromConfig",
    value: function fromConfig(config) {
      return new this.prototype.constructor({
        client: new hybris.ApiClient(config.clientConfig),
        catalogId: config.catalogId,
        catalogVersionId: config.catalogVersionId,
        authentication: config.authentication
      });
    }
  }]);

  return HybrisConnector;
}();

exports.HybrisConnector = HybrisConnector;