/**
 * Tests for Mechanism 4 — KNOWLEDGE.md compounding from judgment log.
 *
 * Covers:
 *   - compoundLearningsIntoKnowledge returns {added:0, skipped:0} with no high-confidence entries
 *   - high-confidence entries are appended under a section heading
 *   - low/medium confidence entries are not included
 *   - duplicate entries are skipped (deduplication)
 *   - creates KNOWLEDGE.md from scratch if missing
 *   - appends to existing KNOWLEDGE.md
 */

import assert from "node:assert/strict";
import {
	existsSync,
	mkdirSync,
	mkdtempSync,
	readFileSync,
	rmSync,
	writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from 'vitest';

import { appendJudgment } from "../judgment-log.ts";
import { compoundLearningsIntoKnowledge } from "../knowledge-compounding.ts";

function makeTmpProject(): string {
	const dir = mkdtempSync(join(tmpdir(), "sf-compound-"));
	mkdirSync(join(dir, ".sf"), { recursive: true });
	return dir;
}

test("knowledge-compounding: returns {added:0, skipped:0} when no entries exist", () => {
	const dir = makeTmpProject();
	try {
		const result = compoundLearningsIntoKnowledge(dir, "M001");
		assert.deepEqual(result, { added: 0, skipped: 0 });
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("knowledge-compounding: returns {added:0, skipped:0} when only low/medium confidence entries", () => {
	const dir = makeTmpProject();
	try {
		appendJudgment(dir, {
			unitId: "M001/S01/T01",
			decision: "low decision",
			alternatives: [],
			reasoning: "low reason",
			confidence: "low",
		});
		appendJudgment(dir, {
			unitId: "M001/S01/T02",
			decision: "medium decision",
			alternatives: [],
			reasoning: "medium reason",
			confidence: "medium",
		});
		const result = compoundLearningsIntoKnowledge(dir, "M001");
		assert.deepEqual(result, { added: 0, skipped: 0 });
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("knowledge-compounding: appends high-confidence entries to KNOWLEDGE.md", () => {
	const dir = makeTmpProject();
	try {
		appendJudgment(dir, {
			unitId: "M001/S01/T01",
			decision: "Used uv run",
			alternatives: ["python directly"],
			reasoning: "uv.lock present",
			confidence: "high",
		});
		const result = compoundLearningsIntoKnowledge(dir, "M001");
		assert.strictEqual(result.added, 1);
		assert.strictEqual(result.skipped, 0);

		const knowledgePath = join(dir, ".sf", "KNOWLEDGE.md");
		assert.ok(existsSync(knowledgePath), "KNOWLEDGE.md should be created");
		const content = readFileSync(knowledgePath, "utf-8");
		assert.ok(content.includes("## Learned during M001"));
		assert.ok(content.includes("Used uv run"));
		assert.ok(content.includes("uv.lock present"));
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("knowledge-compounding: creates KNOWLEDGE.md from scratch when missing", () => {
	const dir = makeTmpProject();
	try {
		appendJudgment(dir, {
			unitId: "M001/S01/T01",
			decision: "new decision",
			alternatives: [],
			reasoning: "new reasoning",
			confidence: "high",
		});
		compoundLearningsIntoKnowledge(dir, "M001");
		const knowledgePath = join(dir, ".sf", "KNOWLEDGE.md");
		assert.ok(existsSync(knowledgePath));
		const content = readFileSync(knowledgePath, "utf-8");
		assert.ok(content.includes("# Project Knowledge"));
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("knowledge-compounding: appends to existing KNOWLEDGE.md", () => {
	const dir = makeTmpProject();
	try {
		const knowledgePath = join(dir, ".sf", "KNOWLEDGE.md");
		writeFileSync(
			knowledgePath,
			"# Project Knowledge\n\n## Existing section\n\n- K001: Old rule.\n",
			"utf-8",
		);
		appendJudgment(dir, {
			unitId: "M001/S01/T01",
			decision: "new high decision",
			alternatives: [],
			reasoning: "new reasoning",
			confidence: "high",
		});
		const result = compoundLearningsIntoKnowledge(dir, "M001");
		assert.strictEqual(result.added, 1);

		const content = readFileSync(knowledgePath, "utf-8");
		assert.ok(content.includes("K001: Old rule"), "existing content should be preserved");
		assert.ok(content.includes("## Learned during M001"));
		assert.ok(content.includes("new high decision"));
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("knowledge-compounding: deduplicates entries on second run", () => {
	const dir = makeTmpProject();
	try {
		appendJudgment(dir, {
			unitId: "M001/S01/T01",
			decision: "dedup decision",
			alternatives: [],
			reasoning: "dedup reasoning",
			confidence: "high",
		});
		const r1 = compoundLearningsIntoKnowledge(dir, "M001");
		assert.strictEqual(r1.added, 1);

		// Run again — same entry should be skipped
		const r2 = compoundLearningsIntoKnowledge(dir, "M001");
		assert.strictEqual(r2.added, 0);
		assert.strictEqual(r2.skipped, 1);
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("knowledge-compounding: only compounds entries for the specified milestoneId", () => {
	const dir = makeTmpProject();
	try {
		appendJudgment(dir, {
			unitId: "M001/S01/T01",
			decision: "M001 decision",
			alternatives: [],
			reasoning: "M001 reasoning",
			confidence: "high",
		});
		appendJudgment(dir, {
			unitId: "M002/S01/T01",
			decision: "M002 decision",
			alternatives: [],
			reasoning: "M002 reasoning",
			confidence: "high",
		});
		const result = compoundLearningsIntoKnowledge(dir, "M001");
		assert.strictEqual(result.added, 1);

		const content = readFileSync(join(dir, ".sf", "KNOWLEDGE.md"), "utf-8");
		assert.ok(content.includes("M001 decision"), "should include M001 entry");
		assert.ok(!content.includes("M002 decision"), "should not include M002 entry");
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});
