"use strict";
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 Cache_exports = {};
__export(Cache_exports, {
  AsyncCache: () => AsyncCache,
  Cache: () => Cache
});
module.exports = __toCommonJS(Cache_exports);
var import_Diagnostic = require("../log/Diagnostic.js");
var import_Time = require("../time/Time.js");
/**
 * Cache computed values or resources for a specified duration to improve performances.
 *
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const KEY_SEPARATOR = "";
class GenericCache {
  constructor(name, expiration, expireCallback) {
    this.expiration = expiration;
    this.expireCallback = expireCallback;
    this.periodicTimer = import_Time.Time.getPeriodicTimer(
      import_Diagnostic.Diagnostic.upgrade(`${name} cache expiration`, [import_Diagnostic.Diagnostic.strong(name), "cache expiration"]),
      expiration,
      () => this.expire()
    ).start();
    this.periodicTimer.utility = true;
  }
  expiration;
  expireCallback;
  knownKeys = /* @__PURE__ */ new Set();
  values = /* @__PURE__ */ new Map();
  timestamps = /* @__PURE__ */ new Map();
  periodicTimer;
  // Single-param keys must stay identical to the stringified raw value so `keys()` → `get()` round-trips.
  keyFor(params) {
    return params.map((p) => String(p)).join(KEY_SEPARATOR);
  }
  keys() {
    return Array.from(this.knownKeys.values());
  }
  async delete(key) {
    if (this.expireCallback !== void 0 && this.values.has(key)) {
      await this.expireCallback(key, this.values.get(key));
    }
    this.values.delete(key);
    this.timestamps.delete(key);
  }
  /** Evict the entry addressed by the same params passed to {@link Cache.get}/{@link AsyncCache.get}. */
  async evict(...params) {
    await this.delete(this.keyFor(params));
  }
  async clear() {
    for (const key of this.values.keys()) {
      await this.delete(key);
    }
    this.values.clear();
    this.timestamps.clear();
  }
  async close() {
    await this.clear();
    this.knownKeys.clear();
    this.periodicTimer.stop();
  }
  async expire() {
    const now = import_Time.Time.nowMs;
    for (const [key, timestamp] of this.timestamps.entries()) {
      if (now - timestamp < this.expiration) continue;
      await this.delete(key);
    }
  }
}
class Cache extends GenericCache {
  constructor(name, generator, expiration, expireCallback) {
    super(name, expiration, expireCallback);
    this.generator = generator;
  }
  generator;
  get(...params) {
    const key = this.keyFor(params);
    if (!this.values.has(key)) {
      this.values.set(key, this.generator(...params));
      this.knownKeys.add(key);
    }
    this.timestamps.set(key, import_Time.Time.nowMs);
    return this.values.get(key);
  }
}
class AsyncCache extends GenericCache {
  constructor(name, generator, expiration, expireCallback) {
    super(name, expiration, expireCallback);
    this.generator = generator;
  }
  generator;
  #inflight = /* @__PURE__ */ new Map();
  get(...params) {
    const key = this.keyFor(params);
    if (this.values.has(key)) {
      this.timestamps.set(key, import_Time.Time.nowMs);
      return Promise.resolve(this.values.get(key));
    }
    let pending = this.#inflight.get(key);
    if (pending === void 0) {
      pending = this.#fill(key, params);
      this.#inflight.set(key, pending);
      const drop = () => this.#inflight.delete(key);
      pending.then(drop, drop);
    }
    return pending;
  }
  async #fill(key, params) {
    const value = await this.generator(...params);
    this.values.set(key, value);
    this.knownKeys.add(key);
    this.timestamps.set(key, import_Time.Time.nowMs);
    return value;
  }
  // Sync point for `expireCallback` to see the settled value.
  async delete(key) {
    const pending = this.#inflight.get(key);
    if (pending !== void 0) {
      try {
        await pending;
      } catch {
      }
    }
    await super.delete(key);
  }
  async clear() {
    await Promise.allSettled(this.#inflight.values());
    await super.clear();
  }
}
//# sourceMappingURL=Cache.js.map
