import path from "node:path";
import { appendEvent } from "../observability/index.js";
import { runDir } from "../run-state/index.js";
import { ensureDir, nowIso, readJsonFile, sanitizeJson, sanitizeText, withFileLock, writeJsonFile } from "../../shared/index.js";
export function idempotencyRoot(store, runId) {
    return path.join(runDir(store, runId), "idempotency");
}
export function idempotencyRecordsPath(store, runId) {
    return path.join(idempotencyRoot(store, runId), "records.json");
}
export async function listIdempotencyRecords(store, runId) {
    await ensureIdempotencyStore(store, runId);
    return ((await readJsonFile(idempotencyRecordsPath(store, runId))) ?? []).map(normalizeRecord);
}
export async function claimIdempotencyKey(store, runId, input) {
    await ensureIdempotencyStore(store, runId);
    const file = idempotencyRecordsPath(store, runId);
    const claim = await withFileLock(file, async () => {
        const records = ((await readJsonFile(file)) ?? []).map(normalizeRecord);
        const key = normalizeKey(input.key);
        const existing = records.find((record) => record.key === key);
        if (existing)
            return { claimed: false, record: existing };
        const timestamp = nowIso();
        const next = {
            schema_version: 1,
            key,
            run_id: runId,
            scope: sanitizeText(input.scope),
            action: sanitizeText(input.action),
            actor: sanitizeText(input.actor),
            status: "claimed",
            created_at: timestamp,
            updated_at: timestamp,
            metadata: sanitizeJson(input.metadata ?? {}),
        };
        records.push(next);
        await writeJsonFile(file, records);
        return { claimed: true, record: next };
    });
    if (claim.claimed) {
        await appendEvent(store, runId, {
            type: "idempotency_key_claimed",
            actor: claim.record.actor,
            summary: `${claim.record.scope}:${claim.record.action}:${claim.record.key}`,
            metadata: { key: claim.record.key, scope: claim.record.scope, action: claim.record.action },
        });
    }
    return claim;
}
export async function completeIdempotencyKey(store, runId, key, input = {}) {
    await ensureIdempotencyStore(store, runId);
    const file = idempotencyRecordsPath(store, runId);
    return withFileLock(file, async () => {
        const records = ((await readJsonFile(file)) ?? []).map(normalizeRecord);
        const normalizedKey = normalizeKey(key);
        const index = records.findIndex((record) => record.key === normalizedKey);
        if (index < 0)
            throw new Error(`idempotency key not found: ${normalizedKey}`);
        const timestamp = nowIso();
        const current = records[index];
        const next = {
            ...current,
            status: input.status ?? "completed",
            updated_at: timestamp,
            completed_at: timestamp,
            result_ref: input.result_ref ?? current.result_ref,
            metadata: sanitizeJson({ ...current.metadata, ...(input.metadata ?? {}) }),
        };
        records[index] = next;
        await writeJsonFile(file, records);
        return next;
    });
}
export function stableIdempotencyKey(parts) {
    return parts.map((part) => sanitizeText(part ?? "").trim().toLowerCase()).filter(Boolean).join(":").replace(/[^a-z0-9_.:-]+/g, "-").slice(0, 240);
}
async function ensureIdempotencyStore(store, runId) {
    await ensureDir(idempotencyRoot(store, runId));
    const file = idempotencyRecordsPath(store, runId);
    const existing = await readJsonFile(file);
    if (!Array.isArray(existing))
        await writeJsonFile(file, []);
}
function normalizeRecord(record) {
    return {
        ...record,
        schema_version: 1,
        key: normalizeKey(record.key),
        status: record.status ?? "claimed",
        metadata: record.metadata ?? {},
    };
}
function normalizeKey(key) {
    const normalized = stableIdempotencyKey([key]);
    if (!normalized)
        throw new Error("idempotency key is required");
    return normalized;
}
//# sourceMappingURL=store.js.map