/**
 * design/weighted-map.mjs — tiny shared helpers for the taste-infer extractors.
 *
 * Both `extract-url.mjs` and `extract-repo.mjs` accumulate observed design
 * tokens (colours / fonts / spacing / etc.) into Map<string, number>
 * counters and then convert those maps to weighted arrays sorted desc by
 * weight, tiebreak alphabetical. The helpers live here so the two
 * extractors share one implementation — per MI-3 in the C43 review.
 *
 * Do NOT add LLM-client-adjacent code to this file. Per invariant I-1, any
 * module reachable from the extractor transitive import graph stays free
 * of LLM SDKs — the I-1 AST invariant test only walks the extractors
 * directly, so keeping this file small and dependency-free is the
 * simplest way to preserve the invariant long term.
 */

/**
 * Map-based counter. Non-string / empty keys are ignored. Keeps insertion
 * order for deterministic output after `toWeighted`.
 *
 * @param {Map<string, number>} map
 * @param {string} key
 * @param {number} [by]
 */
export function bumpCount(map, key, by = 1) {
	if (!key || typeof key !== 'string') return
	const prev = map.get(key) ?? 0
	map.set(key, prev + by)
}

/**
 * Convert a count map into `{ value, weight }[]`, sorted desc by weight
 * with alphabetical tiebreak so output is deterministic even when two
 * values share a weight.
 *
 * @param {Map<string, number>} map
 * @returns {{ value: string, weight: number }[]}
 */
export function toWeighted(map) {
	return [...map.entries()]
		.map(([value, weight]) => ({ value, weight }))
		.sort((a, b) => b.weight - a.weight || a.value.localeCompare(b.value))
}
