const createLRU = (options) => {
  let { max } = options;
  if (!(Number.isInteger(max) && max > 0))
    throw new TypeError("`max` must be a positive integer");
  let size = 0;
  let head = 0;
  let tail = 0;
  let free = [];
  const { onEviction } = options;
  const keyMap = /* @__PURE__ */ new Map();
  const keyList = new Array(max).fill(void 0);
  const valList = new Array(max).fill(void 0);
  const next = new Array(max).fill(0);
  const prev = new Array(max).fill(0);
  const setTail = (index, type) => {
    if (index === tail) return;
    const nextIndex = next[index];
    const prevIndex = prev[index];
    if (index === head) head = nextIndex;
    else if (type === "get" || prevIndex !== 0) next[prevIndex] = nextIndex;
    if (nextIndex !== 0) prev[nextIndex] = prevIndex;
    next[tail] = index;
    prev[index] = tail;
    next[index] = 0;
    tail = index;
  };
  const _evict = () => {
    const evictHead = head;
    const key = keyList[evictHead];
    onEviction == null ? void 0 : onEviction(key, valList[evictHead]);
    keyMap.delete(key);
    keyList[evictHead] = void 0;
    valList[evictHead] = void 0;
    head = next[evictHead];
    if (head !== 0) prev[head] = 0;
    size--;
    if (size === 0) head = tail = 0;
    free.push(evictHead);
    return evictHead;
  };
  return {
    /** Adds a key-value pair to the cache. Updates the value if the key already exists. */
    set(key, value) {
      if (key === void 0) return;
      let index = keyMap.get(key);
      if (index === void 0) {
        index = size === max ? _evict() : free.length > 0 ? free.pop() : size;
        keyMap.set(key, index);
        keyList[index] = key;
        size++;
      } else onEviction == null ? void 0 : onEviction(key, valList[index]);
      valList[index] = value;
      if (size === 1) head = tail = index;
      else setTail(index, "set");
    },
    /** Retrieves the value for a given key and moves the key to the most recent position. */
    get(key) {
      const index = keyMap.get(key);
      if (index === void 0) return;
      if (index !== tail) setTail(index, "get");
      return valList[index];
    },
    /** Retrieves the value for a given key without changing its position. */
    peek: (key) => {
      const index = keyMap.get(key);
      return index !== void 0 ? valList[index] : void 0;
    },
    /** Checks if a key exists in the cache. */
    has: (key) => keyMap.has(key),
    /** Iterates over all keys in the cache, from most recent to least recent. */
    *keys() {
      let current = tail;
      for (let i = 0; i < size; i++) {
        yield keyList[current];
        current = prev[current];
      }
    },
    /** Iterates over all values in the cache, from most recent to least recent. */
    *values() {
      let current = tail;
      for (let i = 0; i < size; i++) {
        yield valList[current];
        current = prev[current];
      }
    },
    /** Iterates over `[key, value]` pairs in the cache, from most recent to least recent. */
    *entries() {
      let current = tail;
      for (let i = 0; i < size; i++) {
        yield [keyList[current], valList[current]];
        current = prev[current];
      }
    },
    /** Iterates over each value-key pair in the cache, from most recent to least recent. */
    forEach: (callback) => {
      let current = tail;
      for (let i = 0; i < size; i++) {
        const key = keyList[current];
        const value = valList[current];
        callback(value, key);
        current = prev[current];
      }
    },
    /** Deletes a key-value pair from the cache. */
    delete(key) {
      const index = keyMap.get(key);
      if (index === void 0) return false;
      onEviction == null ? void 0 : onEviction(key, valList[index]);
      keyMap.delete(key);
      free.push(index);
      keyList[index] = void 0;
      valList[index] = void 0;
      const prevIndex = prev[index];
      const nextIndex = next[index];
      if (prevIndex !== 0) next[prevIndex] = nextIndex;
      if (nextIndex !== 0) prev[nextIndex] = prevIndex;
      if (index === head) head = nextIndex;
      if (index === tail) tail = prevIndex;
      size--;
      return true;
    },
    /** Evicts the oldest item or the specified number of the oldest items from the cache. */
    evict: (number) => {
      let toPrune = Math.min(number, size);
      while (toPrune > 0) {
        _evict();
        toPrune--;
      }
    },
    /** Clears all key-value pairs from the cache. */
    clear() {
      if (typeof onEviction === "function") {
        let current = head;
        for (let i = 0; i < size; i++) {
          onEviction(keyList[current], valList[current]);
          current = next[current];
        }
      }
      keyMap.clear();
      keyList.fill(void 0);
      valList.fill(void 0);
      free = [];
      size = 0;
      head = tail = 0;
    },
    /** Resizes the cache to a new maximum size, evicting items if necessary. */
    resize: (newMax) => {
      if (!(Number.isInteger(newMax) && newMax > 0))
        throw new TypeError("`max` must be a positive integer");
      if (newMax === max) return;
      if (newMax < max) {
        let current = tail;
        const preserve = Math.min(size, newMax);
        const remove = size - preserve;
        const newKeyList = new Array(newMax);
        const newValList = new Array(newMax);
        const newNext = new Array(newMax);
        const newPrev = new Array(newMax);
        for (let i = 1; i <= remove; i++)
          onEviction == null ? void 0 : onEviction(keyList[i], valList[i]);
        for (let i = preserve - 1; i >= 0; i--) {
          newKeyList[i] = keyList[current];
          newValList[i] = valList[current];
          newNext[i] = i + 1;
          newPrev[i] = i - 1;
          keyMap.set(newKeyList[i], i);
          current = prev[current];
        }
        head = 0;
        tail = preserve - 1;
        size = preserve;
        keyList.length = newMax;
        valList.length = newMax;
        next.length = newMax;
        prev.length = newMax;
        for (let i = 0; i < preserve; i++) {
          keyList[i] = newKeyList[i];
          valList[i] = newValList[i];
          next[i] = newNext[i];
          prev[i] = newPrev[i];
        }
        free = [];
        for (let i = preserve; i < newMax; i++) free.push(i);
      } else {
        const fill = newMax - max;
        keyList.push(...new Array(fill).fill(void 0));
        valList.push(...new Array(fill).fill(void 0));
        next.push(...new Array(fill).fill(0));
        prev.push(...new Array(fill).fill(0));
      }
      max = newMax;
    },
    /** Returns the maximum number of items that can be stored in the cache. */
    get max() {
      return max;
    },
    /** Returns the number of items currently stored in the cache. */
    get size() {
      return size;
    },
    /** Returns the number of currently available slots in the cache before reaching the maximum size. */
    get available() {
      return max - size;
    }
  };
};
export {
  createLRU
};
