import { defaultComparator, normalizeValue } from "../utils/comparison.js";
import { findInsertPositionInArray, deleteInSortedArray } from "../utils/array-utils.js";
import { BaseIndex } from "./base-index.js";
class BasicIndex extends BaseIndex {
  constructor(id, expression, name, options) {
    super(id, expression, name, options);
    this.supportedOperations = /* @__PURE__ */ new Set([
      `eq`,
      `gt`,
      `gte`,
      `lt`,
      `lte`,
      `in`
    ]);
    this.valueMap = /* @__PURE__ */ new Map();
    this.sortedValues = [];
    this.indexedKeys = /* @__PURE__ */ new Set();
    this.compareFn = defaultComparator;
    this.compareFn = options?.compareFn ?? defaultComparator;
    if (options?.compareOptions) {
      this.compareOptions = options.compareOptions;
    }
  }
  initialize(_options) {
  }
  /**
   * Adds a value to the index
   */
  add(key, item) {
    let indexedValue;
    try {
      indexedValue = this.evaluateIndexExpression(item);
    } catch (error) {
      throw new Error(
        `Failed to evaluate index expression for key ${key}: ${error}`,
        { cause: error }
      );
    }
    const normalizedValue = normalizeValue(indexedValue);
    if (this.valueMap.has(normalizedValue)) {
      this.valueMap.get(normalizedValue).add(key);
    } else {
      this.valueMap.set(normalizedValue, /* @__PURE__ */ new Set([key]));
      const insertIdx = findInsertPositionInArray(
        this.sortedValues,
        normalizedValue,
        this.compareFn
      );
      this.sortedValues.splice(insertIdx, 0, normalizedValue);
    }
    this.indexedKeys.add(key);
    this.updateTimestamp();
  }
  /**
   * Removes a value from the index
   */
  remove(key, item) {
    let indexedValue;
    try {
      indexedValue = this.evaluateIndexExpression(item);
    } catch (error) {
      console.warn(
        `Failed to evaluate index expression for key ${key} during removal:`,
        error
      );
      this.indexedKeys.delete(key);
      this.updateTimestamp();
      return;
    }
    const normalizedValue = normalizeValue(indexedValue);
    if (this.valueMap.has(normalizedValue)) {
      const keySet = this.valueMap.get(normalizedValue);
      keySet.delete(key);
      if (keySet.size === 0) {
        this.valueMap.delete(normalizedValue);
        deleteInSortedArray(this.sortedValues, normalizedValue, this.compareFn);
      }
    }
    this.indexedKeys.delete(key);
    this.updateTimestamp();
  }
  /**
   * Updates a value in the index
   */
  update(key, oldItem, newItem) {
    this.remove(key, oldItem);
    this.add(key, newItem);
  }
  /**
   * Builds the index from a collection of entries
   */
  build(entries) {
    this.clear();
    const entriesArray = [];
    for (const [key, item] of entries) {
      let indexedValue;
      try {
        indexedValue = this.evaluateIndexExpression(item);
      } catch (error) {
        throw new Error(
          `Failed to evaluate index expression for key ${key}: ${error}`,
          { cause: error }
        );
      }
      entriesArray.push({ key, value: normalizeValue(indexedValue) });
      this.indexedKeys.add(key);
    }
    for (const { key, value } of entriesArray) {
      if (this.valueMap.has(value)) {
        this.valueMap.get(value).add(key);
      } else {
        this.valueMap.set(value, /* @__PURE__ */ new Set([key]));
      }
    }
    this.sortedValues = Array.from(this.valueMap.keys()).sort(this.compareFn);
    this.updateTimestamp();
  }
  /**
   * Clears all data from the index
   */
  clear() {
    this.valueMap.clear();
    this.sortedValues = [];
    this.indexedKeys.clear();
    this.updateTimestamp();
  }
  /**
   * Performs a lookup operation
   */
  lookup(operation, value) {
    const startTime = performance.now();
    let result;
    switch (operation) {
      case `eq`:
        result = this.equalityLookup(value);
        break;
      case `gt`:
        result = this.rangeQuery({ from: value, fromInclusive: false });
        break;
      case `gte`:
        result = this.rangeQuery({ from: value, fromInclusive: true });
        break;
      case `lt`:
        result = this.rangeQuery({ to: value, toInclusive: false });
        break;
      case `lte`:
        result = this.rangeQuery({ to: value, toInclusive: true });
        break;
      case `in`:
        result = this.inArrayLookup(value);
        break;
      default:
        throw new Error(`Operation ${operation} not supported by BasicIndex`);
    }
    this.trackLookup(startTime);
    return result;
  }
  /**
   * Gets the number of indexed keys
   */
  get keyCount() {
    return this.indexedKeys.size;
  }
  /**
   * Performs an equality lookup - O(1)
   */
  equalityLookup(value) {
    const normalizedValue = normalizeValue(value);
    return this.valueMap.get(normalizedValue) ?? /* @__PURE__ */ new Set();
  }
  /**
   * Performs a range query using binary search - O(log n + m)
   */
  rangeQuery(options = {}) {
    const { from, to, fromInclusive = true, toInclusive = true } = options;
    const result = /* @__PURE__ */ new Set();
    if (this.sortedValues.length === 0) {
      return result;
    }
    const normalizedFrom = normalizeValue(from);
    const normalizedTo = normalizeValue(to);
    let startIdx = 0;
    if (normalizedFrom !== void 0) {
      startIdx = findInsertPositionInArray(
        this.sortedValues,
        normalizedFrom,
        this.compareFn
      );
      if (!fromInclusive && startIdx < this.sortedValues.length && this.compareFn(this.sortedValues[startIdx], normalizedFrom) === 0) {
        startIdx++;
      }
    }
    let endIdx = this.sortedValues.length;
    if (normalizedTo !== void 0) {
      endIdx = findInsertPositionInArray(
        this.sortedValues,
        normalizedTo,
        this.compareFn
      );
      if (toInclusive && endIdx < this.sortedValues.length && this.compareFn(this.sortedValues[endIdx], normalizedTo) === 0) {
        endIdx++;
      }
    }
    for (let i = startIdx; i < endIdx; i++) {
      const keys = this.valueMap.get(this.sortedValues[i]);
      if (keys) {
        keys.forEach((key) => result.add(key));
      }
    }
    return result;
  }
  /**
   * Performs a reversed range query
   */
  rangeQueryReversed(options = {}) {
    const { from, to, fromInclusive = true, toInclusive = true } = options;
    const swappedFrom = to ?? (this.sortedValues.length > 0 ? this.sortedValues[this.sortedValues.length - 1] : void 0);
    const swappedTo = from ?? (this.sortedValues.length > 0 ? this.sortedValues[0] : void 0);
    return this.rangeQuery({
      from: swappedFrom,
      to: swappedTo,
      fromInclusive: toInclusive,
      toInclusive: fromInclusive
    });
  }
  /**
   * Returns the next n items in sorted order
   */
  take(n, from, filterFn) {
    const result = [];
    let startIdx = 0;
    if (from !== void 0) {
      const normalizedFrom = normalizeValue(from);
      startIdx = findInsertPositionInArray(
        this.sortedValues,
        normalizedFrom,
        this.compareFn
      );
      while (startIdx < this.sortedValues.length && this.compareFn(this.sortedValues[startIdx], normalizedFrom) <= 0) {
        startIdx++;
      }
    }
    for (let i = startIdx; i < this.sortedValues.length && result.length < n; i++) {
      const keys = this.valueMap.get(this.sortedValues[i]);
      if (keys) {
        for (const key of keys) {
          if (result.length >= n) break;
          if (!filterFn || filterFn(key)) {
            result.push(key);
          }
        }
      }
    }
    return result;
  }
  /**
   * Returns the next n items in reverse sorted order
   */
  takeReversed(n, from, filterFn) {
    const result = [];
    let startIdx = this.sortedValues.length - 1;
    if (from !== void 0) {
      const normalizedFrom = normalizeValue(from);
      startIdx = findInsertPositionInArray(
        this.sortedValues,
        normalizedFrom,
        this.compareFn
      ) - 1;
      while (startIdx >= 0 && this.compareFn(this.sortedValues[startIdx], normalizedFrom) >= 0) {
        startIdx--;
      }
    }
    for (let i = startIdx; i >= 0 && result.length < n; i--) {
      const keys = this.valueMap.get(this.sortedValues[i]);
      if (keys) {
        for (const key of keys) {
          if (result.length >= n) break;
          if (!filterFn || filterFn(key)) {
            result.push(key);
          }
        }
      }
    }
    return result;
  }
  /**
   * Returns the first n items in sorted order (from the start)
   */
  takeFromStart(n, filterFn) {
    const result = [];
    for (let i = 0; i < this.sortedValues.length && result.length < n; i++) {
      const keys = this.valueMap.get(this.sortedValues[i]);
      if (keys) {
        for (const key of keys) {
          if (result.length >= n) break;
          if (!filterFn || filterFn(key)) {
            result.push(key);
          }
        }
      }
    }
    return result;
  }
  /**
   * Returns the first n items in reverse sorted order (from the end)
   */
  takeReversedFromEnd(n, filterFn) {
    const result = [];
    for (let i = this.sortedValues.length - 1; i >= 0 && result.length < n; i--) {
      const keys = this.valueMap.get(this.sortedValues[i]);
      if (keys) {
        for (const key of keys) {
          if (result.length >= n) break;
          if (!filterFn || filterFn(key)) {
            result.push(key);
          }
        }
      }
    }
    return result;
  }
  /**
   * Performs an IN array lookup - O(k) where k is values.length
   */
  inArrayLookup(values) {
    const result = /* @__PURE__ */ new Set();
    for (const value of values) {
      const normalizedValue = normalizeValue(value);
      const keys = this.valueMap.get(normalizedValue);
      if (keys) {
        keys.forEach((key) => result.add(key));
      }
    }
    return result;
  }
  // Getter methods for testing/compatibility
  get indexedKeysSet() {
    return this.indexedKeys;
  }
  get orderedEntriesArray() {
    return this.sortedValues.map((value) => [
      value,
      this.valueMap.get(value) ?? /* @__PURE__ */ new Set()
    ]);
  }
  get orderedEntriesArrayReversed() {
    const result = [];
    for (let i = this.sortedValues.length - 1; i >= 0; i--) {
      const value = this.sortedValues[i];
      result.push([value, this.valueMap.get(value) ?? /* @__PURE__ */ new Set()]);
    }
    return result;
  }
  get valueMapData() {
    return this.valueMap;
  }
}
export {
  BasicIndex
};
//# sourceMappingURL=basic-index.js.map
