import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-DW4mBlAt.js";
import { n as resolveGlobalSingleton } from "./global-singleton-PwlQSEal.js";
import { f as parseThreadSessionSuffix, o as normalizeSessionKeyPreservingOpaquePeerIds, p as requiresFoldedSessionKeyAliasProof } from "./session-key-utils-B8sNp9l4.js";
import { AsyncLocalStorage } from "node:async_hooks";
//#region src/shared/store-writer-queue.ts
const activeStoreWriters = resolveGlobalSingleton(Symbol.for("openclaw.activeStoreWriters"), () => new AsyncLocalStorage());
function isActiveStoreWriter(queues, storePath) {
	let active = activeStoreWriters.getStore();
	while (active) {
		if (active.active && active.queues === queues && active.storePath === storePath) return true;
		active = active.parent;
	}
	return false;
}
async function runActiveStoreWriter(queues, storePath, fn) {
	const writer = {
		active: true,
		parent: activeStoreWriters.getStore(),
		queues,
		storePath
	};
	try {
		return await activeStoreWriters.run(writer, fn);
	} finally {
		writer.active = false;
	}
}
function getOrCreateStoreWriterQueue(queues, storePath) {
	const existing = queues.get(storePath);
	if (existing) return existing;
	const created = {
		running: false,
		pending: [],
		drainPromise: null
	};
	queues.set(storePath, created);
	return created;
}
async function drainStoreWriterQueue(queues, storePath) {
	const queue = queues.get(storePath);
	if (!queue) return;
	if (queue.drainPromise) {
		await queue.drainPromise;
		return;
	}
	queue.running = true;
	queue.drainPromise = (async () => {
		try {
			while (queue.pending.length > 0) {
				const task = queue.pending.shift();
				if (!task) continue;
				let result;
				let failed;
				let hasFailure = false;
				try {
					result = await task.fn();
				} catch (err) {
					hasFailure = true;
					failed = err;
				}
				if (hasFailure) {
					task.reject(failed);
					continue;
				}
				task.resolve(result);
			}
		} finally {
			queue.running = false;
			queue.drainPromise = null;
			if (queue.pending.length === 0) queues.delete(storePath);
			else queueMicrotask(() => {
				drainStoreWriterQueue(queues, storePath);
			});
		}
	})();
	await queue.drainPromise;
}
/** Runs one store write after prior writes for the same store path have finished. */
async function runQueuedStoreWrite(params) {
	if (!params.storePath || typeof params.storePath !== "string") throw new Error(`${params.label}: storePath must be a non-empty string, got ${JSON.stringify(params.storePath)}`);
	if (params.reentrant === true && isActiveStoreWriter(params.queues, params.storePath)) return await params.fn();
	const queue = getOrCreateStoreWriterQueue(params.queues, params.storePath);
	return await new Promise((resolve, reject) => {
		const task = {
			fn: async () => await runActiveStoreWriter(params.queues, params.storePath, params.fn),
			resolve: (value) => resolve(value),
			reject
		};
		queue.pending.push(task);
		drainStoreWriterQueue(params.queues, params.storePath);
	});
}
/** Rejects pending queued writes and clears queue state for test cleanup. */
function clearStoreWriterQueuesForTest(queues, message) {
	for (const queue of queues.values()) for (const task of queue.pending) task.reject(new Error(message));
	queues.clear();
}
//#endregion
//#region src/config/sessions/store-entry.ts
function normalizeStoreSessionKey(sessionKey) {
	return normalizeSessionKeyPreservingOpaquePeerIds(sessionKey);
}
function foldedSessionKeyAliasCandidates(normalizedKey) {
	const aliases = /* @__PURE__ */ new Set();
	const foldedLegacyKey = normalizeLowercaseStringOrEmpty(normalizedKey);
	if (foldedLegacyKey !== normalizedKey) aliases.add(foldedLegacyKey);
	if (requiresFoldedSessionKeyAliasProof(normalizedKey)) {
		const { baseSessionKey, threadId } = parseThreadSessionSuffix(normalizedKey);
		const foldedBaseKey = normalizeLowercaseStringOrEmpty(baseSessionKey);
		if (baseSessionKey && threadId && foldedBaseKey !== baseSessionKey) aliases.add(`${foldedBaseKey}:thread:${threadId}`);
	}
	return [...aliases];
}
/** The case-sensitive room/peer target an entry actually delivers to. Delivery
*  metadata preserves the real opaque id even when the session KEY was lowercased
*  by the bug, so it distinguishes a lowercased artifact from a distinct room. */
function normalizeEntryTarget(value) {
	if (typeof value !== "string") return "";
	const trimmed = value.trim();
	const sigilIndexes = ["!", "#"].map((sigil) => trimmed.indexOf(sigil)).filter((index) => index >= 0);
	if (sigilIndexes.length === 0) return trimmed;
	return trimmed.slice(Math.min(...sigilIndexes));
}
function entryDeliveryTargets(entry) {
	return [
		entry?.deliveryContext?.to,
		entry?.lastTo,
		entry?.origin?.nativeChannelId,
		entry?.origin?.to,
		entry?.groupId
	].map(normalizeEntryTarget).filter(Boolean);
}
function normalizeEntryThreadId(value) {
	if (value == null) return "";
	if (typeof value !== "string" && typeof value !== "number") return "";
	return String(value).trim();
}
function entryThreadId(entry) {
	return normalizeEntryThreadId(entry?.deliveryContext?.threadId ?? entry?.lastThreadId ?? entry?.origin?.threadId);
}
/** Tail-preserved keys like Matrix rooms need delivery-target proof before a
*  folded key is treated as a legacy alias. Segment-preserved legacy keys
*  (Signal groups) keep their old permissive lowercase fallback. */
function isConfirmedLowercasedLegacyAlias(entry, normalizedKey) {
	if (!entry) return false;
	if (!requiresFoldedSessionKeyAliasProof(normalizedKey)) return true;
	const { baseSessionKey, threadId } = parseThreadSessionSuffix(normalizedKey);
	const normalizedBaseKey = baseSessionKey ?? normalizedKey;
	if (!entryDeliveryTargets(entry).some((target) => normalizedBaseKey.includes(target))) return false;
	if (!threadId) return true;
	return entryThreadId(entry) === threadId;
}
function hasMismatchedCaseSensitiveDeliveryProof(entry, normalizedKey) {
	if (!entry || !requiresFoldedSessionKeyAliasProof(normalizedKey)) return false;
	const { baseSessionKey, threadId } = parseThreadSessionSuffix(normalizedKey);
	const normalizedBaseKey = baseSessionKey ?? normalizedKey;
	const targets = entryDeliveryTargets(entry);
	if (targets.length > 0 && !targets.some((target) => normalizedBaseKey.includes(target))) return true;
	const storedThreadId = entryThreadId(entry);
	return Boolean(threadId && storedThreadId && storedThreadId !== threadId);
}
function resolveSessionEntryCandidates(params) {
	const trimmedKey = params.sessionKey.trim();
	const normalizedKey = normalizeStoreSessionKey(trimmedKey);
	const foldedLegacyKeys = foldedSessionKeyAliasCandidates(normalizedKey);
	const entries = new Map(params.entries.map((candidate) => [candidate.sessionKey, candidate]));
	const legacyKeySet = /* @__PURE__ */ new Set();
	const trimmedCandidate = entries.get(trimmedKey);
	if (trimmedKey !== normalizedKey && trimmedCandidate && !hasMismatchedCaseSensitiveDeliveryProof(trimmedCandidate.entry, normalizedKey)) legacyKeySet.add(trimmedKey);
	let foldedLegacyEntry;
	let foldedLegacyUpdatedAt = 0;
	for (const foldedLegacyKey of foldedLegacyKeys) {
		const candidate = entries.get(foldedLegacyKey);
		if (!candidate || !isConfirmedLowercasedLegacyAlias(candidate.entry, normalizedKey)) continue;
		legacyKeySet.add(foldedLegacyKey);
		const updatedAt = candidate.entry.updatedAt ?? 0;
		if (!foldedLegacyEntry || updatedAt > foldedLegacyUpdatedAt) {
			foldedLegacyEntry = candidate;
			foldedLegacyUpdatedAt = updatedAt;
		}
	}
	const exactEntry = entries.get(normalizedKey);
	const usableExactEntry = hasMismatchedCaseSensitiveDeliveryProof(exactEntry?.entry, normalizedKey) ? void 0 : exactEntry;
	const exactKeyWins = requiresFoldedSessionKeyAliasProof(normalizedKey);
	const fallbackLegacyEntry = legacyKeySet.size > 0 && !hasMismatchedCaseSensitiveDeliveryProof(trimmedCandidate?.entry, normalizedKey) ? trimmedCandidate : void 0;
	let existing = exactKeyWins ? usableExactEntry ?? foldedLegacyEntry ?? fallbackLegacyEntry : void 0;
	let existingUpdatedAt = existing?.entry.updatedAt ?? 0;
	if (!exactKeyWins) for (const candidate of [
		usableExactEntry,
		foldedLegacyEntry,
		fallbackLegacyEntry
	]) {
		const candidateUpdatedAt = candidate?.entry.updatedAt ?? 0;
		if (candidate && (!existing || candidateUpdatedAt > existingUpdatedAt)) {
			existing = candidate;
			existingUpdatedAt = candidateUpdatedAt;
		}
	}
	for (const [candidateKey, candidate] of entries) {
		if (candidateKey === normalizedKey) continue;
		if (normalizeStoreSessionKey(candidateKey) !== normalizedKey) continue;
		if (hasMismatchedCaseSensitiveDeliveryProof(candidate.entry, normalizedKey)) continue;
		legacyKeySet.add(candidateKey);
		const candidateUpdatedAt = candidate.entry.updatedAt ?? 0;
		if (!existing || candidateUpdatedAt > existingUpdatedAt) {
			existing = candidate;
			existingUpdatedAt = candidateUpdatedAt;
		}
	}
	return {
		normalizedKey,
		existing,
		legacyKeys: [...legacyKeySet]
	};
}
function resolveSessionStoreEntry(params) {
	const resolved = resolveSessionEntryCandidates({
		entries: Object.entries(params.store).map(([sessionKey, entry]) => ({
			entry,
			sessionKey
		})),
		sessionKey: params.sessionKey
	});
	return {
		normalizedKey: resolved.normalizedKey,
		existing: resolved.existing?.entry,
		legacyKeys: resolved.legacyKeys
	};
}
//#endregion
export { resolveSessionEntryCandidates as a, runQueuedStoreWrite as c, normalizeStoreSessionKey as i, hasMismatchedCaseSensitiveDeliveryProof as n, resolveSessionStoreEntry as o, isConfirmedLowercasedLegacyAlias as r, clearStoreWriterQueuesForTest as s, foldedSessionKeyAliasCandidates as t };
