import { randomUUID } from "node:crypto";
import { auditCategoryForAction, } from "../core/activity-events.js";
import { cursorPage, normalizeCursorLimit } from "../core/pagination.js";
import { createDocumentStore } from "../state/state-backend.js";
export class AuditLogStore {
    store;
    maxEvents;
    constructor(workspace, backend = "json", maxEvents = 1000) {
        this.store = createDocumentStore({
            workspace,
            fileName: "audit.json",
            sqliteKey: "audit",
            backend,
        });
        this.maxEvents = maxEvents;
    }
    append(event) {
        const next = {
            id: randomUUID().replace(/-/g, "").slice(0, 12),
            timestamp: new Date().toISOString(),
            ...event,
            category: event.category ?? auditCategoryForAction(event.action),
            channelId: event.channelId ?? "telegram",
        };
        this.store.update((current) => {
            const payload = this.normalizePayload(current);
            payload.events.push(next);
            if (payload.events.length > this.maxEvents) {
                payload.events.splice(0, payload.events.length - this.maxEvents);
            }
            return payload;
        });
        return next;
    }
    list(options = 20) {
        const resolved = typeof options === "number" ? { limit: options } : options;
        return this.listPage(resolved).items;
    }
    listPage(options = {}) {
        const limit = normalizeCursorLimit(options.limit, 20, 500);
        const events = this.filteredEvents(options)
            .map((event, index) => ({ event, index }))
            .sort((left, right) => {
            const timestampDiff = Date.parse(right.event.timestamp) - Date.parse(left.event.timestamp);
            return timestampDiff || right.index - left.index;
        })
            .map((entry) => entry.event);
        return cursorPage(events, options.cursor, limit, (event) => event.id);
    }
    findByCorrelationId(correlationId, limit = 100) {
        const needle = correlationId.trim();
        if (!needle) {
            return [];
        }
        return this.readPayload().events
            .filter((event) => event.correlationId === needle)
            .sort((left, right) => Date.parse(left.timestamp) - Date.parse(right.timestamp))
            .slice(-Math.max(1, Math.min(500, limit)));
    }
    filteredEvents(options) {
        const since = normalizeSince(options.since);
        return this.readPayload().events
            .filter((event) => !options.channelId || options.channelId === "all" || event.channelId === options.channelId)
            .filter((event) => !options.status || options.status === "all" || event.status === options.status)
            .filter((event) => !options.action || options.action === "all" || event.action === options.action)
            .filter((event) => !options.category || options.category === "all" || (event.category ?? auditCategoryForAction(event.action)) === options.category)
            .filter((event) => !options.agentId || options.agentId === "all" || event.agentId === options.agentId)
            .filter((event) => !options.threadId || event.threadId === options.threadId)
            .filter((event) => !options.workspace || event.workspace === options.workspace)
            .filter((event) => !options.actor || auditActorMatches(event, options.actor))
            .filter((event) => !since || Date.parse(event.timestamp) >= since);
    }
    readPayload() {
        return this.normalizePayload(this.store.read());
    }
    normalizePayload(payload) {
        if (!payload || payload.version !== 1 || !Array.isArray(payload.events)) {
            return { version: 1, events: [] };
        }
        return {
            version: 1,
            events: payload.events.filter(isAuditEvent),
        };
    }
}
function normalizeSince(value) {
    if (value === undefined || value === null || value === "") {
        return null;
    }
    const time = typeof value === "number" ? value : Date.parse(value);
    return Number.isFinite(time) ? time : null;
}
function auditActorMatches(event, query) {
    const needle = query.trim().toLowerCase();
    if (!needle) {
        return true;
    }
    return [
        event.actor?.id,
        event.actor?.label,
        event.actor?.username,
        event.actor?.channelUserId,
        event.actor?.channel,
        event.actorId,
        event.actorRole,
    ].some((value) => String(value ?? "").toLowerCase().includes(needle));
}
function isAuditEvent(value) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return false;
    }
    const candidate = value;
    return typeof candidate.id === "string" &&
        typeof candidate.timestamp === "string" &&
        typeof candidate.action === "string" &&
        typeof candidate.status === "string" &&
        typeof candidate.contextKey === "string";
}
