import { promises as fs } from 'fs';
import path from 'path';
import { randomUUID } from 'crypto';
export class MemoryManager {
    configManager;
    memoryPath;
    indexPath;
    memories;
    semanticIndex;
    memoryGraph;
    constructor(configManager) {
        this.configManager = configManager;
        this.memoryPath = '';
        this.indexPath = '';
        this.memories = new Map();
        this.semanticIndex = {
            entries: new Map(),
            lastUpdated: new Date().toISOString(),
        };
        this.memoryGraph = {
            nodes: new Map(),
            edges: new Map(),
        };
    }
    async init() {
        return this.initialize();
    }
    async initialize() {
        const storageManager = this.configManager.getStorageManager();
        const location = await storageManager.getStorageLocation();
        this.memoryPath = path.join(location.data, 'memory', 'entries.json');
        this.indexPath = path.join(location.data, 'memory', 'index.json');
        // Ensure memory directory exists
        await fs.mkdir(path.dirname(this.memoryPath), { recursive: true });
        // Load existing memories
        await this.loadMemories();
        await this.loadIndex();
    }
    async store(entry) {
        const id = randomUUID();
        const memoryEntry = {
            ...entry,
            id,
            timestamp: new Date().toISOString(),
        };
        // Store in memory
        this.memories.set(id, memoryEntry);
        // Generate embedding for semantic search
        await this.generateEmbedding(memoryEntry);
        // Update graph relationships
        await this.updateGraph(memoryEntry);
        // Save to disk
        await this.saveMemories();
        await this.saveIndex();
        return id;
    }
    async search(query) {
        const results = [];
        // Simple text-based search (can be enhanced with embeddings)
        for (const [id, memory] of this.memories) {
            let score = 0;
            // Calculate relevance score
            score += this.calculateTextRelevance(query.query, memory.content);
            // Type filter
            if (query.type && memory.type !== query.type) {
                continue;
            }
            // Tags filter
            if (query.tags && query.tags.length > 0) {
                const tagMatch = query.tags.some(tag => memory.tags.includes(tag));
                if (!tagMatch)
                    continue;
                score += 0.2; // Bonus for tag match
            }
            // Importance boost
            const importanceBoost = {
                critical: 0.4,
                high: 0.3,
                medium: 0.2,
                low: 0.1,
            };
            score += importanceBoost[memory.importance];
            // Recency boost (more recent = higher score)
            const ageInDays = (Date.now() - new Date(memory.timestamp).getTime()) / (1000 * 60 * 60 * 24);
            const recencyBoost = Math.max(0, 0.2 - (ageInDays * 0.01));
            score += recencyBoost;
            if (score > (query.minScore || 0.1)) {
                results.push({
                    id,
                    content: memory.content,
                    type: memory.type,
                    tags: memory.tags,
                    importance: memory.importance,
                    timestamp: memory.timestamp,
                    score,
                    context: query.includeContext ? this.getContext(memory) : undefined,
                });
            }
        }
        // Sort by score and limit results
        results.sort((a, b) => b.score - a.score);
        return results.slice(0, query.limit || 10);
    }
    async getStats() {
        const memories = Array.from(this.memories.values());
        const byType = {};
        const byImportance = {};
        const tagCounts = {};
        memories.forEach(memory => {
            byType[memory.type] = (byType[memory.type] || 0) + 1;
            byImportance[memory.importance] = (byImportance[memory.importance] || 0) + 1;
            memory.tags.forEach(tag => {
                tagCounts[tag] = (tagCounts[tag] || 0) + 1;
            });
        });
        const now = new Date();
        const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        const thisWeek = new Date(today.getTime() - (7 * 24 * 60 * 60 * 1000));
        const todayCount = memories.filter(m => new Date(m.timestamp) >= today).length;
        const thisWeekCount = memories.filter(m => new Date(m.timestamp) >= thisWeek).length;
        const topTags = Object.entries(tagCounts)
            .sort(([, a], [, b]) => b - a)
            .slice(0, 10)
            .map(([name, count]) => ({ name, count }));
        const storageSize = this.calculateStorageSize();
        return {
            totalEntries: memories.length,
            storageSize,
            lastUpdated: new Date().toISOString(),
            byType,
            byImportance,
            recentActivity: {
                today: todayCount,
                thisWeek: thisWeekCount,
                mostActiveDay: this.getMostActiveDay(memories),
            },
            topTags,
        };
    }
    async clear(options) {
        let deletedCount = 0;
        const toDelete = [];
        for (const [id, memory] of this.memories) {
            let shouldDelete = true;
            if (options.type && memory.type !== options.type) {
                shouldDelete = false;
            }
            if (options.olderThan) {
                const cutoffDate = new Date(options.olderThan);
                const memoryDate = new Date(memory.timestamp);
                if (memoryDate >= cutoffDate) {
                    shouldDelete = false;
                }
            }
            if (shouldDelete) {
                toDelete.push(id);
            }
        }
        // Delete memories
        for (const id of toDelete) {
            this.memories.delete(id);
            this.semanticIndex.entries.delete(id);
            this.memoryGraph.nodes.delete(id);
            this.memoryGraph.edges.delete(id);
            deletedCount++;
        }
        // Save changes
        await this.saveMemories();
        await this.saveIndex();
        return deletedCount;
    }
    async export(options) {
        const memories = Array.from(this.memories.values());
        let data;
        let content;
        switch (options.format) {
            case 'json':
                data = {
                    memories: options.includeMetadata ? memories : memories.map(m => ({
                        id: m.id,
                        content: m.content,
                        type: m.type,
                        tags: m.tags,
                        timestamp: m.timestamp,
                    })),
                    exported: new Date().toISOString(),
                    totalEntries: memories.length,
                };
                content = JSON.stringify(data, null, 2);
                break;
            case 'markdown':
                content = this.generateMarkdownExport(memories, options.includeMetadata);
                break;
            case 'csv':
                content = this.generateCSVExport(memories, options.includeMetadata);
                break;
            default:
                throw new Error(`Unsupported export format: ${options.format}`);
        }
        const size = Buffer.byteLength(content, 'utf8');
        if (options.outputPath) {
            await fs.writeFile(options.outputPath, content);
            return {
                outputPath: options.outputPath,
                entryCount: memories.length,
                size,
            };
        }
        else {
            return {
                data,
                entryCount: memories.length,
                size,
            };
        }
    }
    async suggestRelated(options) {
        const query = {
            query: options.currentContext,
            type: options.type,
            limit: options.limit || 5,
            minScore: 0.3, // Higher threshold for suggestions
        };
        return this.search(query);
    }
    async generateInsights() {
        const insights = [];
        const memories = Array.from(this.memories.values());
        // Pattern detection
        const patterns = this.detectPatterns(memories);
        insights.push(...patterns);
        // Gap analysis
        const gaps = this.detectGaps(memories);
        insights.push(...gaps);
        // Clustering analysis
        const clusters = this.detectClusters(memories);
        insights.push(...clusters);
        return insights;
    }
    async loadMemories() {
        try {
            const data = await fs.readFile(this.memoryPath, 'utf-8');
            const memoriesArray = JSON.parse(data);
            this.memories.clear();
            for (const memory of memoriesArray) {
                this.memories.set(memory.id, memory);
            }
        }
        catch (error) {
            // File doesn't exist or is invalid, start with empty memories
            this.memories.clear();
        }
    }
    async saveMemories() {
        const memoriesArray = Array.from(this.memories.values());
        await fs.writeFile(this.memoryPath, JSON.stringify(memoriesArray, null, 2));
    }
    async loadIndex() {
        try {
            const data = await fs.readFile(this.indexPath, 'utf-8');
            const indexData = JSON.parse(data);
            this.semanticIndex = {
                entries: new Map(indexData.entries || []),
                lastUpdated: indexData.lastUpdated || new Date().toISOString(),
            };
        }
        catch (error) {
            // Index doesn't exist, start fresh
        }
    }
    async saveIndex() {
        const indexData = {
            entries: Array.from(this.semanticIndex.entries.entries()),
            lastUpdated: this.semanticIndex.lastUpdated,
        };
        await fs.writeFile(this.indexPath, JSON.stringify(indexData, null, 2));
    }
    async generateEmbedding(memory) {
        // Simple word-based embedding (in real implementation, use proper embeddings)
        const words = memory.content.toLowerCase().split(/\s+/);
        const embedding = new Array(100).fill(0);
        // Simple hash-based embedding for demonstration
        for (let i = 0; i < words.length; i++) {
            const hash = this.simpleHash(words[i]);
            const index = Math.abs(hash) % embedding.length;
            embedding[index] += 1;
        }
        // Normalize
        const norm = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
        if (norm > 0) {
            for (let i = 0; i < embedding.length; i++) {
                embedding[i] /= norm;
            }
        }
        this.semanticIndex.entries.set(memory.id, embedding);
        this.semanticIndex.lastUpdated = new Date().toISOString();
    }
    async updateGraph(memory) {
        // Update memory graph with new node
        this.memoryGraph.nodes.set(memory.id, {
            id: memory.id,
            type: memory.type,
            importance: this.getImportanceScore(memory.importance),
            connections: 0,
            lastAccessed: memory.timestamp,
        });
        // Find and create edges to related memories
        const related = await this.findRelatedMemories(memory);
        const edges = [];
        for (const relatedId of related) {
            edges.push({
                from: memory.id,
                to: relatedId,
                relationship: 'relates_to',
                strength: 0.5, // Can be calculated based on similarity
            });
        }
        if (edges.length > 0) {
            this.memoryGraph.edges.set(memory.id, edges);
        }
    }
    calculateTextRelevance(query, content) {
        const queryWords = query.toLowerCase().split(/\s+/);
        const contentWords = content.toLowerCase().split(/\s+/);
        let matches = 0;
        for (const queryWord of queryWords) {
            if (contentWords.some(word => word.includes(queryWord) || queryWord.includes(word))) {
                matches++;
            }
        }
        return matches / queryWords.length;
    }
    getContext(memory) {
        // Get related memories as context
        const related = this.memoryGraph.edges.get(memory.id) || [];
        if (related.length === 0)
            return '';
        const relatedMemories = related
            .slice(0, 3)
            .map(edge => this.memories.get(edge.to))
            .filter(Boolean)
            .map(m => m.content.substring(0, 100))
            .join(' ... ');
        return `Related context: ${relatedMemories}`;
    }
    calculateStorageSize() {
        return Buffer.byteLength(JSON.stringify(Array.from(this.memories.values())), 'utf8');
    }
    getMostActiveDay(memories) {
        const dayTallies = {};
        memories.forEach(memory => {
            const day = new Date(memory.timestamp).toDateString();
            dayTallies[day] = (dayTallies[day] || 0) + 1;
        });
        const mostActive = Object.entries(dayTallies)
            .sort(([, a], [, b]) => b - a)[0];
        return mostActive ? mostActive[0] : 'No activity yet';
    }
    generateMarkdownExport(memories, includeMetadata) {
        let content = '# Memory Export\n\n';
        content += `Exported: ${new Date().toISOString()}\n`;
        content += `Total Entries: ${memories.length}\n\n`;
        memories.forEach((memory, index) => {
            content += `## ${index + 1}. ${memory.type.toUpperCase()} - ${memory.importance}\n\n`;
            content += `${memory.content}\n\n`;
            if (memory.tags.length > 0) {
                content += `**Tags:** ${memory.tags.join(', ')}\n\n`;
            }
            if (includeMetadata) {
                content += `**Timestamp:** ${memory.timestamp}\n`;
                content += `**ID:** ${memory.id}\n`;
                if (Object.keys(memory.metadata).length > 0) {
                    content += `**Metadata:** ${JSON.stringify(memory.metadata)}\n`;
                }
            }
            content += '\n---\n\n';
        });
        return content;
    }
    generateCSVExport(memories, includeMetadata) {
        const headers = ['ID', 'Type', 'Content', 'Tags', 'Importance', 'Timestamp'];
        if (includeMetadata) {
            headers.push('Metadata');
        }
        let content = headers.join(',') + '\n';
        memories.forEach(memory => {
            const row = [
                memory.id,
                memory.type,
                `"${memory.content.replace(/"/g, '""')}"`,
                `"${memory.tags.join(', ')}"`,
                memory.importance,
                memory.timestamp,
            ];
            if (includeMetadata) {
                row.push(`"${JSON.stringify(memory.metadata).replace(/"/g, '""')}"`);
            }
            content += row.join(',') + '\n';
        });
        return content;
    }
    async findRelatedMemories(memory) {
        const related = [];
        // Find memories with shared tags
        for (const [id, otherMemory] of this.memories) {
            if (id === memory.id)
                continue;
            const sharedTags = memory.tags.filter(tag => otherMemory.tags.includes(tag));
            if (sharedTags.length > 0) {
                related.push(id);
            }
        }
        return related.slice(0, 5); // Limit to 5 related memories
    }
    getImportanceScore(importance) {
        const scores = { low: 1, medium: 2, high: 3, critical: 4 };
        return scores[importance] || 2;
    }
    simpleHash(str) {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            const char = str.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash; // Convert to 32-bit integer
        }
        return hash;
    }
    detectPatterns(memories) {
        const insights = [];
        // Detect frequent types
        const typeCounts = memories.reduce((acc, memory) => {
            acc[memory.type] = (acc[memory.type] || 0) + 1;
            return acc;
        }, {});
        const dominantType = Object.entries(typeCounts)
            .sort(([, a], [, b]) => b - a)[0];
        if (dominantType && dominantType[1] > memories.length * 0.5) {
            insights.push({
                type: 'pattern',
                title: `Dominant Memory Type: ${dominantType[0]}`,
                description: `${dominantType[1]} out of ${memories.length} memories are of type "${dominantType[0]}"`,
                confidence: 0.8,
                evidence: [`${Math.round((dominantType[1] / memories.length) * 100)}% of memories are ${dominantType[0]} type`],
                recommendations: [`Consider diversifying memory types`, `Create templates for ${dominantType[0]} entries`],
            });
        }
        return insights;
    }
    detectGaps(memories) {
        const insights = [];
        // Check for missing types
        const expectedTypes = ['code', 'documentation', 'decision', 'learning', 'context'];
        const existingTypes = new Set(memories.map(m => m.type));
        const missingTypes = expectedTypes.filter((type) => !existingTypes.has(type));
        if (missingTypes.length > 0) {
            insights.push({
                type: 'gap',
                title: 'Missing Memory Types',
                description: `The following memory types are not represented: ${missingTypes.join(', ')}`,
                confidence: 0.9,
                evidence: [`No memories found of types: ${missingTypes.join(', ')}`],
                recommendations: missingTypes.map(type => `Consider storing ${type} related information`),
            });
        }
        return insights;
    }
    detectClusters(memories) {
        const insights = [];
        // Simple tag-based clustering
        const tagGroups = {};
        memories.forEach(memory => {
            memory.tags.forEach(tag => {
                if (!tagGroups[tag])
                    tagGroups[tag] = [];
                tagGroups[tag].push(memory.id);
            });
        });
        const largeClusters = Object.entries(tagGroups)
            .filter(([, ids]) => ids.length >= 3)
            .sort(([, a], [, b]) => b.length - a.length);
        if (largeClusters.length > 0) {
            const [tag, ids] = largeClusters[0];
            insights.push({
                type: 'cluster',
                title: `Large Memory Cluster: ${tag}`,
                description: `${ids.length} memories are tagged with "${tag}"`,
                confidence: 0.7,
                evidence: [`${ids.length} memories share the tag "${tag}"`],
                recommendations: [`Review and organize memories tagged with "${tag}"`, `Consider creating sub-categories for this cluster`],
            });
        }
        return insights;
    }
}
//# sourceMappingURL=memory-manager.js.map