import { randomUUID } from "node:crypto";
import { isRecord, readString } from "../internal/rowMappers.js";
/**
 * Append-only audit ledger repository. Backs {@link SqliteAuditSink}, mirroring
 * the permission-activity ledger pattern: prepared statements, insertion order
 * preserved via an autoincrement `seq`, and JSON-serialized metadata.
 */
export class AuditRepository {
    #db;
    constructor(db) {
        this.#db = db;
    }
    /**
     * Appends a structured audit event to the durable append-only ledger and
     * returns the stored event (with generated id/timestamp). The metadata object
     * is JSON-serialized.
     */
    appendAuditEvent(event) {
        const stored = {
            id: randomUUID(),
            at: new Date().toISOString(),
            type: event.type,
            severity: event.severity,
            message: event.message,
            ...(event.metadata === undefined ? {} : { metadata: event.metadata }),
        };
        this.#db
            .prepare(`INSERT INTO audit_events (id, type, severity, at, message, metadata_json)
         VALUES (?, ?, ?, ?, ?, ?)`)
            .run(stored.id, stored.type, stored.severity, stored.at, stored.message, event.metadata === undefined ? null : JSON.stringify(event.metadata));
        return stored;
    }
    /** Returns audit events in append (insertion) order. */
    listAuditEvents() {
        const rows = this.#db
            .prepare(`SELECT id, type, severity, at, message, metadata_json
         FROM audit_events
         ORDER BY seq ASC`)
            .all();
        return rows.filter(isRecord).map((row) => mapAuditEvent(row));
    }
    /**
     * Returns the most recent remote-command lifecycle audit events (type LIKE
     * 'command.%'), newest first, capped at `limit`. The bound keeps the read
     * surface from growing without limit, mirroring the permission-activity
     * projection's clampLimit pattern.
     */
    listCommandAuditEvents(limit) {
        const rows = this.#db
            .prepare(`SELECT id, type, severity, at, message, metadata_json
         FROM audit_events
         WHERE type LIKE 'command.%'
         ORDER BY seq DESC
         LIMIT ?`)
            .all(limit);
        return rows.filter(isRecord).map((row) => mapAuditEvent(row));
    }
}
function mapAuditEvent(row) {
    const base = {
        id: readString(row.id, "id"),
        type: readString(row.type, "type"),
        severity: readAuditSeverity(row.severity),
        at: readString(row.at, "at"),
        message: readString(row.message, "message"),
    };
    const metadata = parseAuditMetadata(row.metadata_json);
    return metadata === undefined ? base : { ...base, metadata };
}
function readAuditSeverity(value) {
    if (value === "info" || value === "warning" || value === "critical") {
        return value;
    }
    throw new Error("Expected a finite audit severity column.");
}
function parseAuditMetadata(value) {
    if (typeof value !== "string") {
        return undefined;
    }
    const parsed = JSON.parse(value);
    return isRecord(parsed) ? parsed : undefined;
}
