import { sanitizeTsquery } from "@platform-modules/util/fts/sanitize-tsquery";
//#region src/index.ts
const MAX_CURSOR_OFFSET = 1e4;
function encodeCursor(cursor) {
	return btoa(JSON.stringify(cursor));
}
/**
* Decode a client-supplied cursor (untrusted trust-boundary input). Returns
* null on any malformed/forged token — caller treats null as "first page"
* (lenient reset), never throws.
*/
function decodeCursor(encoded) {
	let parsed;
	try {
		parsed = JSON.parse(atob(encoded));
	} catch {
		return null;
	}
	if (typeof parsed !== "object" || parsed === null) return null;
	const { entityType, offset } = parsed;
	if (typeof entityType !== "string") return null;
	if (typeof offset !== "number" || !Number.isInteger(offset) || offset < 0 || offset > MAX_CURSOR_OFFSET) return null;
	return {
		entityType,
		offset
	};
}
function resolveEntityTypes(registry, entityTypes) {
	const keys = Object.keys(registry);
	if (!entityTypes || entityTypes.length === 0) return keys;
	return entityTypes.filter((type) => type in registry);
}
async function searchEntities(registry, params) {
	const sanitizedQuery = sanitizeTsquery(params.query);
	const candidateTypes = resolveEntityTypes(registry, params.entityTypes);
	const isSingleType = candidateTypes.length === 1;
	let offset = 0;
	let singleEntityType;
	if (isSingleType) {
		singleEntityType = candidateTypes[0];
		if (params.cursor) {
			const decoded = decodeCursor(params.cursor);
			if (decoded && decoded.entityType === singleEntityType) offset = decoded.offset;
		}
	}
	const providerResults = await Promise.all(candidateTypes.map(async (entityType) => {
		const window = {
			limit: params.limit,
			offset: isSingleType ? offset : 0
		};
		const provider = registry[entityType];
		return {
			entityType,
			...await provider(sanitizedQuery, params.ctx, window)
		};
	}));
	const groups = providerResults.filter((result) => result.total > 0 || result.hits.length > 0).map(({ entityType, hits, total }) => ({
		entityType,
		hits,
		total
	})).sort((a, b) => b.total - a.total);
	let nextCursor = null;
	if (isSingleType && singleEntityType) {
		const raw = providerResults.find((result) => result.entityType === singleEntityType)?.total ?? 0;
		if (offset + params.limit < raw) nextCursor = encodeCursor({
			entityType: singleEntityType,
			offset: offset + params.limit
		});
	}
	return {
		groups,
		nextCursor
	};
}
//#endregion
export { searchEntities };
