import { describe, it, expect, beforeEach, vi } from 'vitest';
import { MemoryManager } from '../memory-manager.js';
import { ConfigManager } from '../../../config/config-manager.js';
import { MemoryEntry, MemoryQuery, MemorySearchResult } from '../types.js';

// Mock ConfigManager
vi.mock('../../../config/config-manager.js');

// Mock fs
vi.mock('fs', () => ({
  promises: {
    readFile: vi.fn().mockRejectedValue(new Error('File not found')),
    writeFile: vi.fn().mockResolvedValue(undefined),
    mkdir: vi.fn().mockResolvedValue(undefined),
    readdir: vi.fn().mockResolvedValue([])
  }
}));

const MockConfigManager = ConfigManager as vi.MockedClass<typeof ConfigManager>;

describe('MemoryManager', () => {
  let memoryManager: MemoryManager;
  let mockConfigManager: vi.Mocked<ConfigManager>;

  it('should create MemoryManager instance', () => {
    const config = {} as any;
    const manager = new MemoryManager(config);
    expect(manager).toBeInstanceOf(MemoryManager);
  });

  beforeEach(async () => {
    vi.clearAllMocks();
    
    mockConfigManager = new MockConfigManager() as vi.Mocked<ConfigManager>;
    mockConfigManager.getDataPath = vi.fn().mockReturnValue('/test/data/memory');
    mockConfigManager.getStorageManager = vi.fn().mockReturnValue({
      getModuleDataPath: vi.fn().mockResolvedValue('/test/data/memory/memories.json'),
      getStorageLocation: vi.fn().mockResolvedValue({
        data: '/test/data',
        config: '/test/config',
        cache: '/test/cache'
      }),
      loadData: vi.fn().mockResolvedValue(null),
      saveData: vi.fn().mockResolvedValue(undefined)
    });
    
    memoryManager = new MemoryManager(mockConfigManager);
    await memoryManager.init();
  });

  describe('store', () => {
    it('should store a code memory entry', async () => {
      const memoryData = {
        content: 'function calculateTotal(items) { return items.reduce((sum, item) => sum + item.price, 0); }',
        type: 'code' as const,
        metadata: {
          file: 'src/utils/calculations.js',
          function: 'calculateTotal',
          language: 'javascript',
        },
        tags: ['calculation', 'utility', 'reduce'],
        importance: 'high' as const,
      };

      const entryId = await memoryManager.store(memoryData);

      expect(entryId).toMatch(/^[a-f0-9-]+$/);
    });

    it('should store a documentation memory entry', async () => {
      const memoryData = {
        content: 'API endpoint for user authentication accepts POST requests with email and password',
        type: 'documentation' as const,
        metadata: {
          section: 'Authentication',
          document: 'API.md',
          topics: ['authentication', 'api', 'security'],
        },
        tags: ['api', 'auth', 'security'],
        importance: 'critical' as const,
      };

      const entryId = await memoryManager.store(memoryData);

      expect(entryId).toBeDefined();
    });

    it('should store a decision memory entry', async () => {
      const memoryData = {
        content: 'Decided to use React over Vue.js for better team familiarity and ecosystem support',
        type: 'decision' as const,
        metadata: {
          reasoning: 'Team has more React experience, larger ecosystem, better TypeScript support',
          alternatives: ['Vue.js', 'Angular', 'Svelte'],
          impact: 'high',
        },
        tags: ['architecture', 'frontend', 'framework'],
        importance: 'critical' as const,
      };

      const entryId = await memoryManager.store(memoryData);

      expect(entryId).toBeDefined();
    });
  });

  describe('search', () => {
    it('should search memories by query string', async () => {
      try {
        // Store a memory first
        const memoryId = await memoryManager.store({
          content: 'React component for user authentication',
          type: 'code',
          tags: ['react', 'authentication', 'component'],
          importance: 'high',
          metadata: {}
        });

        // Verify the memory was stored
        expect(memoryId).toBeDefined();

        // Check if the memory is actually in the memories map
        const storedMemory = (memoryManager as any).memories.get(memoryId);
        expect(storedMemory).toBeDefined();
        expect(storedMemory?.content).toBe('React component for user authentication');

        const query: MemoryQuery = {
          query: 'authentication',
          limit: 10,
          minScore: 0 // Lower the threshold
        };

        const results = await memoryManager.search(query);

        // Debug: log the search results and score calculation
        if (results.length === 0) {
          console.log('No results found. Checking memory storage...');
          const stats = await memoryManager.getStats();
          console.log('Total memories:', stats.totalEntries);
          console.log('Memory map size:', (memoryManager as any).memories.size);
          console.log('Stored memory:', storedMemory);
        }

        // Since we're storing in memory, the search should work
        expect(results.length).toBeGreaterThanOrEqual(1);
        expect(results[0].content).toContain('authentication');
      } catch (error) {
        console.error('Test error:', error);
        throw error;
      }
    });

    it('should return empty results when no matches', async () => {
      const query: MemoryQuery = {
        query: 'nonexistent',
        limit: 10
      };

      const results = await memoryManager.search(query);

      expect(results).toEqual([]);
    });

    it('should respect result limit', async () => {
      // Store multiple memories
      for (let i = 0; i < 5; i++) {
        await memoryManager.store({
          content: `Test memory ${i}`,
          type: 'code',
          tags: ['test'],
          importance: 'medium',
          metadata: {}
        });
      }

      const query: MemoryQuery = {
        query: 'test',
        limit: 2
      };

      const results = await memoryManager.search(query);

      expect(results.length).toBeLessThanOrEqual(2);
    });
  });

  describe('getStats', () => {
    beforeEach(async () => {
      // Add memories for stats
      await memoryManager.store({
        content: 'Test memory 1',
        type: 'code',
        tags: ['test', 'code'],
        importance: 'high',
        metadata: {}
      });

      await memoryManager.store({
        content: 'Test memory 2',
        type: 'documentation',
        tags: ['test', 'docs'],
        importance: 'medium',
        metadata: {}
      });
    });

    it('should return memory statistics', async () => {
      const stats = await memoryManager.getStats();

      expect(stats.totalEntries).toBeGreaterThan(0);
      expect(stats.byType).toBeDefined();
      expect(stats.byImportance).toBeDefined();
      expect(stats.topTags).toBeDefined();
      expect(stats.recentActivity).toBeDefined();
    });
  });

  describe('clear', () => {
    beforeEach(async () => {
      await memoryManager.store({
        content: 'Old memory',
        type: 'code',
        tags: ['old'],
        importance: 'low',
        metadata: {}
      });
    });

    it('should clear memories by type', async () => {
      const deletedCount = await memoryManager.clear({ type: 'code' });

      expect(deletedCount).toBeGreaterThan(0);
    });

    it('should clear memories older than specified date', async () => {
      const cutoffDate = new Date(Date.now() + 1000).toISOString(); // Future date
      const deletedCount = await memoryManager.clear({ olderThan: cutoffDate });

      expect(deletedCount).toBeGreaterThan(0);
    });
  });

  describe('export', () => {
    beforeEach(async () => {
      await memoryManager.store({
        content: 'Export test memory',
        type: 'code',
        tags: ['export', 'test'],
        importance: 'medium',
        metadata: {}
      });
    });

    it('should export memories as JSON', async () => {
      const result = await memoryManager.export({
        format: 'json',
        includeMetadata: true
      });

      expect(result).toBeDefined();
      expect(result.entryCount).toBeGreaterThan(0);
      expect(result.size).toBeGreaterThan(0);
    });

    it('should export memories as markdown', async () => {
      const result = await memoryManager.export({
        format: 'markdown',
        includeMetadata: false
      });

      expect(result).toBeDefined();
      expect(result.entryCount).toBeGreaterThan(0);
      expect(result.size).toBeGreaterThan(0);
    });
  });

  describe('generateInsights', () => {
    beforeEach(async () => {
      // Add context-building memories
      await memoryManager.store({
        content: 'Project uses React with TypeScript for frontend',
        type: 'context',
        tags: ['project', 'frontend', 'react', 'typescript'],
        importance: 'high',
        metadata: {}
      });

      await memoryManager.store({
        content: 'Backend built with Node.js and Express',
        type: 'context',
        tags: ['project', 'backend', 'nodejs', 'express'],
        importance: 'high',
        metadata: {}
      });
    });

    it('should generate insights from memories', async () => {
      const insights = await memoryManager.generateInsights();

      expect(insights).toBeDefined();
      expect(Array.isArray(insights)).toBe(true);
      expect(insights.length).toBeGreaterThan(0);
      
      if (insights.length > 0) {
        expect(insights[0]).toHaveProperty('type');
        expect(insights[0]).toHaveProperty('title');
        expect(insights[0]).toHaveProperty('description');
      }
    });
  });

  describe('suggestRelated', () => {
    beforeEach(async () => {
      await memoryManager.store({
        content: 'React hooks for state management',
        type: 'code',
        tags: ['react', 'hooks', 'state'],
        importance: 'high',
        metadata: {}
      });

      await memoryManager.store({
        content: 'useState hook for component state',
        type: 'code',
        tags: ['react', 'hooks', 'useState'],
        importance: 'high',
        metadata: {}
      });

      await memoryManager.store({
        content: 'Vue.js reactive properties',
        type: 'code',
        tags: ['vue', 'reactive', 'properties'],
        importance: 'medium',
        metadata: {}
      });
    });

    it('should suggest related memories', async () => {
      const related = await memoryManager.suggestRelated({
        currentContext: 'React hooks for state management',
        limit: 5
      });

      expect(related).toBeDefined();
      expect(Array.isArray(related)).toBe(true);
    });
  });
});