/**
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.
*/
/*
 * This file exports lookup objects which support the mapping of integer-valued
 * enums to their human-friendly string representations. We perform this mapping
 * when a model is converted to a plain javascript object, by way of
 * `model.toObject().
 *
 * We also use these objects to map strings to integers. The `google-protobuf`
 * models require that the values used to set enums be integers; there is no
 * built-in type conversion. If a string is used to set a value then the model
 * will throw an exception during serialization.
 *
 * These objects are meant to be used only in the translation files located in
 * `./to-protobuf/`. Those using the translation API simply pass domain models
 * as function arguments, and these lookups happen behind the scenes in the
 * translation functions and return the equivalent protobuf models.
 *
 * Example:
 *  const model = new models.StringDefinition();
 *  model.setType('KEYWORD'); // This sets the underlying value, _but_
 *  model.serializeBinary(); // this will throw a type exception later on.
 *
 *  model.setType(0) // This is the proper way to set the `type` to KEYWORD
 */

'use strict';

const {
  ACTIONS,
  ASSESS_STACKTRACES,
  AUTHENTICATION,
  CONNECTION_TYPES,
  DEBUG_LOG_LEVELS,
  DOCUMENT_TYPES,
  EVENT_TYPES,
  EXCLUSION_INPUT_TYPES,
  EXCLUSION_TYPES,
  EXCLUSION_MATCH_STRATEGIES,
  IMPORTANCE,
  INPUT_TYPES,
  INSTRUMENTATION_MODE,
  PROTECT_RULE_DEFINITION_TYPES,
  PROTECTION_MODES,
  REACTION_OPERATION,
  RESPONSE_TYPES,
  SYSLOG_SEVERITIES,
  VIRTUAL_PATCH_INPUT_TYPES,
  VIRTUAL_PATCH_EVALUATIONS
} = require('../../constants');

/**
 * Transposes key/values of an object and assigns the values to the object.
 * This mutate <code>object</code> argument; also assumes argument is "flat".
 * @param {Object} object The object on which to operate
 * @returns {Object}
 */
const assignInverse = (target) => Object.assign(target || {}, invert(target));
module.exports.assignInverse = assignInverse;

module.exports.ACTIONS = assignInverse({
  [ACTIONS.CREATION]: 0,
  [ACTIONS.PROPAGATION]: 1,
  [ACTIONS.TRIGGER]: 2,
  [ACTIONS.TAG]: 3,
  [ACTIONS.A2A]: 4,
  [ACTIONS.A2P]: 5,
  [ACTIONS.A2O]: 6,
  [ACTIONS.A2R]: 7,
  [ACTIONS.O2A]: 8,
  [ACTIONS.O2P]: 9,
  [ACTIONS.O2O]: 10,
  [ACTIONS.O2R]: 11,
  [ACTIONS.P2A]: 12,
  [ACTIONS.P2P]: 13,
  [ACTIONS.P2O]: 14,
  [ACTIONS.P2R]: 15
});

module.exports.ASSESS_STACKTRACES = assignInverse({
  [ASSESS_STACKTRACES.UNDEFINED]: 0,
  [ASSESS_STACKTRACES.ALL]: 1,
  [ASSESS_STACKTRACES.SOME]: 2,
  [ASSESS_STACKTRACES.NONE]: 3
});

module.exports.AUTHENTICATION = assignInverse({
  [AUTHENTICATION.UNAUTHENTICATED]: 0,
  [AUTHENTICATION.AUTHENTICATED]: 1,
  [AUTHENTICATION.LOGIN_FAIL]: 2,
  [AUTHENTICATION.LOGIN_SUCCESS]: 3,
  [AUTHENTICATION.LOGIN_UNKNOWN]: 4
});

module.exports.DOCUMENT_TYPES = assignInverse({
  [DOCUMENT_TYPES.NORMAL]: 0,
  [DOCUMENT_TYPES.JSON]: 1,
  [DOCUMENT_TYPES.XML]: 2
});

module.exports.EVENT_TYPES = assignInverse({
  [EVENT_TYPES.METHOD]: 0,
  [EVENT_TYPES.PROPAGATION]: 1,
  [EVENT_TYPES.TAG]: 2
});

module.exports.EXCLUSION_TYPES = assignInverse({
  [EXCLUSION_TYPES.URL]: 0,
  [EXCLUSION_TYPES.INPUT]: 1,
  [EXCLUSION_TYPES.CODE]: 2
});

module.exports.EXCLUSION_INPUT_TYPES = assignInverse({
  [EXCLUSION_INPUT_TYPES.UNDEFINED]: 0,
  [EXCLUSION_INPUT_TYPES.COOKIE]: 1,
  [EXCLUSION_INPUT_TYPES.PARAMETER]: 2,
  [EXCLUSION_INPUT_TYPES.HEADER]: 3,
  [EXCLUSION_INPUT_TYPES.BODY]: 4,
  [EXCLUSION_INPUT_TYPES.QUERYSTRING]: 5
});

module.exports.EXCLUSION_MATCH_STRATEGIES = assignInverse({
  [EXCLUSION_MATCH_STRATEGIES.ALL]: 0,
  [EXCLUSION_MATCH_STRATEGIES.ONLY]: 1
});

module.exports.INPUT_TYPES = assignInverse({
  [INPUT_TYPES.UNDEFINED_TYPE]: 0,
  [INPUT_TYPES.BODY]: 1,
  [INPUT_TYPES.COOKIE_NAME]: 2,
  [INPUT_TYPES.COOKIE_VALUE]: 3,
  [INPUT_TYPES.HEADER]: 4,
  [INPUT_TYPES.PARAMETER_NAME]: 5,
  [INPUT_TYPES.PARAMETER_VALUE]: 6,
  [INPUT_TYPES.QUERYSTRING]: 7,
  [INPUT_TYPES.URI]: 8,
  [INPUT_TYPES.SOCKET]: 9,
  [INPUT_TYPES.JSON_VALUE]: 10,
  [INPUT_TYPES.JSON_ARRAYED_VALUE]: 11,
  [INPUT_TYPES.MULTIPART_CONTENT_TYPE]: 16,
  [INPUT_TYPES.MULTIPART_VALUE]: 17,
  [INPUT_TYPES.MULTIPART_FIELD_NAME]: 18,
  [INPUT_TYPES.MULTIPART_NAME]: 19,
  [INPUT_TYPES.XML_VALUE]: 20,
  [INPUT_TYPES.DWR_VALUE]: 21,
  [INPUT_TYPES.METHOD]: 22,
  [INPUT_TYPES.REQUEST]: 23,
  [INPUT_TYPES.URL_PARAMETER]: 24,
  [INPUT_TYPES.UNKNOWN]: 99
});

module.exports.SCORE_TYPES = assignInverse({
  [IMPORTANCE.NONE]: 0,
  [IMPORTANCE.WORTH_WATCHING]: 1,
  [IMPORTANCE.DEFINITE]: 2
});

module.exports.INSTRUMENTATION_MODE = assignInverse({
  [INSTRUMENTATION_MODE.UNDEFINED]: 0,
  [INSTRUMENTATION_MODE.AGENT_CONFIG]: 1,
  [INSTRUMENTATION_MODE.DUPLEX]: 2,
  [INSTRUMENTATION_MODE.PROFILER]: 3
});

module.exports.PROTECTION_RULE_MODES = assignInverse({
  [PROTECTION_MODES.NO_ACTION]: 0,
  [PROTECTION_MODES.MONITOR]: 1,
  [PROTECTION_MODES.BLOCK]: 2,
  [PROTECTION_MODES.BLOCK_AT_PERIMETER]: 3,
  [PROTECTION_MODES.PERMIT]: 4
});

module.exports.LOG_LEVEL = assignInverse({
  [DEBUG_LOG_LEVELS.TRACE]: 0,
  [DEBUG_LOG_LEVELS.DEBUG]: 1,
  [DEBUG_LOG_LEVELS.INFO]: 2,
  [DEBUG_LOG_LEVELS.WARN]: 3,
  [DEBUG_LOG_LEVELS.ERROR]: 4
});

module.exports.REACTION_OPERATION = assignInverse({
  [REACTION_OPERATION.NOOP]: 0,
  [REACTION_OPERATION.DISABLE]: 1,
  [REACTION_OPERATION.INVENTORY]: 2
});

module.exports.RESPONSE_TYPES = assignInverse({
  [RESPONSE_TYPES.NO_ACTION]: 0,
  [RESPONSE_TYPES.BLOCKED]: 1,
  [RESPONSE_TYPES.MONITORED]: 2,
  [RESPONSE_TYPES.PROBED]: 3,
  [RESPONSE_TYPES.BLOCKED_AT_PERIMETER]: 4,
  [RESPONSE_TYPES.AGGREGATED]: 5
});

module.exports.STRING_DEFINITION_TYPE = assignInverse({
  [PROTECT_RULE_DEFINITION_TYPES.KEYWORD]: 0,
  [PROTECT_RULE_DEFINITION_TYPES.PATTERN]: 1
});

module.exports.SYSLOG_CONNECTION_TYPES = assignInverse({
  [CONNECTION_TYPES.UNENCRYPTED]: 0,
  [CONNECTION_TYPES.ENCRYPTED]: 1
});

module.exports.SYSLOG_SEVERITIES = assignInverse({
  [SYSLOG_SEVERITIES.UNDEFINED]: 0,
  [SYSLOG_SEVERITIES.ALERT]: 1,
  [SYSLOG_SEVERITIES.CRITICAL]: 2,
  [SYSLOG_SEVERITIES.ERROR]: 3,
  [SYSLOG_SEVERITIES.WARNING]: 4,
  [SYSLOG_SEVERITIES.NOTICE]: 5,
  [SYSLOG_SEVERITIES.INFO]: 6,
  [SYSLOG_SEVERITIES.DEBUG]: 7
});

module.exports.VIRTUAL_PATCH_INPUT_TYPES = assignInverse({
  [VIRTUAL_PATCH_INPUT_TYPES.UNKNOWN_INPUT]: 0,
  [VIRTUAL_PATCH_INPUT_TYPES.USERAGENT]: 1,
  [VIRTUAL_PATCH_INPUT_TYPES.REFERER]: 2,
  [VIRTUAL_PATCH_INPUT_TYPES.URL]: 3,
  [VIRTUAL_PATCH_INPUT_TYPES.PARAMETER]: 4,
  [VIRTUAL_PATCH_INPUT_TYPES.HEADER]: 5,
  [VIRTUAL_PATCH_INPUT_TYPES.CODE]: 6
});

module.exports.VIRTUAL_PATCH_EVALUATIONS = assignInverse({
  [VIRTUAL_PATCH_EVALUATIONS.UNKNOWN_EVALUATION]: 0,
  [VIRTUAL_PATCH_EVALUATIONS.MATCHES]: 1,
  [VIRTUAL_PATCH_EVALUATIONS.DOESNT_MATCH]: 2,
  [VIRTUAL_PATCH_EVALUATIONS.CONTAINS]: 3,
  [VIRTUAL_PATCH_EVALUATIONS.DOESNT_CONTAIN]: 4,
  [VIRTUAL_PATCH_EVALUATIONS.EQUALS]: 5,
  [VIRTUAL_PATCH_EVALUATIONS.DOESNT_EQUAL]: 6
});

/**
 * @param {Object} object The object to invert.
 * @returns {Object} Returns the new inverted object.
 * @example
 *
 * const object = { 'a': 1, 'b': 2, 'c': 1 }
 *
 * invert(object)
 * // => { '1': 'c', '2': 'b' }
 */
function invert(object) {
  const result = {};
  if (object) {
    Object.keys(object).forEach((key) => {
      const value = object[key].toString();
      result[value] = key;
    });
  }
  return result;
}
