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 densify_exports = {};
__export(densify_exports, {
  $densify: () => $densify
});
module.exports = __toCommonJS(densify_exports);
var import_core = require("../../core");
var import_lazy = require("../../lazy");
var import_types = require("../../types");
var import_util = require("../../util");
var import_dateAdd = require("../expression/date/dateAdd");
var import_sort = require("./sort");
const EMPTY_OBJECT = Object.freeze({});
const $densify = (collection, expr, options) => {
  const { step, bounds, unit } = expr.range;
  if (unit) {
    (0, import_util.assert)(import_types.TIME_UNITS.includes(unit), "");
    (0, import_util.assert)(
      Number.isInteger(step) && step > 0,
      "The step parameter in a range statement must be a whole number when densifying a date range."
    );
  } else {
    (0, import_util.assert)(
      (0, import_util.isNumber)(step) && step > 0,
      "The step parameter in a range statement must be a strictly positive numeric value."
    );
  }
  if ((0, import_util.isArray)(bounds)) {
    (0, import_util.assert)(
      !!bounds && bounds.length === 2,
      "A bounding array in a range statement must have exactly two elements."
    );
    (0, import_util.assert)(
      (bounds.every(import_util.isNumber) || bounds.every(import_util.isDate)) && bounds[0] < bounds[1],
      "A bounding array must be an ascending array of either two dates or two numbers."
    );
    (0, import_util.assert)(
      unit && !bounds.some(import_util.isNumber),
      "Numeric bounds may not have unit parameter."
    );
  }
  if (expr.partitionByFields) {
    (0, import_util.assert)(
      (0, import_util.isArray)(expr.partitionByFields),
      "$densify: `partitionByFields` must be an array of strings"
    );
  }
  collection = (0, import_sort.$sort)(collection, { [expr.field]: 1 }, options);
  const nilOptions = import_core.ComputeOptions.init(options, null);
  const computeNextValue = (value) => {
    return (0, import_util.isNumber)(value) ? value + step : (0, import_dateAdd.$dateAdd)(
      EMPTY_OBJECT,
      { startDate: value, unit, amount: step },
      nilOptions
    );
  };
  const isValidUnit = !!unit && import_types.TIME_UNITS.includes(unit);
  const getFieldValue = (o) => {
    const v = (0, import_util.resolve)(o, expr.field);
    if ((0, import_util.isNil)(v)) return v;
    if ((0, import_util.isNumber)(v)) {
      (0, import_util.assert)(
        !isValidUnit,
        "$densify: Encountered non-date value in collection when step has a date unit."
      );
    } else if ((0, import_util.isDate)(v)) {
      (0, import_util.assert)(
        isValidUnit,
        "$densify: Encountered date value in collection when step does not have a date unit."
      );
    } else {
      (0, import_util.assert)(false, "$densify: Densify field type must be numeric or a date");
    }
    return v;
  };
  const peekItem = new Array();
  const nilFieldsIterator = (0, import_lazy.Lazy)(() => {
    const item = collection.next();
    const fieldValue = getFieldValue(item.value);
    if ((0, import_util.isNil)(fieldValue)) return item;
    peekItem.push(item);
    return { done: true };
  });
  const nextDensifyValueMap = import_util.ValueMap.init(
    options.hashFunction
  );
  const [lower, upper] = (0, import_util.isArray)(bounds) ? bounds : [bounds, bounds];
  let maxFieldValue = void 0;
  const updateMaxFieldValue = (value) => {
    maxFieldValue = maxFieldValue === void 0 || maxFieldValue < value ? value : maxFieldValue;
  };
  const rootKey = [];
  const densifyIterator = (0, import_lazy.Lazy)(() => {
    const item = peekItem.length > 0 ? peekItem.pop() : collection.next();
    if (item.done) return item;
    let partitionKey = rootKey;
    if ((0, import_util.isArray)(expr.partitionByFields)) {
      partitionKey = expr.partitionByFields.map(
        (k) => (0, import_util.resolve)(item.value, k)
      );
      (0, import_util.assert)(
        partitionKey.every(import_util.isString),
        "$densify: Partition fields must evaluate to string values."
      );
    }
    (0, import_util.assert)((0, import_util.isObject)(item.value), "$densify: collection must contain documents");
    const itemValue = getFieldValue(item.value);
    if (!nextDensifyValueMap.has(partitionKey)) {
      if (lower == "full") {
        if (!nextDensifyValueMap.has(rootKey)) {
          nextDensifyValueMap.set(rootKey, itemValue);
        }
        nextDensifyValueMap.set(partitionKey, nextDensifyValueMap.get(rootKey));
      } else if (lower == "partition") {
        nextDensifyValueMap.set(partitionKey, itemValue);
      } else {
        nextDensifyValueMap.set(partitionKey, lower);
      }
    }
    const densifyValue = nextDensifyValueMap.get(partitionKey);
    if (
      // current item field value is lower than current densify value.
      itemValue <= densifyValue || // range value equals or exceeds upper bound
      upper != "full" && upper != "partition" && densifyValue >= upper
    ) {
      if (densifyValue <= itemValue) {
        nextDensifyValueMap.set(partitionKey, computeNextValue(densifyValue));
      }
      updateMaxFieldValue(itemValue);
      return item;
    }
    nextDensifyValueMap.set(partitionKey, computeNextValue(densifyValue));
    updateMaxFieldValue(densifyValue);
    const denseObj = { [expr.field]: densifyValue };
    if (partitionKey) {
      partitionKey.forEach((v, i) => {
        denseObj[expr.partitionByFields[i]] = v;
      });
    }
    peekItem.push(item);
    return { done: false, value: denseObj };
  });
  if (lower !== "full") return (0, import_lazy.concat)(nilFieldsIterator, densifyIterator);
  let paritionIndex = -1;
  let partitionKeysSet = void 0;
  const fullBoundsIterator = (0, import_lazy.Lazy)(() => {
    if (paritionIndex === -1) {
      const fullDensifyValue = nextDensifyValueMap.get(rootKey);
      nextDensifyValueMap.delete(rootKey);
      partitionKeysSet = Array.from(nextDensifyValueMap.keys());
      if (partitionKeysSet.length === 0) {
        partitionKeysSet.push(rootKey);
        nextDensifyValueMap.set(rootKey, fullDensifyValue);
      }
      paritionIndex++;
    }
    do {
      const partitionKey = partitionKeysSet[paritionIndex];
      const partitionMaxValue = nextDensifyValueMap.get(partitionKey);
      if (partitionMaxValue < maxFieldValue) {
        nextDensifyValueMap.set(
          partitionKey,
          computeNextValue(partitionMaxValue)
        );
        const denseObj = { [expr.field]: partitionMaxValue };
        partitionKey.forEach((v, i) => {
          denseObj[expr.partitionByFields[i]] = v;
        });
        return { done: false, value: denseObj };
      }
      paritionIndex++;
    } while (paritionIndex < partitionKeysSet.length);
    return { done: true };
  });
  return (0, import_lazy.concat)(nilFieldsIterator, densifyIterator, fullBoundsIterator);
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  $densify
});
