'use strict';

/** The `ImmutableMap` class is a TypeScript implementation of an immutable map data structure that allows for mutation through a `withMutation` method. */
class ImmutableMap {
  /**
   * The constructor initializes a new ImmutableMap object with an optional initial value and an optional callback function for mutation events.
   *
   * @param {Map<unknown,unknown>} [initialValue] - The `initialValue` parameter is an optional parameter that allows you to
   * provide an initial value for the `Map`. It is of type `Map<K, V>`, where `K` represents the key
   * type and `V` represents the value type.
   * @param {(newMap: ImmutableMap<unknown, unknown>) => void} [onMutate] - The `onMutate` parameter is a callback function that is called whenever the
   * `ImmutableMap` is mutated (i.e., when a key-value pair is added, updated, or removed). It receives
   * the new `ImmutableMap` as an argument. This callback function can be used to perform any
   */
  constructor(initialValue, onMutate) {
    this.size = 0;
    this.map = new Map();
    if (initialValue) {
      this.map = initialValue;
      this.size = initialValue.size;
    }
    if (typeof onMutate === 'function') {
      this.onMutate = onMutate;
    }
  }
  /**
   * The "has" function checks if a key exists in a map.
   *
   * @param {unknown} key - The parameter "key" is of type K, which means it can be any type. It is used to specify the key that you want to check for existence in the map.
   * @returns {boolean} A boolean value indicating whether the key exists in the map.
   */
  has(key) {
    return this.map.has(key);
  }
  /**
   * The function returns an iterator of the keys in a map.
   *
   * @typedef K generic type
   * @returns {IterableIterator<K>} The `keys()` method is returning an `IterableIterator` object that contains the keys of the `map`.
   */
  keys() {
    return this.map.keys();
  }
  /**
   * The `set` function adds a key-value pair to an immutable map.
   *
   * @typedef K generic type
   * @typedef V generic type
   * @param {unknown} key - The key parameter is the key of the entry that you want to add or update in the map. It is of type K, which represents the type of the keys in the map.
   * @param {unknown} value - The value parameter represents the value that you want to associate with the specified key in the ImmutableMap.
   * @returns {ImmutableMap<K, V>} The method is returning an ImmutableMap<K, V>.
   */
  set(key, value) {
    return this.withMutation((map) => {
      map.set(key, value);
    });
  }
  /**
   * The get function retrieves the value associated with a given key from a map.
   *
   * @typedef V generic type
   * @param {unknown} key - The parameter "key" is of type "K", which means it can be any type.
   * @returns {Nullable<V>} The value associated with the given key in the map.
   */
  get(key) {
    return this.map.get(key);
  }
  /**
   * The values() method of Map instances returns a new map iterator object that contains the values for each element in this map in insertion order.
   *
   * @typedef V generic type
   * @returns {IterableIterator<V>} An iterator of the values in the map.
   */
  values() {
    return this.map.values();
  }
  /**
   * The `delete` function removes an entry from an ImmutableMap by deleting the corresponding key-value pair from the underlying Map.
   *
   * @typedef K generic type
   * @typedef V generic type
   * @param {unknown} key - The key parameter is the key of the entry that you want to delete from the ImmutableMap.
   * @returns {ImmutableMap<K, V>} The method is returning an ImmutableMap<K, V>.
   */
  delete(key) {
    return this.withMutation((map) => {
      map.delete(key);
    });
  }
  /**
   * The `clear` function clears all key-value pairs from an ImmutableMap.
   *
   * @typedef K generic type
   * @typedef V generic type
   * @returns {ImmutableMap<K, V>} The method is returning an ImmutableMap<K, V>.
   */
  clear() {
    return this.withMutation((map) => {
      map.clear();
    });
  }
  /**
   * The function is called when the map is mutated
   *
   * @param {ImmutableMap<unknown,unknown>} _newMap - The new set that was created.
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  onMutate(_newMap) {
    return;
  }
  /**
   * The `withMutation` function takes a callback function that mutates a map and returns a new immutable map with the changes applied.
   *
   * @typedef K generic type
   * @typedef V generic type
   * @param {(map: Map<unknown, unknown>) => void} fn - A function that takes a map as its parameter and does some mutation on it.
   * @returns {ImmutableMap<K, V>} a new instance of `ImmutableMap<K, V>`.
   */
  withMutation(fn) {
    const newMap = new Map(this.map);
    fn(newMap);
    const newImmutableMap = new ImmutableMap(newMap, this.onMutate);
    this.onMutate(newImmutableMap);
    return newImmutableMap;
  }
}

exports.ImmutableMap = ImmutableMap;
