import { readdir } from "node:fs/promises";
import path from "node:path";
import { createId, ensureDir, normalizeId, nowIso, readJsonFile, withFileLock, writeJsonFile, writeTextFile } from "../../shared/index.js";
import { redactSeedText } from "../redaction/index.js";
const MEMORY_SCHEMA_VERSION = 1;
export function professorMemoryRoot(store) {
    return path.join(path.resolve(store.directory, ".seed-agent"), "memory");
}
export function professorMemoryIndexPath(store) {
    return path.join(professorMemoryRoot(store), "MEMORY.md");
}
export function professorMemoryItemsDir(store) {
    return path.join(professorMemoryRoot(store), "items");
}
export function professorMemoryItemPath(store, memoryId) {
    return path.join(professorMemoryItemsDir(store), `${normalizeId(memoryId)}.json`);
}
export async function listProfessorMemory(store, input = {}) {
    const dir = professorMemoryItemsDir(store);
    let names;
    try {
        names = await readdir(dir);
    }
    catch (error) {
        if (isNodeError(error) && error.code === "ENOENT")
            return [];
        throw error;
    }
    const items = [];
    for (const name of names.sort()) {
        if (!name.endsWith(".json"))
            continue;
        const raw = await readJsonFile(path.join(dir, name));
        if (!raw)
            continue;
        const item = normalizeProfessorMemoryItem(raw);
        if (input.reusableOnly && !item.reusable)
            continue;
        items.push(item);
    }
    return items.sort((left, right) => left.created_at.localeCompare(right.created_at));
}
export async function addProfessorMemory(store, input) {
    return withFileLock(professorMemoryIndexPath(store), async () => {
        await ensureDir(professorMemoryItemsDir(store));
        const existing = input.source_ref
            ? (await listProfessorMemory(store)).find((item) => item.source === input.source && item.source_ref === input.source_ref)
            : undefined;
        const timestamp = nowIso();
        const item = {
            schema_version: MEMORY_SCHEMA_VERSION,
            memory_id: existing?.memory_id ?? createId("prof_memory"),
            kind: input.kind,
            source: input.source,
            source_ref: cleanOptional(input.source_ref, 240),
            run_id: cleanOptional(input.run_id, 160),
            title: cleanMemoryText(input.title ?? defaultTitle(input), 160),
            detail: cleanOptional(input.detail, 2000),
            why: cleanMemoryText(input.why, 420),
            how_to_apply: cleanMemoryText(input.how_to_apply, 700),
            reusable: input.reusable ?? true,
            tags: [...new Set((input.tags ?? []).map((tag) => normalizeId(cleanMemoryText(tag, 64))).filter(Boolean))].slice(0, 12),
            created_at: existing?.created_at ?? timestamp,
            updated_at: timestamp,
        };
        await writeJsonFile(professorMemoryItemPath(store, item.memory_id), item);
        await writeProfessorMemoryIndex(store, [...(await listProfessorMemory(store)).filter((entry) => entry.memory_id !== item.memory_id), item]);
        return item;
    });
}
export async function writeProfessorMemoryIndex(store, items) {
    items ??= await listProfessorMemory(store);
    const reusable = items.filter((item) => item.reusable).sort((left, right) => right.updated_at.localeCompare(left.updated_at));
    const lines = [
        "# SEED Professor Memory",
        "",
        `Updated: ${nowIso()}`,
        "",
        "Durable professor preferences and feedback that should guide future Lab-Representative planning.",
        "",
        "## Items",
        "",
        ...(reusable.length
            ? reusable.map((item) => `- ${item.memory_id} [${item.kind}/${item.source}] ${item.title} (${item.updated_at})`)
            : ["- none"]),
        "",
    ];
    await writeTextFile(professorMemoryIndexPath(store), `${lines.join("\n")}`);
}
function normalizeProfessorMemoryItem(raw) {
    const timestamp = cleanMemoryText(raw.created_at ?? nowIso(), 80);
    return {
        schema_version: MEMORY_SCHEMA_VERSION,
        memory_id: cleanMemoryText(raw.memory_id ?? createId("prof_memory"), 120),
        kind: isMemoryKind(raw.kind) ? raw.kind : "project",
        source: isMemorySource(raw.source) ? raw.source : "manual",
        source_ref: cleanOptional(raw.source_ref, 240),
        run_id: cleanOptional(raw.run_id, 160),
        title: cleanMemoryText(raw.title ?? "Professor memory", 160),
        detail: cleanOptional(raw.detail, 2000),
        why: cleanMemoryText(raw.why ?? "The professor marked this as durable guidance.", 420),
        how_to_apply: cleanMemoryText(raw.how_to_apply ?? "Consider this guidance before planning or dispatching future work.", 700),
        reusable: raw.reusable ?? true,
        tags: Array.isArray(raw.tags) ? raw.tags.map((tag) => normalizeId(cleanMemoryText(String(tag), 64))).filter(Boolean).slice(0, 12) : [],
        created_at: timestamp,
        updated_at: cleanMemoryText(raw.updated_at ?? timestamp, 80),
    };
}
function defaultTitle(input) {
    if (input.source === "lab_meeting")
        return "Lab Meeting feedback";
    if (input.source === "intervention")
        return "Reusable professor intervention";
    return "Professor memory";
}
function cleanOptional(input, maxChars) {
    const cleaned = cleanMemoryText(input ?? "", maxChars);
    return cleaned || undefined;
}
export function cleanMemoryText(input, maxChars = 700) {
    const noCodeBlocks = input
        .replace(/```[\s\S]*?```/g, "[code omitted]")
        .replace(/^\s*(?:commit\s+[a-f0-9]{7,}|diff --git\b|index\s+[a-f0-9]{7,}|@@\s|Trace:|Events:|Agent calls:|Tool calls:).*$\n?/gim, "");
    return redactSeedText(noCodeBlocks).replace(/\s+/g, " ").trim().slice(0, maxChars);
}
function isMemoryKind(value) {
    return value === "user" || value === "feedback" || value === "project" || value === "reference";
}
function isMemorySource(value) {
    return value === "lab_meeting" || value === "intervention" || value === "manual" || value === "command";
}
function isNodeError(error) {
    return typeof error === "object" && error !== null && "code" in error;
}
//# sourceMappingURL=store.js.map