/**
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 { IMPORTANCE } = require('../constants.js');
const Sample = require('../reporter/models/app-activity/sample');

// NOTE(ehden): we may want to roll the SampleAggregator util into and assess whether
// we should use Sets internally or something like b tree to optimize sorting
// on insertion or later
class Samples {
  constructor() {
    this._definite = new Map();
    this._worthWatching = new Map();

    // always add to this when adding to one or the other to
    // eliminate needing to merge objects to provide all samples
    this._all = new Map();
  }

  getDefinite(id) {
    return this._getRuleSamples(id, IMPORTANCE.DEFINITE);
  }

  getWorthWatching(id) {
    return this._getRuleSamples(id, IMPORTANCE.WORTH_WATCHING);
  }

  getAll(id) {
    if (this._all.has(id)) {
      return this._all.get(id);
    }

    const s = new Set();
    this._all.set(id, s);
    return s;
  }

  /**
   * Returns a set of Sample's filtered by type
   * @param {string} type
   * @returns {Set}
   */
  getAllByType(type) {
    const results = new Set();

    for (const set of this._all.values()) {
      for (const sample of set) {
        if (sample.input.type === type) {
          results.add(sample);
        }
      }
    }

    return results;
  }

  /**
   * Creates a sample from the arguments and adds to the appropriate collection,
   * either worth_watching or definite bucket.
   * @param {string} id
   * @param {Object} attributes
   * @param {Object} input
   * @param {Assessment} evaluation
   * @param {String} classification
   * @param {ApplicationContext} appContext
   * @returns {Sample}
   */
  addRuleSample({
    id,
    attributes,
    input,
    evaluation,
    classification,
    appContext
  }) {
    const path = input.documentPath;
    const classifiedAs = classification || evaluation.results.importance;

    if (this.ruleIncludesPath(id, path, classifiedAs)) {
      return;
    }

    const sample = new Sample(input, appContext, evaluation);

    if (attributes) {
      Object.assign(sample, {
        effective: false,
        confirmedAttack: true,
        applicable: true,
        ...attributes
      });
    }

    return this.addRawSample(id, sample, classifiedAs);
  }

  /**
   * Add an already existing sample
   *
   * @param {string} id rule id
   * @param {Sample} sample
   * @param {string} confidence
   */
  addRawSample(id, sample, confidence) {
    const samples = this._getRuleSamples(id, confidence);
    samples.add(sample);
    this.getAll(id).add(sample);

    return sample;
  }

  _getRuleSamples(id, confidence) {
    let collection;

    switch (confidence) {
      case IMPORTANCE.WORTH_WATCHING:
        collection = this._worthWatching;
        break;

      case IMPORTANCE.DEFINITE:
      // fallthrough

      default:
        collection = this._definite;
        break;
    }

    if (collection.has(id)) {
      return collection.get(id);
    }

    const s = new Set();
    collection.set(id, s);
    return s;
  }

  ruleIncludesPath(id, path, confidence) {
    const samples = this.getDefinite(id, confidence);
    for (const s of samples) {
      const samplePath = s.input.documentPath;
      if (
        path &&
        samplePath &&
        samplePath !== path &&
        samplePath.indexOf(path) === 0
      ) {
        return true;
      }
    }

    return false;
  }
}

module.exports = Samples;
