/**
 * Multi-Tenant Memory Core
 * Enhanced memory core with complete tenant isolation and per-user operations
 */
import { MemoryCore } from './memory-core.js';
import { MemoryBuffer } from './buffer.js';
import { AsyncMemoryWriter } from './writer.js';
import { MemoryType, ImportanceLevel, MCPToolResultStatus } from '../types/enums.js';
export class UserBufferManager {
    buffers = new Map();
    locks = new Map();
    async getBuffer(userId) {
        if (!this.buffers.has(userId)) {
            this.buffers.set(userId, {
                userId,
                maxSize: 100,
                flushInterval: 60000, // 60 seconds
                buffer: [],
                lastFlush: new Date(),
            });
        }
        return this.buffers.get(userId);
    }
    async addToBuffer(userId, operation) {
        const buffer = await this.getBuffer(userId);
        buffer.buffer.push(operation);
        // Return true if buffer should be flushed
        return buffer.buffer.length >= buffer.maxSize;
    }
    async getAndClearBuffer(userId) {
        const buffer = await this.getBuffer(userId);
        const operations = [...buffer.buffer];
        buffer.buffer = [];
        buffer.lastFlush = new Date();
        return operations;
    }
    shouldFlush(userId) {
        const buffer = this.buffers.get(userId);
        if (!buffer)
            return false;
        const timeSinceFlush = Date.now() - buffer.lastFlush.getTime();
        return timeSinceFlush >= buffer.flushInterval;
    }
    async removeBuffer(userId) {
        this.buffers.delete(userId);
        this.locks.delete(userId);
    }
    async flushAll() {
        const results = [];
        for (const [userId, buffer] of this.buffers.entries()) {
            if (buffer.buffer.length > 0) {
                const operations = await this.getAndClearBuffer(userId);
                results.push({ userId, operations });
            }
        }
        return results;
    }
}
export class MultiTenantMemoryCore extends MemoryCore {
    bufferManager;
    memoryBuffer;
    memoryWriter;
    userQuotas = new Map();
    userUsage = new Map();
    rateLimits = new Map();
    // Default quotas
    defaultQuota = {
        maxMemories: 10000,
        maxEntities: 1000,
        maxInteractions: 50000,
        maxStorageBytes: 100 * 1024 * 1024, // 100MB
        dailyApiCalls: 10000,
    };
    constructor(db, openaiApiKey) {
        super(db, openaiApiKey);
        this.bufferManager = new UserBufferManager();
        this.memoryBuffer = new MemoryBuffer();
        this.memoryWriter = new AsyncMemoryWriter(this.memoryBuffer, this);
    }
    /**
     * Initialize the multi-tenant memory core
     */
    async initialize() {
        await super.initialize();
        await this.memoryBuffer.restore();
        await this.memoryWriter.start();
        console.info('Multi-tenant memory core initialized');
    }
    /**
     * Close the multi-tenant memory core
     */
    async close() {
        await this.memoryWriter.stop();
        await this.memoryBuffer.close();
        await super.close();
        console.info('Multi-tenant memory core closed');
    }
    /**
     * Set quota for a user
     */
    async setUserQuota(userId, quota) {
        const currentQuota = this.userQuotas.get(userId) || { ...this.defaultQuota };
        this.userQuotas.set(userId, { ...currentQuota, ...quota });
    }
    /**
     * Get quota for a user
     */
    async getUserQuota(userId) {
        return this.userQuotas.get(userId) || { ...this.defaultQuota };
    }
    /**
     * Check if user is within quota limits
     */
    async checkQuota(userId, operation) {
        const quota = await this.getUserQuota(userId);
        const usage = await this.getUserUsage(userId);
        // Check daily API calls
        if (usage.dailyApiCalls >= quota.dailyApiCalls) {
            return { allowed: false, reason: 'Daily API call limit exceeded' };
        }
        // Check specific operation limits
        switch (operation) {
            case 'add_memory':
                if (usage.memoriesCount >= quota.maxMemories) {
                    return { allowed: false, reason: 'Memory limit exceeded' };
                }
                break;
            case 'create_entity':
                if (usage.entitiesCount >= quota.maxEntities) {
                    return { allowed: false, reason: 'Entity limit exceeded' };
                }
                break;
            case 'add_interaction':
                if (usage.interactionsCount >= quota.maxInteractions) {
                    return { allowed: false, reason: 'Interaction limit exceeded' };
                }
                break;
        }
        // Check storage limit
        if (usage.storageBytes >= quota.maxStorageBytes) {
            return { allowed: false, reason: 'Storage limit exceeded' };
        }
        return { allowed: true };
    }
    /**
     * Get current usage for a user
     */
    async getUserUsage(userId) {
        let usage = this.userUsage.get(userId);
        if (!usage) {
            // Calculate usage from database
            usage = await this.calculateUserUsage(userId);
            this.userUsage.set(userId, usage);
        }
        return usage;
    }
    async calculateUserUsage(userId) {
        // Get counts from database
        const memoriesResult = await this.db.execute('SELECT COUNT(*) as count, COALESCE(SUM(LENGTH(content)), 0) as bytes FROM memories WHERE user_id = ?', [userId]);
        const entitiesResult = await this.db.execute('SELECT COUNT(*) as count FROM entities WHERE user_id = ?', [userId]);
        const interactionsResult = await this.db.execute('SELECT COUNT(*) as count FROM interactions WHERE user_id = ?', [userId]);
        // Get daily API calls (reset at midnight)
        const today = new Date().toISOString().split('T')[0];
        const apiCallsResult = await this.db.execute('SELECT COUNT(*) as count FROM interactions WHERE user_id = ? AND DATE(created_at) = ?', [userId, today]);
        const memoriesRow = memoriesResult.rows[0];
        const entitiesRow = entitiesResult.rows[0];
        const interactionsRow = interactionsResult.rows[0];
        const apiCallsRow = apiCallsResult.rows[0];
        return {
            memoriesCount: Number(memoriesRow?.count || 0),
            entitiesCount: Number(entitiesRow?.count || 0),
            interactionsCount: Number(interactionsRow?.count || 0),
            storageBytes: Number(memoriesRow?.bytes || 0),
            dailyApiCalls: Number(apiCallsRow?.count || 0),
            lastApiCall: new Date(),
        };
    }
    /**
     * Update usage after an operation
     */
    async updateUsage(userId, operation, delta = 1) {
        const usage = await this.getUserUsage(userId);
        switch (operation) {
            case 'add_memory':
                usage.memoriesCount += delta;
                break;
            case 'create_entity':
                usage.entitiesCount += delta;
                break;
            case 'add_interaction':
                usage.interactionsCount += delta;
                break;
        }
        usage.dailyApiCalls += 1;
        usage.lastApiCall = new Date();
        this.userUsage.set(userId, usage);
    }
    /**
     * Add memory with quota checking and buffering
     */
    async addMemory(title, content, memoryType = MemoryType.MEMORY, options = {}) {
        const userId = this.getUserId(options.userId);
        // Check quota
        const quotaCheck = await this.checkQuota(userId, 'add_memory');
        if (!quotaCheck.allowed) {
            return {
                status: MCPToolResultStatus.ERROR,
                message: 'Quota exceeded',
                error: quotaCheck.reason,
            };
        }
        // If buffering is enabled, add to buffer
        if (options.useBuffer !== false) {
            const memoryData = {
                title,
                content,
                memoryType,
                userId,
                importance: options.importance || ImportanceLevel.MEDIUM,
                tags: options.tags,
                entityIds: options.entityIds,
                generateEmbedding: options.generateEmbedding !== false,
            };
            try {
                const memoryId = await this.memoryBuffer.add(memoryData);
                await this.updateUsage(userId, 'add_memory');
                return {
                    status: MCPToolResultStatus.SUCCESS,
                    message: 'Memory queued for processing',
                    data: { id: memoryId, buffered: true },
                };
            }
            catch (error) {
                return {
                    status: MCPToolResultStatus.ERROR,
                    message: 'Failed to queue memory',
                    error: String(error),
                };
            }
        }
        // Direct write (bypass buffer)
        const result = await super.addMemory(title, content, memoryType, options);
        if (result.status === MCPToolResultStatus.SUCCESS) {
            await this.updateUsage(userId, 'add_memory');
        }
        return result;
    }
    /**
     * Get tenant statistics
     */
    async getTenantStats(userId) {
        const usage = await this.getUserUsage(userId);
        const quota = await this.getUserQuota(userId);
        const bufferStats = await this.memoryBuffer.getStatus();
        const isOverQuota = usage.memoriesCount >= quota.maxMemories ||
            usage.entitiesCount >= quota.maxEntities ||
            usage.interactionsCount >= quota.maxInteractions ||
            usage.storageBytes >= quota.maxStorageBytes ||
            usage.dailyApiCalls >= quota.dailyApiCalls;
        return {
            userId,
            usage,
            quota,
            isOverQuota,
            bufferStats,
        };
    }
    /**
     * Get buffer status for a user
     */
    async getBufferStatus() {
        return await this.memoryBuffer.getStatus();
    }
    /**
     * Get writer statistics
     */
    async getWriterStats() {
        return await this.memoryWriter.getStats();
    }
}
//# sourceMappingURL=multi-tenant-memory-core.js.map