const require_rolldown_runtime = require('../../../../../_virtual/rolldown_runtime.cjs');
const require_package = require('../config/package.cjs');
let node_path = require("node:path");
let node_crypto = require("node:crypto");
let node_fs_promises = require("node:fs/promises");
let node_v8 = require("node:v8");
let node_zlib = require("node:zlib");

//#region ../@intlayer/config/dist/esm/utils/cache.mjs
/** ------------------------- Utilities ------------------------- **/
/** Prefer a fast non-crypto hash if available, then fast crypto, then sha256. */
const pickHashAlgorithm = () => {
	try {
		(0, node_crypto.createHash)("xxhash64").update("test").digest();
		return "xxhash64";
	} catch {}
	try {
		(0, node_crypto.createHash)("sha1").update("test").digest();
		return "sha1";
	} catch {}
	return "sha256";
};
const HASH_ALGORITHM = pickHashAlgorithm();
/** Base64url without padding for compact, file-system-safe ids. */
const toBase64Url = (buffer) => buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
/** Token helpers to minimize collisions while streaming to the hasher. */
const token = {
	start: (hasher, tag) => hasher.update(`<${tag}>`),
	sep: (hasher) => hasher.update("|"),
	end: (hasher, tag) => hasher.update(`</${tag}>`),
	str: (hasher, stringValue) => {
		hasher.update(`${stringValue.length}#`);
		hasher.update(stringValue);
	},
	num: (hasher, numberValue) => hasher.update(Number.isNaN(numberValue) ? "NaN" : numberValue === Infinity ? "Inf" : numberValue === -Infinity ? "-Inf" : String(numberValue)),
	big: (hasher, bigintValue) => hasher.update(bigintValue.toString(10)),
	bool: (hasher, booleanValue) => hasher.update(booleanValue ? "1" : "0")
};
/**
* Streams a canonical representation of `value` into `hasher` without
* constructing large intermediate strings. Objects/Maps/Sets are normalized.
*/
const stableHashValue = (hasher, value, seen) => {
	const valueType = typeof value;
	if (value === null) {
		token.start(hasher, "null");
		token.end(hasher, "null");
		return;
	}
	if (valueType === "undefined") {
		token.start(hasher, "undef");
		token.end(hasher, "undef");
		return;
	}
	if (valueType === "number") {
		token.start(hasher, "num");
		token.num(hasher, value);
		token.end(hasher, "num");
		return;
	}
	if (valueType === "bigint") {
		token.start(hasher, "big");
		token.big(hasher, value);
		token.end(hasher, "big");
		return;
	}
	if (valueType === "boolean") {
		token.start(hasher, "bool");
		token.bool(hasher, value);
		token.end(hasher, "bool");
		return;
	}
	if (valueType === "string") {
		token.start(hasher, "str");
		token.str(hasher, value);
		token.end(hasher, "str");
		return;
	}
	if (valueType === "symbol") {
		token.start(hasher, "sym");
		token.str(hasher, String(value));
		token.end(hasher, "sym");
		return;
	}
	if (valueType === "function") {
		const functionValue = value;
		token.start(hasher, "fn");
		token.str(hasher, functionValue.name ?? "");
		token.sep(hasher);
		token.num(hasher, functionValue.length);
		token.end(hasher, "fn");
		return;
	}
	if (Array.isArray(value)) {
		if (seen.has(value)) {
			token.start(hasher, "arr");
			token.str(hasher, "Circular");
			token.end(hasher, "arr");
			return;
		}
		seen.add(value);
		token.start(hasher, "arr");
		for (let i = 0; i < value.length; i++) {
			token.sep(hasher);
			stableHashValue(hasher, value[i], seen);
		}
		token.end(hasher, "arr");
		seen.delete(value);
		return;
	}
	if (value instanceof Date) {
		token.start(hasher, "date");
		token.str(hasher, value.toISOString());
		token.end(hasher, "date");
		return;
	}
	if (value instanceof RegExp) {
		const regex = value;
		token.start(hasher, "re");
		token.str(hasher, regex.source);
		token.sep(hasher);
		token.str(hasher, regex.flags);
		token.end(hasher, "re");
		return;
	}
	if (value instanceof Set) {
		const setValue = value;
		if (seen.has(setValue)) {
			token.start(hasher, "set");
			token.str(hasher, "Circular");
			token.end(hasher, "set");
			return;
		}
		seen.add(setValue);
		const items = [];
		for (const v of setValue) items.push(stableStringify(v));
		items.sort();
		token.start(hasher, "set");
		for (const item of items) {
			token.sep(hasher);
			token.str(hasher, item);
		}
		token.end(hasher, "set");
		seen.delete(setValue);
		return;
	}
	if (value instanceof Map) {
		const mapObject = value;
		if (seen.has(mapObject)) {
			token.start(hasher, "map");
			token.str(hasher, "Circular");
			token.end(hasher, "map");
			return;
		}
		seen.add(mapObject);
		const entries = [];
		for (const [k, v] of mapObject.entries()) entries.push([stableStringify(k), v]);
		entries.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
		token.start(hasher, "map");
		for (const [keyFingerprint, entryValue] of entries) {
			token.sep(hasher);
			token.str(hasher, keyFingerprint);
			token.sep(hasher);
			stableHashValue(hasher, entryValue, seen);
		}
		token.end(hasher, "map");
		seen.delete(mapObject);
		return;
	}
	if (ArrayBuffer.isView(value)) {
		const view = value;
		token.start(hasher, "typed");
		token.str(hasher, Object.getPrototypeOf(view).constructor.name);
		token.sep(hasher);
		hasher.update(Buffer.from(view.buffer, view.byteOffset, view.byteLength));
		token.end(hasher, "typed");
		return;
	}
	if (value instanceof ArrayBuffer) {
		const buffer = Buffer.from(value);
		token.start(hasher, "ab");
		hasher.update(buffer);
		token.end(hasher, "ab");
		return;
	}
	if (typeof URL !== "undefined" && value instanceof URL) {
		token.start(hasher, "url");
		token.str(hasher, value.toString());
		token.end(hasher, "url");
		return;
	}
	if (value instanceof Error) {
		const errorValue = value;
		token.start(hasher, "err");
		token.str(hasher, errorValue.name || "");
		token.sep(hasher);
		token.str(hasher, errorValue.message || "");
		token.sep(hasher);
		token.str(hasher, errorValue.stack || "");
		token.end(hasher, "err");
		return;
	}
	if (valueType === "object") {
		const objectValue = value;
		if (seen.has(objectValue)) {
			token.start(hasher, "obj");
			token.str(hasher, "Circular");
			token.end(hasher, "obj");
			return;
		}
		seen.add(objectValue);
		const keys = Object.keys(objectValue).sort();
		token.start(hasher, "obj");
		for (const key of keys) {
			token.sep(hasher);
			token.str(hasher, key);
			token.sep(hasher);
			stableHashValue(hasher, objectValue[key], seen);
		}
		token.end(hasher, "obj");
		seen.delete(objectValue);
		return;
	}
	token.start(hasher, "other");
	token.str(hasher, String(value));
	token.end(hasher, "other");
};
/** Public stringify kept for convenience / debugging (now faster & broader). */
const stableStringify = (value, _stack = /* @__PURE__ */ new WeakSet()) => {
	const hasher = (0, node_crypto.createHash)(HASH_ALGORITHM);
	stableHashValue(hasher, value, _stack);
	return toBase64Url(hasher.digest());
};
/** Compute a compact, stable id for arbitrary key tuples. */
const computeKeyId = (keyParts) => {
	const h = (0, node_crypto.createHash)(HASH_ALGORITHM);
	token.start(h, "keys");
	for (let i = 0; i < keyParts.length; i++) {
		token.sep(h);
		stableHashValue(h, keyParts[i], /* @__PURE__ */ new WeakSet());
	}
	token.end(h, "keys");
	return toBase64Url(h.digest());
};
const cacheMap = /* @__PURE__ */ new Map();
const getCache = (...key) => {
	return cacheMap.get(computeKeyId(key));
};
const setCache = (...args) => {
	const value = args[args.length - 1];
	const key = args.slice(0, -1);
	cacheMap.set(computeKeyId(key), value);
};
const clearCache = (idOrKey) => {
	cacheMap.delete(idOrKey);
};
const clearAllCache = () => {
	cacheMap.clear();
};
const cache = {
	get: getCache,
	set: setCache,
	clear: clearCache
};
const DEFAULTS = { compress: true };
const ensureDir = async (dir) => {
	await (0, node_fs_promises.mkdir)(dir, { recursive: true });
};
const atomicWriteFile = async (file, data) => {
	const tmp = `${file}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
	await (0, node_fs_promises.writeFile)(tmp, data);
	await (0, node_fs_promises.rename)(tmp, file);
};
const shouldUseCompression = (buf, force) => force === true || force !== false && buf.byteLength > 1024;
/** Derive on-disk path from config dir + namespace + key id. */
const cachePath = (cacheDir, id, ns) => (0, node_path.join)(cacheDir, ns ? (0, node_path.join)(ns, id) : id);
/** ------------------------- Local cache facade ------------------------- **/
const localCache = (intlayerConfig, keys, options) => {
	const { cacheDir } = intlayerConfig.content;
	const buildCacheEnabled = intlayerConfig.build.cache ?? true;
	const persistent = options?.persistent === true || typeof options?.persistent === "undefined" && buildCacheEnabled;
	const { compress, ttlMs, maxTimeMs, namespace } = {
		...DEFAULTS,
		...options
	};
	const id = computeKeyId(keys);
	const filePath = cachePath(cacheDir, id, namespace);
	const readFromDisk = async () => {
		try {
			const statValue = await (0, node_fs_promises.stat)(filePath).catch(() => void 0);
			if (!statValue) return void 0;
			if (typeof ttlMs === "number" && ttlMs > 0) {
				if (Date.now() - statValue.mtimeMs > ttlMs) return void 0;
			}
			let raw = await (0, node_fs_promises.readFile)(filePath);
			const flag = raw[0];
			raw = raw.subarray(1);
			const deserialized = (0, node_v8.deserialize)(flag === 1 ? (0, node_zlib.gunzipSync)(raw) : raw);
			let value;
			const maybeObj = deserialized;
			if (!!maybeObj && typeof maybeObj === "object" && typeof maybeObj.v === "string" && typeof maybeObj.ts === "number" && Object.hasOwn(maybeObj, "d")) {
				const entry = maybeObj;
				if (entry.v !== require_package.version) {
					try {
						await (0, node_fs_promises.unlink)(filePath);
					} catch {}
					return;
				}
				if (typeof maxTimeMs === "number" && maxTimeMs > 0) {
					if (Date.now() - entry.ts > maxTimeMs) {
						try {
							await (0, node_fs_promises.unlink)(filePath);
						} catch {}
						return;
					}
				}
				value = entry.d;
			} else {
				if (typeof maxTimeMs === "number" && maxTimeMs > 0) {
					if (Date.now() - statValue.mtimeMs > maxTimeMs) {
						try {
							await (0, node_fs_promises.unlink)(filePath);
						} catch {}
						return;
					}
				}
				value = deserialized;
			}
			cacheMap.set(id, value);
			return value;
		} catch {
			return;
		}
	};
	const writeToDisk = async (value) => {
		try {
			await ensureDir((0, node_path.dirname)(filePath));
			const envelope = {
				v: require_package.version,
				ts: Date.now(),
				d: value
			};
			const payload = Buffer.from((0, node_v8.serialize)(envelope));
			const gz = shouldUseCompression(payload, compress) ? (0, node_zlib.gzipSync)(payload) : payload;
			await atomicWriteFile(filePath, Buffer.concat([Buffer.from([gz === payload ? 0 : 1]), gz]));
		} catch {}
	};
	return {
		get: async () => {
			const mem = cacheMap.get(id);
			if (mem !== void 0) return mem;
			if (persistent && buildCacheEnabled) return await readFromDisk();
		},
		set: async (value) => {
			cacheMap.set(id, value);
			if (persistent && buildCacheEnabled) await writeToDisk(value);
		},
		clear: async () => {
			cacheMap.delete(id);
			try {
				await (0, node_fs_promises.unlink)(filePath);
			} catch {}
		},
		clearAll: async () => {
			clearAllCache();
			if (persistent && buildCacheEnabled) {
				const base = namespace ? (0, node_path.join)(cacheDir, namespace) : cacheDir;
				try {
					await (0, node_fs_promises.rm)(base, {
						recursive: true,
						force: true
					});
				} catch {}
				try {
					await (0, node_fs_promises.mkdir)(base, { recursive: true });
				} catch {}
			}
		},
		isValid: async () => {
			if (cacheMap.get(id) !== void 0) return true;
			if (!persistent || !buildCacheEnabled) return false;
			try {
				const statValue = await (0, node_fs_promises.stat)(filePath).catch(() => void 0);
				if (!statValue) return false;
				if (typeof ttlMs === "number" && ttlMs > 0) {
					if (Date.now() - statValue.mtimeMs > ttlMs) return false;
				}
				let raw = await (0, node_fs_promises.readFile)(filePath);
				const flag = raw[0];
				raw = raw.subarray(1);
				const maybeObj = (0, node_v8.deserialize)(flag === 1 ? (0, node_zlib.gunzipSync)(raw) : raw);
				if (!!maybeObj && typeof maybeObj === "object" && typeof maybeObj.v === "string" && typeof maybeObj.ts === "number" && Object.hasOwn(maybeObj, "d")) {
					const entry = maybeObj;
					if (entry.v !== require_package.version) return false;
					if (typeof maxTimeMs === "number" && maxTimeMs > 0) {
						if (Date.now() - entry.ts > maxTimeMs) return false;
					}
					return true;
				}
				if (typeof maxTimeMs === "number" && maxTimeMs > 0) {
					if (Date.now() - statValue.mtimeMs > maxTimeMs) return false;
				}
				return true;
			} catch {
				return false;
			}
		},
		id,
		filePath
	};
};

//#endregion
exports.cache = cache;
exports.localCache = localCache;
//# sourceMappingURL=cache.cjs.map