/**
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';

/**
 * Propagation Utility
 *
 * @module lib/assess/propagators/utils
 * @see module:lib/assess/propagators/manager
 */

/**
 * @static
 * @param {*} val The value of which we want the original value - <code>.valueof()</code>.
 */
function origValue(val) {
  if (val && typeof val.valueOf === 'function') {
    val = val.valueOf();
  }
  return val;
}

/**
 * @static
 * @param {*} val The value of which we want the original value - <code>.valueof()</code>.
 * @param {*} max The max depth to traverse
 */
function deepOrigValue(val, max = 10) {
  const depth = 0;

  function walk(item, depth) {
    if (Array.isArray(item) && depth < max) {
      depth++;
      return item.map(function (item) {
        return walk(item, depth);
      });
    }

    return origValue(item);
  }

  return walk(val, depth);
}

/**
 * It extracts the appropriate value for an arg based on the
 * noNegatives and length of source
 *
 * @param {Object} params
 * @param {*} params.arg argument passed for a given index
 * @param {Boolean} params.inclusive include
 * @param {Boolean} [params.allowNegatives=true] flag to indicate if
 * an arg allows negatives. both substr length arg and substring
 * start/end will set the value to 0 if a negative value
 * @param {Int} params.sourceLength length of source string
 * @return {*} possibly transformed arg
 */
function getArgValue({ arg, inclusive, allowNegatives = true, sourceLength }) {
  if (isNaN(arg)) {
    return arg;
  } else if (arg < 0) {
    if (allowNegatives) {
      arg = arg + sourceLength;
      if (inclusive) {
        arg = arg + 1;
      }
    } else {
      arg = 0;
    }

    return arg;
  } else {
    return arg;
  }
}

/**
 * Sets the appropriate value for a param
 * type based on the index value
 *
 * @param {Array} params collection of param configs
 * @param {Arary} args args to the given function
 * @return {Object}
 */
function sortParamTypes(params, args, sourceLength) {
  const paramTypes = {};
  params.forEach(function (param) {
    paramTypes[param.type] = getArgValue({
      arg: args[param.index],
      inclusive: param.inclusive,
      allowNegatives: param.allowNegatives,
      sourceLength,
    });
  });

  return paramTypes;
}

/**
 * Checks whether the input is an object
 * @param {*} value
 * @return {boolean}
 */
function isObject(value) {
  if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
    return true;
  }

  return false;
}

function setProxyTaggedString(contrastData, input, tracker) {
  let proxyTaggedString;

  if (contrastData.proxyTaggedString) {
    proxyTaggedString = contrastData.proxyTaggedString;
  } else {
    const { str } = tracker.track(input);
    proxyTaggedString = str;
  }

  contrastData.proxyTaggedString = proxyTaggedString;

  return proxyTaggedString;
}

function getContrastData(data) {
  return data.args[1].schema && data.args[1].schema.__CONTRAST__;
}

module.exports.origValue = origValue;
module.exports.deepOrigValue = deepOrigValue;
module.exports.sortParamTypes = sortParamTypes;
module.exports.isObject = isObject;
module.exports.setProxyTaggedString = setProxyTaggedString;
module.exports.getContrastData = getContrastData;
