/**
 * Tests for Mechanism 2 — milestone framing check.
 *
 * Covers:
 *   - Returns empty array when milestone context file is missing
 *   - Anti-goal keyword match produces a warning finding
 *   - No anti-goal match when keywords are absent
 *   - PROJECT.md vision mismatch produces an info finding
 *   - formatFramingFindings returns empty string for empty findings
 *   - formatFramingFindings returns formatted block with severity labels
 */

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

import {
	checkMilestoneFraming,
	formatFramingFindings,
} from "../milestone-framing-check.ts";

function makeProject(antiGoals?: string, projectMd?: string, contextMd?: string): string {
	const dir = mkdtempSync(join(tmpdir(), "sf-framing-"));
	mkdirSync(join(dir, ".sf", "milestones", "M001"), { recursive: true });
	if (antiGoals !== undefined) {
		writeFileSync(join(dir, ".sf", "ANTI-GOALS.md"), antiGoals, "utf-8");
	}
	if (projectMd !== undefined) {
		writeFileSync(join(dir, "PROJECT.md"), projectMd, "utf-8");
	}
	if (contextMd !== undefined) {
		writeFileSync(
			join(dir, ".sf", "milestones", "M001", "M001-CONTEXT.md"),
			contextMd,
			"utf-8",
		);
	}
	return dir;
}

test("framing-check: returns empty array when milestone context file is missing", () => {
	const dir = makeProject("- no federation", undefined, undefined);
	try {
		const findings = checkMilestoneFraming(dir, "M001");
		assert.deepEqual(findings, []);
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("framing-check: returns empty array when no anti-goals file exists", () => {
	const dir = makeProject(
		undefined,
		undefined,
		"# Context\n\nBuild a central federation system.\n",
	);
	try {
		const findings = checkMilestoneFraming(dir, "M001");
		// No anti-goals file → no anti-goal warnings
		const antiGoalFindings = findings.filter((f) => f.source === "anti_goal");
		assert.deepEqual(antiGoalFindings, []);
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("framing-check: anti-goal keyword match produces a warning finding", () => {
	const antiGoals =
		"# Anti-goals\n\n- No central federation of repositories across projects.\n";
	const context = "# Build federated central repository\n\nThis milestone implements federation.\n";
	const dir = makeProject(antiGoals, undefined, context);
	try {
		const findings = checkMilestoneFraming(dir, "M001");
		const warnings = findings.filter(
			(f) => f.source === "anti_goal" && f.severity === "warning",
		);
		assert.ok(warnings.length > 0, "should have at least one anti-goal warning");
		assert.ok(
			warnings[0].concern.includes("ANTI-GOALS.md"),
			"concern should reference ANTI-GOALS.md",
		);
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("framing-check: no anti-goal warning when context has no matching keywords", () => {
	const antiGoals =
		"# Anti-goals\n\n- No central federation of repositories across projects.\n";
	const context = "# Add login screen\n\nBuild the user authentication UI.\n";
	const dir = makeProject(antiGoals, undefined, context);
	try {
		const findings = checkMilestoneFraming(dir, "M001");
		const antiGoalWarnings = findings.filter((f) => f.source === "anti_goal");
		assert.deepEqual(antiGoalWarnings, []);
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("framing-check: PROJECT.md vision mismatch produces an info finding", () => {
	const projectMd =
		"# Project\n\nBuild a fast payment processing pipeline for merchants.\n";
	// Context completely unrelated to payment/processing/pipeline/merchants
	const context = "# Refactor CSS classes\n\nUpdate button colors and fonts.\n";
	const dir = makeProject(undefined, projectMd, context);
	try {
		const findings = checkMilestoneFraming(dir, "M001");
		const infoFindings = findings.filter(
			(f) => f.source === "project_vision" && f.severity === "info",
		);
		assert.ok(
			infoFindings.length > 0,
			"should have a project-vision info finding when context is unrelated",
		);
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

test("framing-check: no project-vision finding when PROJECT.md is absent", () => {
	const context = "# Some milestone\n\nDoes something.\n";
	const dir = makeProject(undefined, undefined, context);
	try {
		const findings = checkMilestoneFraming(dir, "M001");
		const visionFindings = findings.filter((f) => f.source === "project_vision");
		assert.deepEqual(visionFindings, []);
	} finally {
		rmSync(dir, { recursive: true, force: true });
	}
});

// ─── formatFramingFindings ────────────────────────────────────────────────────

test("formatFramingFindings: returns empty string for empty findings array", () => {
	const result = formatFramingFindings("M001", []);
	assert.strictEqual(result, "");
});

test("formatFramingFindings: returns block with milestone ID and severity labels", () => {
	const findings = [
		{
			concern: "Uses federation",
			source: "anti_goal" as const,
			severity: "warning" as const,
		},
		{
			concern: "No project link",
			source: "project_vision" as const,
			severity: "info" as const,
		},
	];
	const result = formatFramingFindings("M001", findings);
	assert.ok(result.includes("MILESTONE FRAMING CHECK"));
	assert.ok(result.includes("M001"));
	assert.ok(result.includes("WARNING"));
	assert.ok(result.includes("INFO"));
	assert.ok(result.includes("Uses federation"));
	assert.ok(result.includes("No project link"));
});
