/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * This map will return the default value of 0 if the decoded body size for
 * a given channel was not received yet.
 *
 * Bug 1959614: The NetworkDecodedBodySizeMap used to setup promises to wait for
 * the decoded body size to be ready. However the timing differences from this
 * change leads to regressions in Playwright tests. The new implementation of
 * the map is only synchronous and if the size was not received yet, 0 will be
 * returned. In theory, the decoded body size should be set via
 * http-on-before-stop-request which should be received before the request is
 * stopped.
 */
export class NetworkDecodedBodySizeMap {
  #authenticationAttemptsMap;
  #channelIdToBodySizeMap;

  constructor() {
    this.#authenticationAttemptsMap = new Map();
    this.#channelIdToBodySizeMap = new Map();
  }

  destroy() {
    // Reset both maps rather than making them null to avoid errors in case any
    // API is called after the helper was destroyed.
    this.#authenticationAttemptsMap = new Map();
    this.#channelIdToBodySizeMap = new Map();
  }

  getDecodedBodySize(channelId) {
    const decodedBodySize = this.#channelIdToBodySizeMap.get(channelId);
    return typeof decodedBodySize === "number" ? decodedBodySize : 0;
  }

  /**
   * For authentication attempts for the same request, keep track of the
   * authentication chain in order to resolve the final getDecodedBodySize when
   * the information is received from the content process.
   *
   * @param {number} previousChannelId
   *     The channel ID of the previous channel in the authentication chain.
   * @param {number} nextChannelId
   *     The channel ID of the new channel in the authentication chain.
   */
  setAuthenticationAttemptMapping(previousChannelId, nextChannelId) {
    if (previousChannelId === nextChannelId) {
      // If the preference network.auth.use_redirect_for_retries is set to false
      // all channels in the authentication chain will share the same channelId.
      // In this case there is no need to set anything in the map, the content
      // size will be set for the correct channelId by design.
      return;
    }
    this.#authenticationAttemptsMap.set(previousChannelId, nextChannelId);
  }

  /**
   * Set the decodedBodySize for the provided channelId. If the channel is part
   * of an authentication chain, the decoded body size will instead be set for
   * the current channel of the chain.
   *
   * @param {number} channelId
   *     The id of the channel to update.
   * @param {number} decodedBodySize
   *     The decoded body size for the channel.
   */
  setDecodedBodySize(channelId, decodedBodySize) {
    if (this.#authenticationAttemptsMap.has(channelId)) {
      // If there is an auth chain for this channel, setDecodedBodySize will be
      // called for the original channel ID. Traverse the authentication attempt
      // map to set the decoded body size for the current channel in the chain.
      this.setDecodedBodySize(
        this.#authenticationAttemptsMap.get(channelId),
        decodedBodySize
      );
      return;
    }

    this.#channelIdToBodySizeMap.set(channelId, decodedBodySize);
  }

  delete(channelId) {
    this.#authenticationAttemptsMap.delete(channelId);
    this.#channelIdToBodySizeMap.delete(channelId);
  }
}
