import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { THREADWELL_CONFIG_DIR_NAME } from "../../config.js";
import { MemoryDedupeWindow } from "./dedupe.js";
import { summarizeMemoryEvent } from "./event-summary.js";
import { normalizeMemoryEventInput, validateMemoryEventInput } from "./memory-schema.js";
import { NoopMemoryStore } from "./memory-store.js";
import { redactMemoryEventInput } from "./redaction.js";
export class MemoryService {
    session;
    store;
    enabled;
    retention;
    dedupe = new MemoryDedupeWindow();
    initialized = false;
    constructor(options) {
        this.session = options.session;
        this.enabled = options.enabled ?? true;
        this.retention = {
            retentionDays: options.retention?.retentionDays ?? 90,
            maxEvents: options.retention?.maxEvents ?? 10000,
        };
        this.store = options.store ?? new NoopMemoryStore();
    }
    async initialize() {
        if (this.initialized)
            return;
        this.initialized = true;
        if (!this.enabled)
            return;
        await this.store.initialize(this.session);
    }
    async recordEvent(event) {
        if (!this.enabled)
            return undefined;
        const eventWithSummary = { ...event, summary: event.summary.trim() || summarizeMemoryEvent(event) };
        const normalizedEvent = normalizeMemoryEventInput(redactMemoryEventInput(eventWithSummary));
        if (!this.dedupe.shouldRecord(normalizedEvent))
            return undefined;
        const errors = validateMemoryEventInput(normalizedEvent);
        if (errors.length > 0) {
            throw new Error(`Invalid memory event: ${errors.join("; ")}`);
        }
        await this.initialize();
        const recorded = await this.store.recordEvent(this.session, normalizedEvent);
        await this.applyRetentionPolicy();
        return recorded;
    }
    async search(request) {
        if (!this.enabled)
            return [];
        await this.initialize();
        return this.store.search({ projectId: this.session.projectId, ...request });
    }
    async getRecent(limit = 10) {
        if (!this.enabled)
            return [];
        await this.initialize();
        return this.store.getRecent(this.session.projectId, limit);
    }
    async exportProject() {
        if (!this.enabled)
            return { path: "", eventCount: 0 };
        await this.initialize();
        const status = await this.store.getStatus();
        const events = await this.store.getRecent(this.session.projectId, Math.max(status.eventCount, 1));
        const exportDir = join(this.session.cwd, THREADWELL_CONFIG_DIR_NAME, "exports");
        await mkdir(exportDir, { recursive: true });
        const path = join(exportDir, `memory-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`);
        await writeFile(path, `${events.map((event) => JSON.stringify(event)).join("\n")}\n`, "utf-8");
        return { path, eventCount: events.length };
    }
    async forgetEvent(eventId) {
        if (!this.enabled)
            return false;
        await this.initialize();
        return this.store.deleteEvent(this.session.projectId, eventId);
    }
    async purgeProject() {
        if (!this.enabled)
            return 0;
        await this.initialize();
        return this.store.purgeProject(this.session.projectId);
    }
    async applyRetentionPolicy() {
        if (!this.enabled)
            return 0;
        await this.initialize();
        const status = await this.store.getStatus();
        const events = await this.store.getRecent(this.session.projectId, Math.max(status.eventCount, 1));
        const cutoff = Date.now() - this.retention.retentionDays * 24 * 60 * 60 * 1000;
        const idsToDelete = new Set();
        for (const event of events) {
            if (Date.parse(event.createdAt) < cutoff)
                idsToDelete.add(event.id);
        }
        for (const event of events.slice(this.retention.maxEvents)) {
            idsToDelete.add(event.id);
        }
        let deleted = 0;
        for (const id of idsToDelete) {
            if (await this.store.deleteEvent(this.session.projectId, id))
                deleted++;
        }
        return deleted;
    }
    async getStatus() {
        if (!this.enabled) {
            return {
                enabled: false,
                projectId: this.session.projectId,
                sessionId: this.session.sessionId,
                eventCount: 0,
                store: this.store.kind,
            };
        }
        await this.initialize();
        const status = await this.store.getStatus();
        return { ...status, enabled: true, projectId: this.session.projectId, sessionId: this.session.sessionId };
    }
    async close() {
        await this.store.close();
    }
}
//# sourceMappingURL=memory-service.js.map