/**
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 logger = require('../logger');

/**
 * Report debugging and instrumentation metrics
 */
class Metrics {
  constructor() {
    this.config = null;
    this.enabled = false;
  }

  /**
   * Configure and enable metrics configuration
   *
   * @param config agent configuration
   */
  configure(config) {
    if (!config || !config.agent.node.dev.metrics) {
      return;
    }
    this.enabled = true;
    this.client = require('prom-client');
    this.client.collectDefaultMetrics({
      eventLoopMonitoringPrecision: 1
    });
    const fs = require('fs');
    this.hist = {};
    this.logger = logger('contrast:metrics');
    setInterval(() => {
      fs.writeFile(
        './metrics.txt',
        this.client.register.metrics(),
        { flag: 'w' },
        (err) => {
          if (err) {
            this.logger.debug(`failed to write to ./metrics.txt: ${err}`);
          }
        }
      );
    }, 5000).unref();
  }

  /**
   * Record a value in a histogram (creates it if it doesn't exist)
   *
   * @param key the name of the histogram
   * @param help the help text for the histogram
   * @param value the value to record
   */
  histogramRecord(key, help, value) {
    if (!this.enabled) {
      return;
    }
    if (!this.hist[key]) {
      this.hist[key] = new this.client.Histogram({
        name: key,
        help,
        buckets: [2, 5, 10, 20, 40]
      });
    }
    this.hist[key].observe(value);
  }

  /**
   * Add the time it took to send a speedracer message to the corresponding histogram
   *
   * @param msgType the type of speedracer message
   * @param time the time it took in milliseconds
   */
  recordMessageTime(msgType, time) {
    this.histogramRecord(
      `speedracer_${msgType}_latency`,
      `end to end time spent handling a speedracer ${msgType} message`,
      time
    );
  }

  /**
   * Add the time it took to serialize a speedracer message to the corresponding histogram
   *
   * @param msgType the type of speedracer message
   * @param time the time it took in milliseconds
   */
  recordSerializeTime(msgType, time) {
    this.histogramRecord(
      `speedracer_${msgType}_serialize_latency`,
      `time spent serializing a speedracer ${msgType} message`,
      time
    );
  }

  /**
   * Add the time it took to deserialize a speedracer message to the corresponding histogram
   *
   * @param msgType the type of speedracer message
   * @param time the time it took in milliseconds
   */
  recordDeserializeTime(msgType, time) {
    this.histogramRecord(
      `speedracer_${msgType}_deserialize_latency`,
      `time spent deserializing a speedracer ${msgType} message`,
      time
    );
  }

  /**
   * Add the time it took to connect to speedracer to the histogram
   *
   * @param time the time it took in milliseconds
   */
  recordConnectTime(time) {
    this.histogramRecord(
      'speedracer_connect',
      'time from calling connect to callback',
      time
    );
  }

  /**
   * Add the time it took from calling write to the write being successful to the histogram
   *
   * @param time the time it took in milliseconds
   */
  recordWriteTime(time) {
    this.histogramRecord(
      'speedracer_write',
      'time from calling write to callback',
      time
    );
  }
}

const metrics = new Metrics();
module.exports = metrics;
