import { assert } from "../../../../shared/src/asserts.js";
class SlidingWindowLimiter {
  #windowSizeMs;
  #maxMutations;
  #priorWindow;
  #nextWindow;
  constructor(windowSizeMs, maxMutations) {
    this.#windowSizeMs = windowSizeMs;
    this.#maxMutations = maxMutations;
    [this.#priorWindow, this.#nextWindow] = this.#newWindows();
  }
  canDo() {
    const now = Date.now();
    if (now - this.#windowSizeMs > this.#nextWindow.start + this.#windowSizeMs) {
      [this.#priorWindow, this.#nextWindow] = this.#newWindows();
    }
    if (now - this.#windowSizeMs >= this.#nextWindow.start) {
      this.#rotateWindows();
    }
    const totalCalls = this.totalCallsForTime(now);
    const canDo = totalCalls < this.#maxMutations;
    if (canDo) {
      if (now < this.#nextWindow.start) {
        this.#priorWindow.count++;
      } else {
        this.#nextWindow.count++;
      }
    }
    return canDo;
  }
  totalCallsForTime(now) {
    let fraction;
    if (now < this.#priorWindow.start + this.#windowSizeMs) {
      fraction = 1;
    } else {
      fraction = (this.#priorWindow.start + (this.#windowSizeMs - 1) - (now - this.#windowSizeMs)) / this.#windowSizeMs;
    }
    if (fraction < 0) {
      fraction = 0;
    }
    assert(
      fraction <= 1,
      "The past cannot contribute more than the a full window."
    );
    const totalCalls = this.#priorWindow.count * fraction + this.#nextWindow.count;
    return totalCalls;
  }
  get priorWindow() {
    return this.#priorWindow;
  }
  get nextWindow() {
    return this.#nextWindow;
  }
  /**
   * If there were no mutations in over `windowSizeMs` we need new windows.
   */
  #newWindows() {
    const now = Date.now();
    const start = now - now % this.#windowSizeMs;
    return [
      {
        // clamp to a `windowSizeMs` boundary to start the prior window.
        start,
        count: 0
      },
      {
        start: start + this.#windowSizeMs,
        count: 0
      }
    ];
  }
  #rotateWindows() {
    this.#priorWindow = this.#nextWindow;
    this.#nextWindow = {
      start: this.#priorWindow.start + this.#windowSizeMs,
      count: 0
    };
  }
}
export {
  SlidingWindowLimiter
};
//# sourceMappingURL=sliding-window-limiter.js.map
