/**
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 { dtm } = require('@contrast/protobuf-api');

/**
 * Main translation function. This will dispatch to the appropriate translation
 * function based on parameters provided. If body data is provided, the message
 * constructed will contain only body data (in terms of request data). Otherwise
 * all _but_ body data will be included on the returned message.
 * @param {Object} data
 * @returns {RawRequest}
 */
function RawRequest(data = {}) {
  return data.incomingMessage
    ? RawRequestWithoutBody(data)
    : RawRequestWithBody(data);
}

/**
 * Returns the protobuf instance with only the body field set. This supports
 * setting the body from a string, a buffer instance, or an array of buffers.
 * @param {String} requestId
 * @param {String} [bodyStr]
 * @param {Buffer[]} [chunks]
 * @param {Buffer} [buffer]
 * @returns {RawRequest}
 */
function RawRequestWithBody({ requestId, bodyStr, chunks, buffer }) {
  let _body = '';

  if (Array.isArray(chunks)) {
    if (typeof chunks[0] == 'string') {
      const bStr = ''.concat('', ...chunks);
      _body = Buffer.from(bStr).toString('base64');
    } else {
      const bBuffer = Buffer.concat(chunks);
      _body = Uint8Array.from(bBuffer);
    }
  } else if (buffer) {
    _body = Uint8Array.from(buffer);
  } else if (bodyStr) {
    _body = Buffer.from(bodyStr).toString('base64');
  }

  return new dtm.RawRequest({
    0: requestId, //             1         uuid
    1: new Date().getTime(), //  2 timestamp_ms
    15: _body //                16 request_body
  });
}

/**
 * Returns the protobuf instance without all fields set other than body.
 * @param {String} requestId
 * @param {IncomingMessage} incomingMessage
 * @returns {RawRequest}
 */
function RawRequestWithoutBody({ requestId, incomingMessage }) {
  const { method, url, httpVersion, socket, rawHeaders } = incomingMessage;

  return new dtm.RawRequest({
    0: requestId,
    1: Date.now(),
    3: `${method} ${url} HTTP/${httpVersion}`,
    5: socket.remoteAddress,
    6: socket.remoteFamily === 'IPv4' ? 4 : 6,
    7: socket.remotePort,
    9: socket.localAddress,
    10: socket.remoteFamily === 'IPv4' ? 4 : 6,
    11: socket.localPort,
    13: formatHeaders(rawHeaders)
  });
}

/**
 * Formats the <code>IncomingMessage</code>'s raw headers to the
 * <code>SimplePair</code> message type.
 * @param {Array[]} rawHeaders
 * @returns {SimplePair[]}
 */
function formatHeaders(rawHeaders) {
  const collection = [];
  for (let i = 0; i < rawHeaders.length; i += 2) {
    collection.push(
      new dtm.SimplePair([rawHeaders[i], rawHeaders[i + 1]]).array
    );
  }
  return collection;
}

module.exports = RawRequest;
