/**
Copyright: 2024 Contrast Security, Inc
  Contact: support@contrastsecurity.com
  License: Commercial

  NOTICE: This Software and the patented inventions embodied within may only be
  used as part of Contrast Security’s commercial offerings. Even though it is
  made available through public repositories, use of this Software is subject to
  the applicable End User Licensing Agreement found at
  https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
  between Contrast Security and the End User. The Software may not be reverse
  engineered, modified, repackaged, sold, redistributed or otherwise used in a
  way not consistent with the End User License Agreement.
*/
'use strict';

const {
  AUTHENTICATION,
  INPUT_TYPES,
  RESPONSE_TYPES
} = require('../../constants');
const {
  AUTHENTICATION: AUTH_ENUM,
  INPUT_TYPES: INPUT_ENUM,
  RESPONSE_TYPES: RESPONSE_ENUM,
  DOCUMENT_TYPES
} = require('./enums');

const helpers = exports;

/**
 * We wrap the google-protobuf models so that we can customize `.toObject()`.
 * When the method is invoked, we can provide custom logic to act on the return
 * value. The main use case for this is to convert integer-valued enumerated
 * types to their human-friendly string representations (see `./enums.js`).
 * @param {Object} model A `google-protobuf` model instance
 * @param {Function} fn Recieves the return value of `toObject()`
 * @returns {Object} The model, having it's `toObject()` method patched
 */
helpers.wrapToObject = (model, fn = (x) => x) => {
  if (!model || typeof model.toObject !== 'function') {
    return model;
  }

  ((toObject) => {
    model.toObject = function(...args) {
      const value = toObject.apply(this, args);
      return fn(value) || value;
    };
  })(model.toObject);

  return model;
};

/**
 * Given an input type, return the appropriate enum
 * @param type
 * @return {number}
 */
helpers.resolveInputType = (type) => INPUT_ENUM[INPUT_TYPES[type]];

/**
 * Given a doctype, return the appropriate enum.
 * @param {string} type
 * @return {number}
 */
helpers.resolveDocumentType = (type) => {
  switch (type) {
    case INPUT_TYPES.JSON_VALUE:
      return DOCUMENT_TYPES.JSON;
    case INPUT_TYPES.XML_VALUE:
      return DOCUMENT_TYPES.XML_VALUE;
    default:
      return DOCUMENT_TYPES.NORMAL;
  }
};

/**
 * Given a response type, return the appropriate enum.
 * @param {string} type
 * @return {number}
 */
helpers.resolveResponseType = (type) => RESPONSE_ENUM[RESPONSE_TYPES[type]];

/**
 * Given an authentication result, return the appropriate enum
 * @param {Object} opts
 * @param {string} opts.result
 * @return {number}
 */
helpers.resolveAuthentication = ({ result }) =>
  AUTH_ENUM[result] || AUTH_ENUM[AUTHENTICATION.LOGIN_UNKNOWN];

/**
 * Given a model translation function and a collection, will map the collection
 * over the function and return the <code>array</code> value of each result,
 * producing a multi-dimensional array.
 * @param {Function} Model The translation function returning a model instance
 * @param {Array[*]} coll Collection of items to undergo translation
 * @return {Array}
 */
helpers.mapToModelArray = (Model, coll = []) =>
  coll.map((el) => Model(el).array);

/**
 * maps obj (flat object of <T, T>) to a protobuf-style map
 * @param {Object} obj the object form of the mapping
 */
helpers.objToMap = function mapObj(obj = {}) {
  const protobufMap = Object.keys(obj).map((key) => [key, obj[key]]);

  // if the map is empty then we don't want to populate the array
  // and instead want to report that there were no obj.
  return protobufMap;
};
