/**
 * AI Memory Tools — Tools the agent can call to emit structured memories.
 *
 * Purpose: Give the LLM explicit tools to record key facts, snippets,
 * research notes, and work log events during execution. This makes memory
 * accumulation AI-driven rather than passive.
 *
 * Consumer: auto/phases.js dispatch path — injected into unit prompts.
 *
 * Design:
 * - Each tool validates input and stores to memory-repository.js
 * - Content hash deduplication prevents redundant entries
 * - Session-scoped by default, unit-tagged for traceability
 * - Returns confirmation to the agent so it knows the memory was recorded
 */

import { MEMORY_TYPES, storeMemory } from "./memory-repository.js";
import { getDatabase, isDbAvailable } from "./sf-db.js";
import { logWarning } from "./workflow-logger.js";

/**
 * Emit a key fact discovered during work.
 *
 * @param {object} params
 * @param {string} params.content — The fact to record
 * @param {string} [params.source] — Where this fact came from (file, tool, etc)
 * @param {string} [params.sessionId] — Override session ID
 * @param {string} [params.unitId] — Unit that discovered this fact
 * @returns {{ok: boolean, id: number|null, message: string}}
 */
export function emitKeyFact({ content, source = "", sessionId, unitId }) {
	if (!content || content.trim().length === 0) {
		return { ok: false, id: null, message: "Content is required" };
	}
	if (!isDbAvailable()) {
		return { ok: false, id: null, message: "Database not available" };
	}

	try {
		const db = getDatabase();
		const sid = sessionId || process.env.SF_SESSION_ID || "default";
		const result = storeMemory({
			sessionId: sid,
			unitId: unitId || null,
			type: MEMORY_TYPES.KEY_FACT,
			content: content.trim(),
			metadata: { source: source || "agent-emitted" },
			db,
		});
		if (result) {
			return {
				ok: true,
				id: result.id,
				message: `Key fact recorded (#${result.id})`,
			};
		}
		return { ok: false, id: null, message: "Duplicate or failed to store" };
	} catch (err) {
		logWarning("ai-memory", "emitKeyFact failed", { error: String(err) });
		return { ok: false, id: null, message: String(err) };
	}
}

/**
 * Emit a key code snippet discovered during work.
 *
 * @param {object} params
 * @param {string} params.content — The code snippet
 * @param {string} [params.filePath] — Source file path
 * @param {string} [params.language] — Programming language
 * @param {string} [params.sessionId] — Override session ID
 * @param {string} [params.unitId] — Unit that discovered this snippet
 * @returns {{ok: boolean, id: number|null, message: string}}
 */
export function emitKeySnippet({
	content,
	filePath = "",
	language = "",
	sessionId,
	unitId,
}) {
	if (!content || content.trim().length === 0) {
		return { ok: false, id: null, message: "Content is required" };
	}
	if (!isDbAvailable()) {
		return { ok: false, id: null, message: "Database not available" };
	}

	try {
		const db = getDatabase();
		const sid = sessionId || process.env.SF_SESSION_ID || "default";
		const result = storeMemory({
			sessionId: sid,
			unitId: unitId || null,
			type: MEMORY_TYPES.KEY_SNIPPET,
			content: content.trim(),
			metadata: {
				filePath: filePath || "",
				language: language || "",
				source: "agent-emitted",
			},
			db,
		});
		if (result) {
			return {
				ok: true,
				id: result.id,
				message: `Key snippet recorded (#${result.id})`,
			};
		}
		return { ok: false, id: null, message: "Duplicate or failed to store" };
	} catch (err) {
		logWarning("ai-memory", "emitKeySnippet failed", { error: String(err) });
		return { ok: false, id: null, message: String(err) };
	}
}

/**
 * Emit a research note.
 *
 * @param {object} params
 * @param {string} params.content — The research note
 * @param {string} [params.topic] — Topic/tag for the note
 * @param {string} [params.sessionId] — Override session ID
 * @param {string} [params.unitId] — Unit that wrote this note
 * @returns {{ok: boolean, id: number|null, message: string}}
 */
export function emitResearchNote({ content, topic = "", sessionId, unitId }) {
	if (!content || content.trim().length === 0) {
		return { ok: false, id: null, message: "Content is required" };
	}
	if (!isDbAvailable()) {
		return { ok: false, id: null, message: "Database not available" };
	}

	try {
		const db = getDatabase();
		const sid = sessionId || process.env.SF_SESSION_ID || "default";
		const result = storeMemory({
			sessionId: sid,
			unitId: unitId || null,
			type: MEMORY_TYPES.RESEARCH_NOTE,
			content: content.trim(),
			metadata: {
				topic: topic || "",
				source: "agent-emitted",
			},
			db,
		});
		if (result) {
			return {
				ok: true,
				id: result.id,
				message: `Research note recorded (#${result.id})`,
			};
		}
		return { ok: false, id: null, message: "Duplicate or failed to store" };
	} catch (err) {
		logWarning("ai-memory", "emitResearchNote failed", { error: String(err) });
		return { ok: false, id: null, message: String(err) };
	}
}

/**
 * Log a work event.
 *
 * @param {object} params
 * @param {string} params.event — Event description
 * @param {string} [params.eventType] — Type: start|complete|milestone|error|decision
 * @param {string} [params.sessionId] — Override session ID
 * @param {string} [params.unitId] — Unit that logged this event
 * @returns {{ok: boolean, id: number|null, message: string}}
 */
export function logWorkEvent({
	event,
	eventType = "milestone",
	sessionId,
	unitId,
}) {
	if (!event || event.trim().length === 0) {
		return { ok: false, id: null, message: "Event is required" };
	}
	if (!isDbAvailable()) {
		return { ok: false, id: null, message: "Database not available" };
	}

	try {
		const db = getDatabase();
		const sid = sessionId || process.env.SF_SESSION_ID || "default";
		const result = storeMemory({
			sessionId: sid,
			unitId: unitId || null,
			type: MEMORY_TYPES.WORK_LOG,
			content: event.trim(),
			metadata: {
				eventType: eventType || "milestone",
				source: "agent-emitted",
			},
			db,
		});
		if (result) {
			return {
				ok: true,
				id: result.id,
				message: `Work event logged (#${result.id})`,
			};
		}
		return { ok: false, id: null, message: "Duplicate or failed to store" };
	} catch (err) {
		logWarning("ai-memory", "logWorkEvent failed", { error: String(err) });
		return { ok: false, id: null, message: String(err) };
	}
}

/**
 * Format all memories for injection into a prompt.
 *
 * @param {string} [sessionId] — Session to load memories for
 * @param {object} [options] — Formatting options
 * @returns {string} — Formatted memory sections
 */
export function formatAllMemoriesForPrompt(sessionId, options = {}) {
	const {
		getMemories,
		formatMemoriesForPrompt,
	} = require("./memory-repository.js");
	const db = isDbAvailable() ? getDatabase() : null;
	if (!db) return "";

	const sid = sessionId || process.env.SF_SESSION_ID || "default";
	const sections = [];
	const maxChars = options.maxChars || 8000;

	// Key facts
	const facts = getMemories({
		sessionId: sid,
		type: MEMORY_TYPES.KEY_FACT,
		limit: options.factLimit ?? 30,
		db,
	});
	if (facts.length > 0) {
		const formatted = formatMemoriesForPrompt(facts, {
			header: "Key Facts",
			maxChars: Math.floor(maxChars * 0.3),
		});
		if (formatted) sections.push(formatted);
	}

	// Key snippets
	const snippets = getMemories({
		sessionId: sid,
		type: MEMORY_TYPES.KEY_SNIPPET,
		limit: options.snippetLimit ?? 15,
		db,
	});
	if (snippets.length > 0) {
		const formatted = formatMemoriesForPrompt(snippets, {
			header: "Key Snippets",
			maxChars: Math.floor(maxChars * 0.4),
		});
		if (formatted) sections.push(formatted);
	}

	// Research notes
	const notes = getMemories({
		sessionId: sid,
		type: MEMORY_TYPES.RESEARCH_NOTE,
		limit: options.noteLimit ?? 15,
		db,
	});
	if (notes.length > 0) {
		const formatted = formatMemoriesForPrompt(notes, {
			header: "Research Notes",
			maxChars: Math.floor(maxChars * 0.2),
		});
		if (formatted) sections.push(formatted);
	}

	// Work log (last 10 events)
	const logs = getMemories({
		sessionId: sid,
		type: MEMORY_TYPES.WORK_LOG,
		limit: options.logLimit ?? 10,
		db,
	});
	if (logs.length > 0) {
		const formatted = formatMemoriesForPrompt(logs, {
			header: "Work Log",
			maxChars: Math.floor(maxChars * 0.1),
		});
		if (formatted) sections.push(formatted);
	}

	return sections.join("\n\n");
}
