var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var cursor_exports = {};
__export(cursor_exports, {
  Cursor: () => Cursor
});
module.exports = __toCommonJS(cursor_exports);
var import_core = require("./core");
var import_lazy = require("./lazy");
var import_limit = require("./operators/pipeline/limit");
var import_project = require("./operators/pipeline/project");
var import_skip = require("./operators/pipeline/skip");
var import_sort = require("./operators/pipeline/sort");
var import_util = require("./util");
const OPERATORS = { $sort: import_sort.$sort, $skip: import_skip.$skip, $limit: import_limit.$limit };
class Cursor {
  #source;
  #predicate;
  #projection;
  #options;
  #operators = {};
  #result = null;
  #buffer = [];
  /**
   * Creates an instance of the Cursor class.
   *
   * @param source - The source of data to be iterated over.
   * @param predicate - A function or condition to filter the data.
   * @param projection - An object specifying the fields to include or exclude in the result.
   * @param options - Optional settings to customize the behavior of the cursor.
   */
  constructor(source, predicate, projection, options) {
    this.#source = source;
    this.#predicate = predicate;
    this.#projection = projection;
    this.#options = options;
  }
  /** Returns the iterator from running the query */
  fetch() {
    if (this.#result) return this.#result;
    this.#result = (0, import_lazy.Lazy)(this.#source).filter(this.#predicate);
    const mode = this.#options.processingMode;
    if (mode & import_core.ProcessingMode.CLONE_INPUT) this.#result.map(import_util.cloneDeep);
    for (const op of ["$sort", "$skip", "$limit"]) {
      if ((0, import_util.has)(this.#operators, op)) {
        this.#result = OPERATORS[op](
          this.#result,
          this.#operators[op],
          this.#options
        );
      }
    }
    if (Object.keys(this.#projection).length) {
      this.#result = (0, import_project.$project)(this.#result, this.#projection, this.#options);
    }
    if (mode & import_core.ProcessingMode.CLONE_OUTPUT) this.#result.map(import_util.cloneDeep);
    return this.#result;
  }
  /** Returns an iterator with the buffered data included */
  fetchAll() {
    const buffered = (0, import_lazy.Lazy)([...this.#buffer]);
    this.#buffer.length = 0;
    return (0, import_lazy.concat)(buffered, this.fetch());
  }
  /**
   * Return remaining objects in the cursor as an array. This method exhausts the cursor
   * @returns {Array}
   */
  all() {
    return this.fetchAll().value();
  }
  /**
   * Returns the number of objects return in the cursor. This method exhausts the cursor
   * @returns {Number}
   */
  count() {
    return this.all().length;
  }
  /**
   * Returns a cursor that begins returning results only after passing or skipping a number of documents.
   * @param {Number} n the number of results to skip.
   * @return {Cursor} Returns the cursor, so you can chain this call.
   */
  skip(n) {
    this.#operators["$skip"] = n;
    return this;
  }
  /**
   * Limits the number of items returned by the cursor.
   *
   * @param n - The maximum number of items to return.
   * @returns The current cursor instance for chaining.
   */
  limit(n) {
    this.#operators["$limit"] = n;
    return this;
  }
  /**
   * Returns results ordered according to a sort specification.
   * @param {AnyObject} modifier an object of key and values specifying the sort order. 1 for ascending and -1 for descending
   * @return {Cursor} Returns the cursor, so you can chain this call.
   */
  sort(modifier) {
    this.#operators["$sort"] = modifier;
    return this;
  }
  /**
   * Applies a sort operation to the cursor using the specified sort modifier.
   *
   * @param modifier - An object specifying the sort order. The keys represent
   * the field names, and the values indicate the sort direction (e.g., 1 for
   * ascending and -1 for descending).
   * @returns The current cursor instance for method chaining.
   */
  collation(spec) {
    this.#options = { ...this.#options, collation: spec };
    return this;
  }
  /**
   * Retrieves the next item in the cursor.
   *
   * If there are items in the internal buffer, the next item is returned from the buffer.
   * Otherwise, it fetches the next item from the underlying data source.
   *
   * @returns The next item of type `T` if available, or `undefined` if there are no more items.
   */
  next() {
    if (this.#buffer.length > 0) {
      return this.#buffer.pop();
    }
    const o = this.fetch().next();
    if (o.done) return;
    return o.value;
  }
  /**
   * Determines if there are more elements available in the cursor.
   *
   * This method checks if there are any elements left in the internal buffer.
   * If the buffer is empty, it attempts to fetch the next element from the source.
   * If a new element is found, it is added to the buffer and the method returns `true`.
   * Otherwise, it returns `false` indicating no more elements are available.
   *
   * @returns {boolean} `true` if there are more elements to iterate over, otherwise `false`.
   */
  hasNext() {
    if (this.#buffer.length > 0) return true;
    const o = this.fetch().next();
    if (o.done) return false;
    this.#buffer.push(o.value);
    return true;
  }
  /**
   * Transforms the elements of the cursor using the provided callback function.
   *
   * @template R - The type of the elements in the resulting array.
   * @template T - The type of the elements in the cursor.
   * @param fn - A callback function that is invoked for each element in the cursor.
   *             It receives the current element, its index, and the entire array as arguments.
   * @returns An array of transformed elements of type `R`.
   */
  map(fn) {
    return this.all().map(fn);
  }
  /**
   * Iterates over all elements in the cursor and executes the provided callback function for each element.
   *
   * @param fn - A callback function to execute for each element. The function receives the following arguments:
   *   - `t`: The current element being processed in the cursor.
   *   - `i`: The index of the current element in the array.
   *   - `a`: The array of all elements in the cursor.
   */
  forEach(fn) {
    this.all().forEach(fn);
  }
  /**
   * Returns an iterator for the cursor, allowing it to be used in `for...of` loops.
   * The iterator fetches all the results from the cursor.
   *
   * @returns {Iterator} An iterator over the fetched results.
   */
  [Symbol.iterator]() {
    return this.fetchAll();
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  Cursor
});
