"use strict";
/* Socket Lib - Built with rolldown */
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_primordials_map_set = require('../primordials/map-set.js');
const require_primordials_date = require('../primordials/date.js');
const require_primordials_promise = require('../primordials/promise.js');
const require_debug_output = require('../debug/output.js');
const require_memo__internal = require('./_internal.js');

//#region src/memo/async.ts
/**
* @file `memoizeAsync` — async-aware memoizer with the same LRU+TTL contract as
*   `memoize`, plus thundering-herd dedup. Concurrent callers join an in-flight
*   promise instead of starting fresh fetches, and the cache timestamp is
*   refreshed on resolution so a slow fn can't land a value that's already
*   "expired".
*/
/**
* Memoize an async function. Similar to memoize() but handles promises
* properly.
*
* @example
*   import { memoizeAsync } from '@socketsecurity/lib/memo/async'
*
*   const fetchUser = memoizeAsync(
*     async (id: string) => {
*       const response = await fetch(`/api/users/${id}`)
*       return response.json()
*     },
*     { ttl: 300000, name: 'fetchUser' },
*   )
*
*   await fetchUser('123') // Fetches from API
*   await fetchUser('123') // Returns cached result
*
* @param fn - Async function to memoize.
* @param options - Memoization options.
*
* @returns Memoized version of the async function
*/
function memoizeAsync(fn, options = {}) {
	const { keyGen = (...args) => require_memo__internal.defaultKeyGen(args), maxSize = Number.POSITIVE_INFINITY, name = fn.name || "anonymous", ttl = Number.POSITIVE_INFINITY } = options;
	const cache = new require_primordials_map_set.MapCtor();
	require_memo__internal.cacheRegistry.push(() => {
		cache.clear();
	});
	function evictLRU() {
		if (cache.size >= maxSize) {
			const oldest = cache.keys().next().value;
			/* c8 ignore next 8 - cache.size >= maxSize guarantees keys().next()
			yields a defined value; the undefined branch is defensive. */
			if (oldest !== void 0) {
				cache.delete(oldest);
				require_debug_output.debugLog(`[memoizeAsync:${name}] clear`, {
					key: oldest,
					reason: "LRU"
				});
			}
		}
	}
	function isExpired(entry) {
		/* c8 ignore next 3 */
		if (ttl === Number.POSITIVE_INFINITY) return false;
		return require_primordials_date.DateNow() - entry.timestamp > ttl;
	}
	function bumpRecency(key, entry) {
		cache.delete(key);
		cache.set(key, entry);
	}
	const refreshing = new require_primordials_map_set.MapCtor();
	return async function memoized(...args) {
		const key = keyGen(...args);
		const cached = cache.get(key);
		/* c8 ignore start */
		if (cached) {
			if (!isExpired(cached)) {
				cached.hits++;
				bumpRecency(key, cached);
				require_debug_output.debugLog(`[memoizeAsync:${name}] hit`, {
					key,
					hits: cached.hits
				});
				return await cached.value;
			}
			const inflight = refreshing.get(key);
			if (inflight) {
				require_debug_output.debugLog(`[memoizeAsync:${name}] stale-dedup`, { key });
				bumpRecency(key, cached);
				return await inflight;
			}
			cache.delete(key);
		}
		const inflightCold = refreshing.get(key);
		if (inflightCold) {
			require_debug_output.debugLog(`[memoizeAsync:${name}] cold-dedup`, { key });
			return await inflightCold;
		}
		/* c8 ignore stop */
		require_debug_output.debugLog(`[memoizeAsync:${name}] miss`, { key });
		const promise = (async () => {
			try {
				const result = await fn(...args);
				refreshing.delete(key);
				const entry = cache.get(key);
				if (entry) {
					entry.value = require_primordials_promise.PromiseResolve(result);
					entry.timestamp = require_primordials_date.DateNow();
				}
				return result;
			} catch (error) {
				refreshing.delete(key);
				cache.delete(key);
				require_debug_output.debugLog(`[memoizeAsync:${name}] error`, {
					key,
					error
				});
				throw error;
			}
		})();
		refreshing.set(key, promise);
		evictLRU();
		cache.set(key, {
			value: promise,
			timestamp: Date.now(),
			hits: 0
		});
		require_debug_output.debugLog(`[memoizeAsync:${name}] set`, {
			key,
			cacheSize: cache.size
		});
		return await promise;
	};
}

//#endregion
exports.memoizeAsync = memoizeAsync;