import type {
	MemoryProvider,
	MemoryRecord,
} from "@singularity-forge/agent-core";

function recordId(memory: MemoryRecord): string {
	return String(memory.id ?? Date.now());
}

function searchableText(memory: MemoryRecord): string {
	return [
		memory.id,
		memory.text,
		memory.summary,
		...(Array.isArray(memory.tags) ? memory.tags : []),
	]
		.filter((part): part is string => typeof part === "string")
		.join(" ")
		.toLowerCase();
}

function matchesQuery(memory: MemoryRecord, query: string): boolean {
	const normalizedQuery = query.trim().toLowerCase();
	if (!normalizedQuery) return true;
	return searchableText(memory).includes(normalizedQuery);
}

/**
 * Provides local-first memory search with a future federated sync boundary.
 *
 * Purpose: let swarm/critic features share durable learnings through the same
 * MemoryProvider contract while remote federation is still behind a transport.
 *
 * Consumer: predictive execution and future UOK background critic integrations.
 */
export class FederatedMemoryProvider implements MemoryProvider {
	private localCache = new Map<string, MemoryRecord>();
	private remoteEndpoint?: string;

	constructor(remoteEndpoint?: string) {
		this.remoteEndpoint = remoteEndpoint;
	}

	async search(
		query: string,
		options?: { limit?: number; threshold?: number },
	): Promise<MemoryRecord[]> {
		const limit = Math.max(1, options?.limit ?? 20);
		const localResults = Array.from(this.localCache.values())
			.filter((memory) => matchesQuery(memory, query))
			.slice(0, limit);

		if (!this.remoteEndpoint || localResults.length >= limit) {
			return localResults;
		}

		// Remote federation intentionally remains a no-op until the daemon RPC
		// contract exists. Keeping this boundary explicit avoids fake network
		// behavior while preserving the constructor/API shape.
		return localResults;
	}

	async store(memory: MemoryRecord): Promise<void> {
		this.localCache.set(recordId(memory), memory);

		if (!this.remoteEndpoint) return;
		// Future daemon sync belongs here; storage must stay local-first and
		// non-blocking for agent loop callers.
	}
}
