import { Buffer } from "node:buffer";
import { createHash } from "node:crypto";
import path from "node:path";
import { createId, appendJsonl, ensureDir, normalizeId, nowIso, readJsonFile, sanitizeJson, sanitizeText, withFileLock, writeJsonFile, writeTextFile, } from "../../shared/index.js";
import { runDir } from "../run-state/index.js";
import { SEED_CONTEXT_BUNDLE_SCHEMA_VERSION, SEED_CONTEXT_METRIC_SCHEMA_VERSION } from "./types.js";
export function contextRoot(store, runId) {
    return path.join(runDir(store, runId), "context");
}
export function contextBodiesRoot(store, runId) {
    return path.join(contextRoot(store, runId), "bodies");
}
export function contextBundlesPath(store, runId) {
    return path.join(contextRoot(store, runId), "bundles.json");
}
export function contextMetricsPath(store, runId) {
    return path.join(contextRoot(store, runId), "metrics.jsonl");
}
export function contextBundleBodyPath(store, runId, bodyRef) {
    return path.join(runDir(store, runId), bodyRef);
}
export async function upsertContextBundle(store, runId, input) {
    if (!store.config.context_ledger.enabled) {
        throw new Error("SEED context ledger is disabled by config.");
    }
    const body = sanitizeText(input.body);
    const byteLength = Buffer.byteLength(body, "utf8");
    if (byteLength > store.config.context_ledger.max_bundle_body_bytes) {
        throw new Error(`SEED context bundle body too large: ${byteLength} bytes > ${store.config.context_ledger.max_bundle_body_bytes}`);
    }
    const bundleId = input.bundle_id ? normalizeId(input.bundle_id) : createId("ctx");
    const bodyRef = path.join("context", "bodies", `${bundleId}.md`).replace(/\\/g, "/");
    const timestamp = nowIso();
    const digest = createDigest(body, store.config.context_ledger.max_digest_chars);
    const bodySha256 = `sha256:${createHash("sha256").update(body).digest("hex")}`;
    const bundlesFile = contextBundlesPath(store, runId);
    await ensureDir(contextBodiesRoot(store, runId));
    await writeTextFile(contextBundleBodyPath(store, runId, bodyRef), body);
    const saved = await withFileLock(bundlesFile, async () => {
        const bundles = await readContextBundleIndex(store, runId);
        const existing = bundles.find((bundle) => bundle.bundle_id === bundleId);
        const bundle = {
            schema_version: SEED_CONTEXT_BUNDLE_SCHEMA_VERSION,
            bundle_id: bundleId,
            category: input.category,
            scope: input.scope,
            title: sanitizeText(input.title),
            summary: input.summary ? sanitizeText(input.summary) : undefined,
            owner_agent: input.owner_agent,
            owner_thread_id: input.owner_thread_id,
            related_run_id: runId,
            related_message_id: input.related_message_id,
            related_subplan_id: input.related_subplan_id,
            related_artifact_path: input.related_artifact_path,
            body_ref: bodyRef,
            body_sha256: bodySha256,
            byte_length: byteLength,
            char_length: body.length,
            digest,
            created_at: existing?.created_at ?? timestamp,
            updated_at: timestamp,
            metadata: input.metadata ? sanitizeJson(input.metadata) : undefined,
        };
        const next = existing
            ? bundles.map((item) => item.bundle_id === bundleId ? bundle : item)
            : [...bundles, bundle];
        await writeJsonFile(bundlesFile, next.sort(compareBundles));
        return bundle;
    });
    if (store.config.context_ledger.metrics_enabled) {
        await appendJsonl(contextMetricsPath(store, runId), {
            schema_version: SEED_CONTEXT_METRIC_SCHEMA_VERSION,
            metric_id: createId("ctxmetric"),
            run_id: runId,
            created_at: nowIso(),
            event: "bundle_upserted",
            bundle_id: saved.bundle_id,
            category: saved.category,
            summary: `${saved.title}: ${saved.byte_length} byte(s)`,
        });
    }
    return saved;
}
export async function listContextBundles(store, runId, filter = {}) {
    const bundles = await readContextBundleIndex(store, runId);
    return bundles.filter((bundle) => {
        if (filter.category && bundle.category !== filter.category)
            return false;
        if (filter.scope && bundle.scope !== filter.scope)
            return false;
        if (filter.owner_agent && bundle.owner_agent !== filter.owner_agent)
            return false;
        if (filter.owner_thread_id && bundle.owner_thread_id !== filter.owner_thread_id)
            return false;
        return true;
    });
}
export async function getContextBundle(store, runId, bundleIdOrRef) {
    const bundleId = normalizeContextBundleId(bundleIdOrRef);
    return (await readContextBundleIndex(store, runId)).find((bundle) => bundle.bundle_id === bundleId);
}
export async function readContextBundleBody(store, runId, bundle) {
    const file = contextBundleBodyPath(store, runId, bundle.body_ref);
    return sanitizeText(await import("node:fs/promises").then(({ readFile }) => readFile(file, "utf8")));
}
export function normalizeContextBundleId(value) {
    return normalizeId(value.replace(/^context:/, ""));
}
function createDigest(body, maxChars) {
    const compacted = body.replace(/\s+/g, " ").trim();
    if (compacted.length <= maxChars)
        return compacted;
    return `${compacted.slice(0, Math.max(0, maxChars - 1)).trimEnd()}…`;
}
async function readContextBundleIndex(store, runId) {
    const raw = (await readJsonFile(contextBundlesPath(store, runId))) ?? [];
    return raw
        .filter((bundle) => Boolean(bundle.bundle_id && bundle.category && bundle.scope && bundle.body_ref))
        .map((bundle) => ({
        schema_version: SEED_CONTEXT_BUNDLE_SCHEMA_VERSION,
        bundle_id: String(bundle.bundle_id),
        category: bundle.category,
        scope: bundle.scope,
        title: bundle.title ?? String(bundle.bundle_id),
        summary: bundle.summary,
        owner_agent: bundle.owner_agent,
        owner_thread_id: bundle.owner_thread_id,
        related_run_id: bundle.related_run_id ?? runId,
        related_message_id: bundle.related_message_id,
        related_subplan_id: bundle.related_subplan_id,
        related_artifact_path: bundle.related_artifact_path,
        body_ref: bundle.body_ref,
        body_sha256: bundle.body_sha256 ?? "",
        byte_length: bundle.byte_length ?? 0,
        char_length: bundle.char_length ?? 0,
        digest: bundle.digest ?? "",
        created_at: bundle.created_at ?? nowIso(),
        updated_at: bundle.updated_at ?? nowIso(),
        metadata: bundle.metadata,
    }))
        .sort(compareBundles);
}
function compareBundles(left, right) {
    return left.created_at.localeCompare(right.created_at) || left.bundle_id.localeCompare(right.bundle_id);
}
//# sourceMappingURL=store.js.map