"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventStore = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const readline = __importStar(require("readline"));
const fs_1 = require("fs");
/**
 * EventStore
 *
 * Minimal append-only event log:
 * - Persists to .mycontext/event-store.jsonl (JSON Lines format)
 * - Simple querying (type, time range, agent)
 * - Event replay capability
 * - No complex projections yet
 */
class EventStore {
    constructor(baseDir) {
        const storeDir = path.join(baseDir || process.cwd(), ".mycontext");
        this.storePath = path.join(storeDir, "event-store.jsonl");
        this.eventCount = 0;
    }
    /**
     * Initialize event store
     */
    async init() {
        // Ensure directory exists
        await fs.ensureDir(path.dirname(this.storePath));
        // Create file if doesn't exist
        if (!(await fs.pathExists(this.storePath))) {
            await fs.writeFile(this.storePath, "");
        }
        // Open write stream for appending
        this.writeStream = fs.createWriteStream(this.storePath, { flags: "a" });
        // Count existing events
        this.eventCount = await this.countEvents();
    }
    /**
     * Append event to store
     */
    async append(eventType, payload, options) {
        if (!this.writeStream) {
            throw new Error("EventStore not initialized. Call init() first.");
        }
        const event = {
            eventId: `evt-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
            eventType,
            timestamp: new Date().toISOString(),
            agentType: options?.agentType,
            instanceId: options?.instanceId,
            payload,
            metadata: options?.metadata,
        };
        // Write as JSON Line (one event per line)
        const line = JSON.stringify(event) + "\n";
        this.writeStream.write(line);
        this.eventCount++;
        return event;
    }
    /**
     * Append batch of events
     */
    async appendBatch(events) {
        const appendedEvents = [];
        for (const event of events) {
            const appended = await this.append(event.eventType, event.payload, {
                agentType: event.agentType,
                instanceId: event.instanceId,
                metadata: event.metadata,
            });
            appendedEvents.push(appended);
        }
        return appendedEvents;
    }
    /**
     * Query events
     */
    async query(filter) {
        const events = [];
        let count = 0;
        const offset = filter?.offset || 0;
        const limit = filter?.limit || 1000;
        await this.readEvents((event) => {
            // Apply filters
            if (filter?.eventTypes && !filter.eventTypes.includes(event.eventType)) {
                return true; // Continue
            }
            if (filter?.agentTypes && event.agentType && !filter.agentTypes.includes(event.agentType)) {
                return true;
            }
            if (filter?.instanceIds && event.instanceId && !filter.instanceIds.includes(event.instanceId)) {
                return true;
            }
            if (filter?.startTime && event.timestamp < filter.startTime) {
                return true;
            }
            if (filter?.endTime && event.timestamp > filter.endTime) {
                return true;
            }
            // Apply offset and limit
            if (count >= offset && events.length < limit) {
                events.push(event);
            }
            count++;
            // Stop if limit reached
            return events.length < limit;
        });
        return events;
    }
    /**
     * Get recent events
     */
    async getRecent(limit = 100) {
        const allEvents = [];
        await this.readEvents((event) => {
            allEvents.push(event);
            return true;
        });
        // Return last N events
        return allEvents.slice(-limit);
    }
    /**
     * Get events by type
     */
    async getByType(eventType, limit) {
        return this.query({
            eventTypes: [eventType],
            limit,
        });
    }
    /**
     * Get events by agent
     */
    async getByAgent(agentType, limit) {
        return this.query({
            agentTypes: [agentType],
            limit,
        });
    }
    /**
     * Get events by instance
     */
    async getByInstance(instanceId) {
        return this.query({
            instanceIds: [instanceId],
        });
    }
    /**
     * Get events in time range
     */
    async getByTimeRange(startTime, endTime, limit) {
        return this.query({
            startTime,
            endTime,
            limit,
        });
    }
    /**
     * Replay events (useful for debugging and reconstruction)
     */
    async replay(callback, filter) {
        const events = await this.query(filter);
        for (const event of events) {
            await callback(event);
        }
        return events.length;
    }
    /**
     * Get statistics
     */
    async getStats() {
        const eventsByType = {};
        let oldestEvent;
        let newestEvent;
        await this.readEvents((event) => {
            // Count by type
            eventsByType[event.eventType] = (eventsByType[event.eventType] || 0) + 1;
            // Track oldest/newest
            if (!oldestEvent || event.timestamp < oldestEvent) {
                oldestEvent = event.timestamp;
            }
            if (!newestEvent || event.timestamp > newestEvent) {
                newestEvent = event.timestamp;
            }
            return true;
        });
        // Get file size
        let storeSizeBytes = 0;
        if (await fs.pathExists(this.storePath)) {
            const stats = await fs.stat(this.storePath);
            storeSizeBytes = stats.size;
        }
        return {
            totalEvents: this.eventCount,
            eventsByType,
            oldestEvent,
            newestEvent,
            storeSizeBytes,
        };
    }
    /**
     * Clear all events (use with caution!)
     */
    async clear() {
        if (this.writeStream) {
            this.writeStream.close();
        }
        await fs.writeFile(this.storePath, "");
        // Reopen stream
        this.writeStream = fs.createWriteStream(this.storePath, { flags: "a" });
        this.eventCount = 0;
    }
    /**
     * Compact store (remove old events, keep recent N)
     */
    async compact(keepRecent = 10000) {
        // Read all events
        const allEvents = [];
        await this.readEvents((event) => {
            allEvents.push(event);
            return true;
        });
        // Keep only recent N
        const eventsToKeep = allEvents.slice(-keepRecent);
        const removedCount = allEvents.length - eventsToKeep.length;
        if (removedCount === 0) {
            return 0;
        }
        // Close write stream
        if (this.writeStream) {
            this.writeStream.close();
        }
        // Rewrite file with compacted events
        const lines = eventsToKeep.map((event) => JSON.stringify(event)).join("\n");
        await fs.writeFile(this.storePath, lines + (lines ? "\n" : ""));
        // Reopen stream
        this.writeStream = fs.createWriteStream(this.storePath, { flags: "a" });
        this.eventCount = eventsToKeep.length;
        return removedCount;
    }
    /**
     * Export events to JSON array
     */
    async export(outputPath, filter) {
        const events = await this.query(filter);
        await fs.writeJson(outputPath, events, { spaces: 2 });
        return events.length;
    }
    /**
     * Import events from JSON array
     */
    async import(inputPath) {
        const events = await fs.readJson(inputPath);
        if (!Array.isArray(events)) {
            throw new Error("Import file must contain an array of events");
        }
        let imported = 0;
        for (const event of events) {
            await this.append(event.eventType, event.payload, {
                agentType: event.agentType,
                instanceId: event.instanceId,
                metadata: event.metadata,
            });
            imported++;
        }
        return imported;
    }
    /**
     * Close event store
     */
    async close() {
        if (this.writeStream) {
            this.writeStream.close();
            this.writeStream = undefined;
        }
    }
    /**
     * Read events from file (streaming)
     */
    async readEvents(callback) {
        if (!(await fs.pathExists(this.storePath))) {
            return;
        }
        const fileStream = (0, fs_1.createReadStream)(this.storePath);
        const rl = readline.createInterface({
            input: fileStream,
            crlfDelay: Infinity,
        });
        for await (const line of rl) {
            if (!line.trim())
                continue;
            try {
                const event = JSON.parse(line);
                const shouldContinue = await callback(event);
                if (!shouldContinue) {
                    break;
                }
            }
            catch (error) {
                console.error("Failed to parse event line:", error);
                // Continue with next line
            }
        }
        fileStream.close();
    }
    /**
     * Count total events in store
     */
    async countEvents() {
        let count = 0;
        await this.readEvents(() => {
            count++;
            return true;
        });
        return count;
    }
}
exports.EventStore = EventStore;
//# sourceMappingURL=EventStore.js.map