/**
 * Memory Repository Tests
 *
 * Tests for structured memory storage (key facts, snippets, notes).
 */

import { beforeEach, describe, expect, test } from "vitest";
import {
	ensureMemoryTable,
	formatMemoriesForPrompt,
	getMemories,
	getMemoryCounts,
	MEMORY_TYPES,
	pruneMemories,
	storeMemory,
} from "../memory-repository.js";
import { closeDatabase, getDatabase, openDatabase } from "../sf-db.js";

describe("Memory Repository", () => {
	let db;
	let sessionId;

	beforeEach(() => {
		closeDatabase();
		openDatabase(":memory:");
		db = getDatabase();
		sessionId = `test-session-${Date.now()}`;
		ensureMemoryTable(db);
	});

	test("storeMemory_creates_key_fact", () => {
		const result = storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "SF uses SQLite for canonical state",
			db,
		});
		expect(result).not.toBeNull();
		expect(result.created).toBe(true);
		expect(result.deduped).toBe(false);
		expect(result.id).toBeGreaterThan(0);
	});

	test("storeMemory_dedupes_same_content", () => {
		const r1 = storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "Same content",
			db,
		});
		const r2 = storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "Same content",
			db,
		});
		expect(r1.id).toBe(r2.id);
		expect(r2.deduped).toBe(true);
	});

	test("storeMemory_with_metadata", () => {
		const result = storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_SNIPPET,
			content: "const x = 1;",
			metadata: {
				filepath: "src/foo.js",
				line_number: 42,
				description: "Init",
			},
			db,
		});
		expect(result.created).toBe(true);
		const memories = getMemories({ sessionId, db });
		expect(memories[0].metadata).toEqual({
			filepath: "src/foo.js",
			line_number: 42,
			description: "Init",
		});
	});

	test("getMemories_filters_by_type", () => {
		storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "Fact 1",
			db,
		});
		storeMemory({
			sessionId,
			type: MEMORY_TYPES.RESEARCH_NOTE,
			content: "Note 1",
			db,
		});
		storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "Fact 2",
			db,
		});

		const facts = getMemories({ sessionId, type: MEMORY_TYPES.KEY_FACT, db });
		expect(facts.length).toBe(2);
		expect(facts.every((m) => m.type === "key_fact")).toBe(true);
	});

	test("getMemories_searches_by_query", () => {
		storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "SQLite is the canonical store",
			db,
		});
		storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "Node 26 is the target runtime",
			db,
		});

		const results = getMemories({ sessionId, query: "SQLite", db });
		expect(results.length).toBe(1);
		expect(results[0].content).toContain("SQLite");
	});

	test("getMemoryCounts_returns_counts_by_type", () => {
		storeMemory({ sessionId, type: MEMORY_TYPES.KEY_FACT, content: "F1", db });
		storeMemory({ sessionId, type: MEMORY_TYPES.KEY_FACT, content: "F2", db });
		storeMemory({
			sessionId,
			type: MEMORY_TYPES.RESEARCH_NOTE,
			content: "N1",
			db,
		});

		const counts = getMemoryCounts(sessionId, db);
		expect(counts.key_fact).toBe(2);
		expect(counts.research_note).toBe(1);
	});

	test("formatMemoriesForPrompt_formats_with_header", () => {
		const memories = [
			{ type: "key_fact", content: "Fact A" },
			{ type: "key_fact", content: "Fact B" },
		];
		const formatted = formatMemoriesForPrompt(memories, {
			header: "Key Facts:",
		});
		expect(formatted).toContain("Key Facts:");
		expect(formatted).toContain("[key_fact] Fact A");
		expect(formatted).toContain("[key_fact] Fact B");
	});

	test("formatMemoriesForPrompt_respects_maxChars", () => {
		const memories = Array.from({ length: 10 }, (_, i) => ({
			type: "key_fact",
			content: `This is a very long fact number ${i} with lots of content to test truncation`,
		}));
		const formatted = formatMemoriesForPrompt(memories, { maxChars: 100 });
		expect(formatted.length).toBeLessThanOrEqual(150); // header + some content + "..."
		expect(formatted).toContain("...");
	});

	test("pruneMemories_deletes_old_entries", () => {
		storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "Old fact",
			db,
		});
		// Manually set created_at to past
		db.prepare(
			`UPDATE sf_memory SET created_at = '2020-01-01' WHERE session_id = ?`,
		).run(sessionId);

		storeMemory({
			sessionId,
			type: MEMORY_TYPES.KEY_FACT,
			content: "New fact",
			db,
		});

		const deleted = pruneMemories(sessionId, "2025-01-01", db);
		expect(deleted).toBe(1);

		const remaining = getMemories({ sessionId, db });
		expect(remaining.length).toBe(1);
		expect(remaining[0].content).toBe("New fact");
	});

	test("storeMemory_rejects_invalid_type", () => {
		const result = storeMemory({
			sessionId,
			type: "invalid_type",
			content: "Should fail",
			db,
		});
		expect(result).toBeNull();
	});

	test("storeMemory_requires_sessionId", () => {
		const result = storeMemory({
			type: MEMORY_TYPES.KEY_FACT,
			content: "Should fail",
			db,
		});
		expect(result).toBeNull();
	});
});
