/**
 * self-feedback-db.test.mjs — DB-backed self-feedback source of truth.
 *
 * Purpose: prove forge-local self-feedback uses SQLite as primary state while
 * keeping markdown as a generated projection and JSONL as versioned fallback.
 */
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 { afterEach, test } from "vitest";
import {
	compactSelfFeedbackMarkdown,
	markResolved,
	readAllSelfFeedback,
	recordSelfFeedback,
} from "../self-feedback.js";
import {
	closeDatabase,
	listSelfFeedbackEntries,
	openDatabase,
} from "../sf-db.js";

const tmpDirs = [];

afterEach(() => {
	closeDatabase();
	while (tmpDirs.length > 0) {
		const dir = tmpDirs.pop();
		if (dir) rmSync(dir, { recursive: true, force: true });
	}
});

function makeForgeProject() {
	const dir = mkdtempSync(join(tmpdir(), "sf-self-feedback-db-"));
	tmpDirs.push(dir);
	mkdirSync(join(dir, ".sf"), { recursive: true });
	writeFileSync(
		join(dir, "package.json"),
		JSON.stringify({ name: "singularity-forge" }),
	);
	openDatabase(join(dir, ".sf", "sf.db"));
	return dir;
}

test("recordSelfFeedback_when_db_available_writes_sqlite_and_versioned_projection", () => {
	const project = makeForgeProject();

	const result = recordSelfFeedback(
		{
			kind: "test-feedback",
			severity: "medium",
			summary: "DB backed feedback",
			evidence: "unit test",
		},
		project,
	);

	assert.ok(result?.entry.id);
	assert.equal(result.entry.schemaVersion, 1);
	assert.equal(existsSync(join(project, ".sf", "self-feedback.jsonl")), false);
	const rows = listSelfFeedbackEntries();
	assert.equal(rows.length, 1);
	assert.equal(rows[0].id, result.entry.id);
	assert.equal(rows[0].summary, "DB backed feedback");
	const markdown = readFileSync(
		join(project, ".sf", "SELF-FEEDBACK.md"),
		"utf-8",
	);
	assert.match(markdown, /durable source of truth is `.sf\/sf.db`/);
	assert.match(markdown, /DB backed feedback/);
});

test("readAllSelfFeedback_imports_legacy_jsonl_once_when_db_available", () => {
	const project = makeForgeProject();
	const legacyEntry = {
		id: "legacy-1",
		ts: "2026-05-07T00:00:00.000Z",
		kind: "legacy-feedback",
		severity: "high",
		blocking: true,
		summary: "Legacy JSONL entry",
		repoIdentity: "forge",
		sfVersion: "old",
		basePath: project,
	};
	writeFileSync(
		join(project, ".sf", "self-feedback.jsonl"),
		`${JSON.stringify(legacyEntry)}\n`,
	);

	const first = readAllSelfFeedback(project);
	const second = readAllSelfFeedback(project);

	assert.equal(first.length, 1);
	assert.equal(second.length, 1);
	assert.equal(first[0].id, "legacy-1");
	assert.equal(listSelfFeedbackEntries().length, 1);
});

test("markResolved_when_db_available_updates_sqlite_and_markdown_projection", () => {
	const project = makeForgeProject();
	const result = recordSelfFeedback(
		{
			kind: "resolvable-feedback",
			severity: "high",
			summary: "Resolve through DB",
		},
		project,
	);
	assert.ok(result?.entry.id);

	const ok = markResolved(
		result.entry.id,
		{
			reason: "verified fix",
			evidence: { kind: "agent-fix", commitSha: "abcdef123456" },
			criteriaMet: ["test passed"],
		},
		project,
	);

	assert.equal(ok, true);
	const [entry] = readAllSelfFeedback(project);
	assert.ok(entry.resolvedAt);
	assert.equal(entry.resolvedReason, "verified fix");
	assert.deepEqual(entry.resolvedCriteriaMet, ["test passed"]);
	const markdown = readFileSync(
		join(project, ".sf", "SELF-FEEDBACK.md"),
		"utf-8",
	);
	assert.match(markdown, /No unresolved self-feedback entries/);
	assert.match(markdown, /Recently Resolved/);
});

test("compactSelfFeedbackMarkdown_when_projection_stale_rewrites_from_sqlite", () => {
	const project = makeForgeProject();
	const result = recordSelfFeedback(
		{
			kind: "stale-projection",
			severity: "medium",
			summary: "Projection should be repaired",
		},
		project,
	);
	assert.ok(result?.entry.id);
	writeFileSync(
		join(project, ".sf", "SELF-FEEDBACK.md"),
		"durable source of truth is `self-feedback.jsonl`\n",
	);

	assert.equal(compactSelfFeedbackMarkdown(project), true);

	const markdown = readFileSync(
		join(project, ".sf", "SELF-FEEDBACK.md"),
		"utf-8",
	);
	assert.match(markdown, /durable source of truth is `.sf\/sf.db`/);
	assert.match(markdown, /Projection should be repaired/);
	assert.doesNotMatch(
		markdown,
		/durable source of truth is `self-feedback\.jsonl`/,
	);
});
