// SF — Decisions -> memories backfill
//
// Idempotent one-shot migration that copies every active decisions row into
// the memories table with category="architecture". Idempotency is enforced
// by tagging each backfilled memory's content with the original decision ID
// via a structured prefix and skipping any decision whose ID already appears
// in the memories table.
//
// Triggered opportunistically by buildBeforeAgentStartResult so the cost
// only ever fires once per project. Costs O(N) inserts on first run where
// N is the active-decisions count; subsequent runs are an O(N) lookup that
// finds existing markers and exits.

import { createMemory } from "./memory-store.js";
import { _getAdapter, isDbAvailable } from "./sf-db.js";
import { logWarning } from "./workflow-logger.js";
/**
 * Backfill active decisions rows into the memories table.
 *
 * - Idempotent (per-row): every row written embeds
 *   `[decision:${decisionId}]` as a prefix in the content so we can
 *   detect existing backfills via a LIKE query. Only decisions whose id
 *   is already present in the memory store are skipped.
 * - Best-effort: never throws. Logs and returns 0 on failure so a broken
 *   backfill cannot block agent startup.
 * - Active-only: skips rows where `superseded_by IS NOT NULL`. Superseded
 *   decisions are historical record; the memory store is for active
 *   knowledge.
 *
 * Returns the number of memories written (0 when already backfilled or
 * when the DB has no decisions). Callers can log the result or surface it
 * to the user.
 */
export function backfillDecisionsToMemories() {
	if (!isDbAvailable()) return 0;
	const adapter = _getAdapter();
	if (!adapter) return 0;
	try {
		const decisions = adapter
			.prepare(
				"SELECT id, when_context, scope, decision, choice, rationale, made_by, revisable, superseded_by FROM decisions WHERE superseded_by IS NULL",
			)
			.all();
		if (decisions.length === 0) return 0;
		// Per-row idempotency: each backfilled memory starts with
		// "[decision:<id>]" in the content. Detect existing rows via LIKE.
		const checkExisting = adapter.prepare(
			"SELECT 1 FROM memories WHERE content LIKE :pattern LIMIT 1",
		);
		let written = 0;
		for (const raw of decisions) {
			const row = {
				id: String(raw["id"] ?? ""),
				when_context: String(raw["when_context"] ?? ""),
				scope: String(raw["scope"] ?? ""),
				decision: String(raw["decision"] ?? ""),
				choice: String(raw["choice"] ?? ""),
				rationale: String(raw["rationale"] ?? ""),
				made_by: String(raw["made_by"] ?? "agent"),
				revisable: String(raw["revisable"] ?? ""),
				superseded_by:
					raw["superseded_by"] == null ? null : String(raw["superseded_by"]),
			};
			if (!row.id) continue;
			if (checkExisting.get({ ":pattern": `[decision:${row.id}] %` })) continue;
			const content = synthesizeContent(row);
			const id = createMemory({
				category: "architecture",
				content,
				confidence: 0.85,
			});
			if (id) written += 1;
		}
		return written;
	} catch (e) {
		logWarning(
			"memory-backfill",
			`decisions->memories backfill failed: ${e.message}`,
		);
		return 0;
	}
}
/**
 * Combine the decision's structured fields into a 1-3 sentence content
 * string suitable for keyword retrieval and human review.
 *
 * Format: "[decision:<id>] <decision> Chose: <choice>. Rationale: <rationale>."
 * The "[decision:<id>]" prefix enables idempotent backfill detection.
 * Truncates each field to keep the synthesized line under ~600 chars.
 */
function synthesizeContent(row) {
	const trim = (value, max) => {
		const cleaned = value.replace(/\s+/g, " ").trim();
		return cleaned.length > max ? cleaned.slice(0, max - 1) + "…" : cleaned;
	};
	const parts = [`[decision:${row.id}]`];
	const decision = trim(row.decision, 240);
	const choice = trim(row.choice, 200);
	const rationale = trim(row.rationale, 200);
	if (decision) parts.push(decision);
	if (choice) parts.push(`Chose: ${choice}.`);
	if (rationale) parts.push(`Rationale: ${rationale}.`);
	return parts.join(" ");
}
