/**
 * Judgment log — records agent decision-making during autonomous mode.
 *
 * When the agent makes a non-trivial call between alternatives, it logs a
 * JudgmentEntry. These accumulate in the SQLite judgments table (schema v40+)
 * with fallback to .sf/runtime/judgment-log.jsonl for legacy environments.
 *
 * Storage: sf.db judgments table (preferred) or sfRuntimeRoot/judgment-log.jsonl (fallback).
 *
 * The tool `sf_log_judgment` (registered in dynamic-tools.ts or equivalent)
 * calls appendJudgment(). readJudgmentLog() is used by the compounding step.
 */
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { sfRoot, sfRuntimeRoot } from "./paths.js";
import { getJudgmentsForUnit, insertJudgment, openDatabase } from "./sf-db.js";

const JUDGMENT_LOG_SCHEMA_VERSION = 1;

/**
 * Append a single judgment entry to the judgment log.
 * Prefers SQLite; falls back to JSONL when DB is unavailable.
 * Failure is non-fatal — silently swallowed so the agent loop is not disrupted.
 */
export function appendJudgment(basePath, entry) {
	const full = {
		schemaVersion: JUDGMENT_LOG_SCHEMA_VERSION,
		ts: new Date().toISOString(),
		...entry,
	};

	try {
		ensureJudgmentDb(basePath);
		insertJudgment(full);
		return;
	} catch {
		// Fall through to JSONL backup
	}

	// Fallback: JSONL file
	try {
		const logPath = resolveJudgmentLogPath(basePath);
		mkdirSync(join(logPath, ".."), { recursive: true });
		appendFileSync(logPath, JSON.stringify(full) + "\n", "utf-8");
	} catch {
		// Non-fatal — judgment logging must never break the agent loop
	}
}

/**
 * Read judgment log entries, optionally filtered by unitId prefix.
 *
 * @param basePath - project root
 * @param unitId - optional filter; when provided, only entries whose unitId
 *   starts with this value are returned (e.g. "M001" returns all M001 entries)
 */
export function readJudgmentLog(basePath, unitId) {
	try {
		ensureJudgmentDb(basePath);
		const rows = getJudgmentsForUnit(unitId ?? "", 1000);
		if (rows.length > 0) {
			return rows.map((r) => ({
				schemaVersion: JUDGMENT_LOG_SCHEMA_VERSION,
				...r,
				alternatives: r.alternatives,
			}));
		}
	} catch {
		// Fall through to JSONL backup
	}

	// Fallback: JSONL file
	const logPath = resolveJudgmentLogPath(basePath);
	if (!existsSync(logPath)) return [];
	try {
		const raw = readFileSync(logPath, "utf-8");
		const entries = [];
		for (const line of raw.split("\n")) {
			const trimmed = line.trim();
			if (!trimmed) continue;
			try {
				const parsed = normalizeJudgmentEntry(JSON.parse(trimmed));
				if (!parsed) continue;
				if (unitId && !parsed.unitId.startsWith(unitId)) continue;
				entries.push(parsed);
			} catch {
				// Skip malformed lines
			}
		}
		return entries;
	} catch {
		return [];
	}
}

function normalizeJudgmentEntry(entry) {
	if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
	const schemaVersion = entry.schemaVersion ?? JUDGMENT_LOG_SCHEMA_VERSION;
	if (schemaVersion !== JUDGMENT_LOG_SCHEMA_VERSION) return null;
	return {
		...entry,
		schemaVersion,
	};
}

/**
 * Resolve the absolute path to the judgment log file.
 */
export function resolveJudgmentLogPath(basePath) {
	return join(sfRuntimeRoot(basePath), "judgment-log.jsonl");
}

function ensureJudgmentDb(basePath) {
	const dir = sfRoot(basePath);
	mkdirSync(dir, { recursive: true });
	if (!openDatabase(join(dir, "sf.db"))) {
		throw new Error("SF judgment database unavailable");
	}
}
