"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const predicateUtils = require("./predicate-utils.cjs");
class DeduplicatedLoadSubset {
  constructor(opts) {
    this.unlimitedWhere = void 0;
    this.hasLoadedAllData = false;
    this.limitedCalls = [];
    this.inflightCalls = [];
    this.generation = 0;
    this.loadSubset = (options) => {
      if (this.hasLoadedAllData) {
        this.onDeduplicate?.(options);
        return true;
      }
      if (this.unlimitedWhere !== void 0 && options.where !== void 0) {
        if (predicateUtils.isWhereSubset(options.where, this.unlimitedWhere)) {
          this.onDeduplicate?.(options);
          return true;
        }
      }
      if (options.limit !== void 0) {
        const alreadyLoaded = this.limitedCalls.some(
          (loaded) => predicateUtils.isPredicateSubset(options, loaded)
        );
        if (alreadyLoaded) {
          this.onDeduplicate?.(options);
          return true;
        }
      }
      const matchingInflight = this.inflightCalls.find(
        (inflight) => predicateUtils.isPredicateSubset(options, inflight.options)
      );
      if (matchingInflight !== void 0) {
        const prom = matchingInflight.promise;
        prom.then(() => this.onDeduplicate?.(options)).catch();
        return prom;
      }
      const trackingOptions = cloneOptions(options);
      const loadOptions = cloneOptions(options);
      if (this.unlimitedWhere !== void 0 && options.limit === void 0) {
        loadOptions.where = predicateUtils.minusWherePredicates(loadOptions.where, this.unlimitedWhere) ?? loadOptions.where;
      }
      const resultPromise = this._loadSubset(loadOptions);
      if (resultPromise === true) {
        this.updateTracking(trackingOptions);
        return true;
      } else {
        const capturedGeneration = this.generation;
        const inflightEntry = {
          options: trackingOptions,
          promise: resultPromise.then((result) => {
            if (capturedGeneration === this.generation) {
              this.updateTracking(trackingOptions);
            }
            return result;
          }).finally(() => {
            const index = this.inflightCalls.indexOf(inflightEntry);
            if (index !== -1) {
              this.inflightCalls.splice(index, 1);
            }
          })
        };
        this.inflightCalls.push(inflightEntry);
        return inflightEntry.promise;
      }
    };
    this._loadSubset = opts.loadSubset;
    this.onDeduplicate = opts.onDeduplicate;
  }
  /**
   * Reset all tracking state.
   * Clears the history of loaded predicates and in-flight calls.
   * Use this when you want to start fresh, for example after clearing the underlying data store.
   *
   * Note: Any in-flight requests will still complete, but they will not update the tracking
   * state after the reset. This prevents old requests from repopulating cleared state.
   */
  reset() {
    this.unlimitedWhere = void 0;
    this.hasLoadedAllData = false;
    this.limitedCalls = [];
    this.inflightCalls = [];
    this.generation++;
  }
  updateTracking(options) {
    if (options.limit === void 0) {
      if (options.where === void 0) {
        this.hasLoadedAllData = true;
        this.unlimitedWhere = void 0;
        this.limitedCalls = [];
        this.inflightCalls = [];
      } else if (this.unlimitedWhere === void 0) {
        this.unlimitedWhere = options.where;
      } else {
        this.unlimitedWhere = predicateUtils.unionWherePredicates([
          this.unlimitedWhere,
          options.where
        ]);
      }
    } else {
      this.limitedCalls.push(options);
    }
  }
}
function cloneOptions(options) {
  return {
    ...options,
    orderBy: options.orderBy?.map((clause) => ({
      ...clause,
      compareOptions: { ...clause.compareOptions }
    })),
    cursor: options.cursor ? { ...options.cursor } : void 0
  };
}
exports.DeduplicatedLoadSubset = DeduplicatedLoadSubset;
exports.cloneOptions = cloneOptions;
//# sourceMappingURL=subset-dedupe.cjs.map
