/**
 * Test the canonical static memory score formula.
 *
 * Previous formula `confidence * (1 + hit_count * 0.1)` was linear
 * in hit_count → self-reinforcing popularity (a memory that surfaces
 * once gets hit_count++, ranks higher, surfaces again, hits again...).
 *
 * Replacement `confidence * (1 + log(1 + hit_count) * 0.5)` keeps
 * the curve at hit_count=0 identical, climbs noticeably at low hits
 * (1.55× at h=2 vs 1.2× before — old was *too* flat there), and
 * flattens fast (3.30× at h=100 vs the runaway 11.0× under the
 * linear formula).
 *
 * This test pins:
 *   - identity at h=0
 *   - monotonic non-decreasing in h
 *   - bounded growth (no value above an upper guardrail)
 *   - defensiveness against NaN / negative inputs
 */

import assert from "node:assert/strict";
import { test } from "vitest";
import { computeStaticMemoryScore } from "../sf-db/sf-db-memory.js";

test("identity at hit_count=0", () => {
	assert.equal(computeStaticMemoryScore(0.8, 0), 0.8);
	assert.equal(computeStaticMemoryScore(0.5, 0), 0.5);
	assert.equal(computeStaticMemoryScore(1.0, 0), 1.0);
});

test("monotonic non-decreasing in hit_count", () => {
	const c = 0.7;
	let prev = computeStaticMemoryScore(c, 0);
	for (let h = 1; h <= 1000; h++) {
		const curr = computeStaticMemoryScore(c, h);
		assert.ok(curr >= prev, `non-monotonic at h=${h}: ${curr} < ${prev}`);
		prev = curr;
	}
});

test("growth is bounded under runaway hit counts (log-shaped)", () => {
	// Hand-computed pins: confidence * (1 + log(1+h)*0.5).
	// h=100 → 1 + log(101)*0.5 ≈ 1 + 2.3081 ≈ 3.308
	// h=10000 → 1 + log(10001)*0.5 ≈ 1 + 4.605 ≈ 5.605
	const heavy = computeStaticMemoryScore(1.0, 10000);
	assert.ok(heavy < 7, `score at h=10000 too high: ${heavy}`);
	const veryHeavy = computeStaticMemoryScore(1.0, 1_000_000);
	assert.ok(veryHeavy < 10, `score at h=1M too high: ${veryHeavy}`);
});

test("monotonic in confidence at fixed hit_count", () => {
	for (let h = 0; h <= 100; h += 10) {
		const low = computeStaticMemoryScore(0.2, h);
		const mid = computeStaticMemoryScore(0.5, h);
		const high = computeStaticMemoryScore(0.9, h);
		assert.ok(low <= mid, `low<=mid at h=${h}: ${low} ${mid}`);
		assert.ok(mid <= high, `mid<=high at h=${h}: ${mid} ${high}`);
	}
});

test("NaN / negative inputs collapse to 0 rather than poisoning sorts", () => {
	assert.equal(computeStaticMemoryScore(Number.NaN, 5), 0);
	assert.equal(computeStaticMemoryScore(-0.5, 5), 0);
	// Negative hit_count clamps to 0 → returns base confidence.
	assert.equal(computeStaticMemoryScore(0.7, -3), 0.7);
	assert.equal(computeStaticMemoryScore(0.7, Number.NaN), 0.7);
});

test("regression: new formula tames the runaway compared to linear", () => {
	// Old: confidence * (1 + hit_count * 0.1) → at h=100 was 11×.
	// New: cap at 1M hits stays under 10× (effective ceiling for any
	// realistic memory). This pins the central property of the fix.
	const linearAtHundred = 0.5 * (1 + 100 * 0.1); // 5.5
	const newAtHundred = computeStaticMemoryScore(0.5, 100);
	assert.ok(
		newAtHundred < linearAtHundred,
		`new (${newAtHundred}) should be < linear (${linearAtHundred}) at h=100`,
	);
});
