/**
 * Reciprocal Rank Fusion (RRF) implementation.
 *
 * RRF combines multiple ranked lists into a single fused ranking without
 * requiring score normalization. The standard constant K=60 is used to
 * dampen the influence of very high ranks.
 *
 * Reference: Cormack, Clarke & Buettcher (2009) — "Reciprocal Rank Fusion
 * outperforms Condorcet and individual Rank Learning Methods."
 */

/** A single item in a ranked list, identified by an opaque string id. */
export interface RankedItem {
	id: string;
	score: number;
}

/** Output item produced by RRF. */
export interface FusedItem {
	id: string;
	rrfScore: number;
}

/**
 * Combine multiple ranked lists using Reciprocal Rank Fusion.
 *
 * Each item's contribution per list is `1 / (K + rank + 1)` where rank is
 * 0-based. Items that appear in multiple lists accumulate contributions from
 * each list, naturally boosting items ranked highly across several sources.
 *
 * @param rankings - One or more arrays of `{ id, score }` items sorted by
 *                   descending relevance (index 0 = most relevant).
 * @returns Fused array sorted by descending rrfScore, deduplicated by id.
 */
export function reciprocalRankFusion(
	...rankings: Array<Array<RankedItem>>
): Array<FusedItem> {
	const K = 60; // Standard RRF constant.
	const scores = new Map<string, number>();

	for (const ranking of rankings) {
		ranking.forEach(({ id }, rank) => {
			const current = scores.get(id) ?? 0;
			scores.set(id, current + 1 / (K + rank + 1));
		});
	}

	return Array.from(scores.entries())
		.map(([id, rrfScore]) => ({ id, rrfScore }))
		.sort((a, b) => b.rrfScore - a.rrfScore);
}
