import path from "node:path";
import fs from "node:fs/promises";
import os from "node:os";
import crypto from "node:crypto";
//#region src/klaw/learnings-log.ts
function resolvePaths() {
	const workspace = process.env.KLAW_WORKSPACE_DIR ?? path.join(os.homedir(), ".klaw", "workspace");
	const dir = path.join(workspace, ".learnings");
	return {
		dir,
		seen: path.join(dir, ".seen.json"),
		errors: path.join(dir, "ERRORS.md"),
		learnings: path.join(dir, "LEARNINGS.md"),
		features: path.join(dir, "FEATURE_REQUESTS.md")
	};
}
const DEDUP_WINDOW_MS = 360 * 60 * 1e3;
function headerForFile(file, p) {
	if (file === p.errors) return "# Errors\n\nCommand failures and integration errors.\n\n---\n";
	if (file === p.learnings) return "# Learnings\n\nCorrections, insights, and knowledge gaps captured during development.\n\n**Categories**: correction | insight | knowledge_gap | best_practice\n\n---\n";
	if (file === p.features) return "# Feature Requests\n\nCapabilities requested by the user.\n\n---\n";
	return "";
}
function pad2(n) {
	return n < 10 ? `0${n}` : String(n);
}
function dateStamp(d) {
	return `${d.getFullYear()}${pad2(d.getMonth() + 1)}${pad2(d.getDate())}`;
}
function makeId(type, d) {
	const rand = crypto.randomBytes(2).toString("hex").toUpperCase().slice(0, 3);
	return `${type}-${dateStamp(d)}-${rand}`;
}
function signature(parts) {
	const joined = parts.map((p) => (p ?? "").slice(0, 120)).join("|");
	return crypto.createHash("sha1").update(joined).digest("hex").slice(0, 16);
}
async function readSeen(p) {
	try {
		const raw = await fs.readFile(p.seen, "utf8");
		const parsed = JSON.parse(raw);
		return parsed && typeof parsed === "object" ? parsed : {};
	} catch {
		return {};
	}
}
async function writeSeen(p, seen) {
	const tmp = `${p.seen}.tmp`;
	await fs.writeFile(tmp, JSON.stringify(seen));
	await fs.rename(tmp, p.seen);
}
/**
* Returns true if this signature was logged within the dedup window. Records
* the timestamp for future calls and prunes expired entries.
*/
async function isDuplicate(p, sig, now) {
	const seen = await readSeen(p);
	const last = seen[sig];
	if (typeof last === "number" && now - last < DEDUP_WINDOW_MS) return true;
	const pruned = { [sig]: now };
	for (const [k, v] of Object.entries(seen)) if (typeof v === "number" && now - v < DEDUP_WINDOW_MS) pruned[k] = v;
	await fs.mkdir(p.dir, { recursive: true });
	await writeSeen(p, pruned);
	return false;
}
async function ensureFile(p, file) {
	await fs.mkdir(p.dir, { recursive: true });
	try {
		await fs.access(file);
	} catch {
		await fs.writeFile(file, headerForFile(file, p));
	}
}
async function appendEntry(p, file, block) {
	await ensureFile(p, file);
	await fs.appendFile(file, block);
}
function metaLine(label, value) {
	return value ? `- ${label}: ${value}\n` : "";
}
/**
* Append a structured ERR- entry to .learnings/ERRORS.md. Deduped within a
* 6-hour window. Best-effort — never throws.
*/
async function logErrorEntry(input) {
	try {
		const p = resolvePaths();
		const now = Date.now();
		if (await isDuplicate(p, signature([
			"ERR",
			input.skillOrCommand,
			input.summary,
			input.errorText
		]), now)) return;
		const d = new Date(now);
		const id = makeId("ERR", d);
		const priority = input.priority ?? "high";
		const block = `\n## [${id}] ${input.skillOrCommand}\n\n**Logged**: ${d.toISOString()}\n**Priority**: ${priority}\n**Status**: pending\n` + (input.area ? `**Area**: ${input.area}\n` : "") + `\n### Summary\n${input.summary}\n` + (input.errorText ? `\n### Error\n\`\`\`\n${input.errorText.slice(0, 1e3)}\n\`\`\`\n` : "") + (input.context ? `\n### Context\n${input.context}\n` : "") + (input.suggestedFix ? `\n### Suggested Fix\n${input.suggestedFix}\n` : "") + `\n### Metadata\n` + metaLine("Reproducible", input.reproducible ?? "unknown") + metaLine("Related Files", input.relatedFiles?.join(", ")) + metaLine("Source", "auto:reply-reflection") + `\n---\n`;
		await appendEntry(p, p.errors, block);
	} catch {}
}
/** Append a structured LRN- entry to .learnings/LEARNINGS.md. Best-effort. */
async function logLearningEntry(input) {
	try {
		const p = resolvePaths();
		const now = Date.now();
		if (await isDuplicate(p, signature([
			"LRN",
			input.category,
			input.summary
		]), now)) return;
		const d = new Date(now);
		const block = `\n## [${makeId("LRN", d)}] ${input.category}\n\n**Logged**: ${d.toISOString()}\n**Priority**: ${input.priority ?? "medium"}\n**Status**: pending\n` + (input.area ? `**Area**: ${input.area}\n` : "") + `\n### Summary\n${input.summary}\n` + (input.details ? `\n### Details\n${input.details}\n` : "") + (input.suggestedAction ? `\n### Suggested Action\n${input.suggestedAction}\n` : "") + `\n### Metadata\n` + metaLine("Source", input.source ?? "conversation") + metaLine("Related Files", input.relatedFiles?.join(", ")) + metaLine("Tags", input.tags?.join(", ")) + `\n---\n`;
		await appendEntry(p, p.learnings, block);
	} catch {}
}
/** Append a structured FEAT- entry to .learnings/FEATURE_REQUESTS.md. Best-effort. */
async function logFeatureRequestEntry(input) {
	try {
		const p = resolvePaths();
		const now = Date.now();
		if (await isDuplicate(p, signature(["FEAT", input.capability]), now)) return;
		const d = new Date(now);
		const block = `\n## [${makeId("FEAT", d)}] ${input.capability}\n\n**Logged**: ${d.toISOString()}\n**Priority**: ${input.priority ?? "medium"}\n**Status**: pending\n\n### Requested Capability\n${input.capability}\n` + (input.userContext ? `\n### User Context\n${input.userContext}\n` : "") + (input.complexity ? `\n### Complexity Estimate\n${input.complexity}\n` : "") + (input.suggestedImplementation ? `\n### Suggested Implementation\n${input.suggestedImplementation}\n` : "") + `\n### Metadata\n` + metaLine("Frequency", input.frequency ?? "first_time") + `\n---\n`;
		await appendEntry(p, p.features, block);
	} catch {}
}
/** Test helper: resolves the paths currently in use, for assertions. */
function __learningsPaths() {
	return resolvePaths();
}
//#endregion
export { __learningsPaths, logErrorEntry, logFeatureRequestEntry, logLearningEntry };
