import { describe, it, expect } from 'vitest';
import type {
  RAGDocument,
  RAGChunk,
  RAGSearchQuery,
  RAGSearchResult,
  RAGCollection,
  RAGConfig,
  EmbeddingVector,
  ChunkMetadata,
  DocumentMetadata,
  IndexingOptions,
  RAGStats
} from '../types.js';

describe('RAG Types', () => {
  describe('RAGDocument Interface', () => {
    it('should have required fields', () => {
      const doc: RAGDocument = {
        id: 'doc-123',
        path: '/docs/test.md',
        content: '# Test Document',
        metadata: {
          title: 'Test',
          lastModified: new Date().toISOString(),
          size: 1024
        }
      };

      expect(doc.id).toBeDefined();
      expect(doc.path).toBeDefined();
      expect(doc.content).toBeDefined();
      expect(doc.metadata).toBeDefined();
    });

    it('should support optional fields', () => {
      const doc: RAGDocument = {
        id: 'doc-456',
        path: '/docs/guide.md',
        content: '# Guide',
        metadata: {
          title: 'Guide',
          lastModified: new Date().toISOString(),
          size: 2048,
          author: 'John Doe',
          tags: ['tutorial', 'guide']
        },
        chunks: [],
        embedding: new Float32Array([0.1, 0.2, 0.3])
      };

      expect(doc.chunks).toBeDefined();
      expect(doc.embedding).toBeInstanceOf(Float32Array);
      expect(doc.metadata.author).toBe('John Doe');
      expect(doc.metadata.tags).toEqual(['tutorial', 'guide']);
    });
  });

  describe('RAGChunk Interface', () => {
    it('should have required chunk fields', () => {
      const chunk: RAGChunk = {
        id: 'chunk-001',
        documentId: 'doc-123',
        content: 'This is a chunk of text',
        index: 0,
        metadata: {
          startOffset: 0,
          endOffset: 23,
          type: 'paragraph'
        }
      };

      expect(chunk.id).toBeDefined();
      expect(chunk.documentId).toBeDefined();
      expect(chunk.content).toBeDefined();
      expect(chunk.index).toBe(0);
      expect(chunk.metadata).toBeDefined();
    });

    it('should support embedding vectors', () => {
      const chunk: RAGChunk = {
        id: 'chunk-002',
        documentId: 'doc-123',
        content: 'Another chunk',
        index: 1,
        metadata: {
          startOffset: 24,
          endOffset: 37,
          type: 'header',
          level: 2
        },
        embedding: new Float32Array([0.4, 0.5, 0.6])
      };

      expect(chunk.embedding).toBeInstanceOf(Float32Array);
      expect(chunk.embedding!.length).toBe(3);
      expect(chunk.metadata.level).toBe(2);
    });
  });

  describe('RAGSearchQuery Interface', () => {
    it('should have minimal required fields', () => {
      const query: RAGSearchQuery = {
        query: 'test search'
      };

      expect(query.query).toBe('test search');
      expect(query.limit).toBeUndefined();
      expect(query.collection).toBeUndefined();
    });

    it('should support all optional parameters', () => {
      const query: RAGSearchQuery = {
        query: 'advanced search',
        limit: 10,
        collection: 'docs',
        threshold: 0.7,
        filters: {
          author: 'Jane Doe',
          tags: ['api', 'reference']
        }
      };

      expect(query.limit).toBe(10);
      expect(query.collection).toBe('docs');
      expect(query.threshold).toBe(0.7);
      expect(query.filters).toBeDefined();
      expect(query.filters!.author).toBe('Jane Doe');
    });
  });

  describe('RAGSearchResult Interface', () => {
    it('should contain all result fields', () => {
      const result: RAGSearchResult = {
        chunk: {
          id: 'chunk-001',
          documentId: 'doc-123',
          content: 'Matching content',
          index: 0,
          metadata: {
            startOffset: 0,
            endOffset: 16,
            type: 'paragraph'
          }
        },
        score: 0.95,
        document: {
          id: 'doc-123',
          path: '/docs/test.md',
          title: 'Test Document'
        }
      };

      expect(result.chunk).toBeDefined();
      expect(result.score).toBe(0.95);
      expect(result.document).toBeDefined();
      expect(result.document.title).toBe('Test Document');
    });

    it('should support highlights', () => {
      const result: RAGSearchResult = {
        chunk: {
          id: 'chunk-002',
          documentId: 'doc-456',
          content: 'This is the matching text',
          index: 1,
          metadata: {
            startOffset: 100,
            endOffset: 125,
            type: 'paragraph'
          }
        },
        score: 0.88,
        document: {
          id: 'doc-456',
          path: '/docs/guide.md',
          title: 'Guide'
        },
        highlights: [
          { start: 12, end: 20, text: 'matching' }
        ]
      };

      expect(result.highlights).toBeDefined();
      expect(result.highlights![0].text).toBe('matching');
    });
  });

  describe('RAGCollection Interface', () => {
    it('should define collection properties', () => {
      const collection: RAGCollection = {
        name: 'default',
        description: 'Default document collection',
        documentCount: 10,
        chunkCount: 150,
        lastUpdated: new Date().toISOString()
      };

      expect(collection.name).toBe('default');
      expect(collection.documentCount).toBe(10);
      expect(collection.chunkCount).toBe(150);
    });

    it('should support metadata', () => {
      const collection: RAGCollection = {
        name: 'api-docs',
        description: 'API documentation',
        documentCount: 25,
        chunkCount: 500,
        lastUpdated: new Date().toISOString(),
        metadata: {
          version: '1.0.0',
          language: 'en'
        }
      };

      expect(collection.metadata).toBeDefined();
      expect(collection.metadata!.version).toBe('1.0.0');
    });
  });

  describe('RAGConfig Interface', () => {
    it('should have all configuration options', () => {
      const config: RAGConfig = {
        enabled: true,
        indexPaths: ['./docs/**/*.md'],
        chunkSize: 500,
        chunkOverlap: 50,
        embeddingModel: 'all-MiniLM-L6-v2',
        collections: {
          default: {
            name: 'default',
            paths: ['./docs/**/*.md']
          }
        }
      };

      expect(config.enabled).toBe(true);
      expect(config.indexPaths).toContain('./docs/**/*.md');
      expect(config.chunkSize).toBe(500);
      expect(config.chunkOverlap).toBe(50);
      expect(config.embeddingModel).toBe('all-MiniLM-L6-v2');
      expect(config.collections.default).toBeDefined();
    });

    it('should support persistence options', () => {
      const config: RAGConfig = {
        enabled: true,
        indexPaths: [],
        chunkSize: 300,
        chunkOverlap: 30,
        embeddingModel: 'all-MiniLM-L6-v2',
        collections: {},
        persistencePath: '.atlas/rag',
        autoIndex: true,
        cacheEmbeddings: true
      };

      expect(config.persistencePath).toBe('.atlas/rag');
      expect(config.autoIndex).toBe(true);
      expect(config.cacheEmbeddings).toBe(true);
    });
  });

  describe('EmbeddingVector Type', () => {
    it('should be a Float32Array', () => {
      const vector: EmbeddingVector = new Float32Array([0.1, 0.2, 0.3, 0.4]);
      
      expect(vector).toBeInstanceOf(Float32Array);
      expect(vector.length).toBe(4);
      expect(vector[0]).toBeCloseTo(0.1, 5);
    });
  });

  describe('ChunkMetadata Interface', () => {
    it('should support various chunk types', () => {
      const paragraphMeta: ChunkMetadata = {
        startOffset: 0,
        endOffset: 100,
        type: 'paragraph'
      };

      const headerMeta: ChunkMetadata = {
        startOffset: 101,
        endOffset: 120,
        type: 'header',
        level: 1
      };

      const codeBlockMeta: ChunkMetadata = {
        startOffset: 200,
        endOffset: 300,
        type: 'code',
        language: 'typescript'
      };

      expect(paragraphMeta.type).toBe('paragraph');
      expect(headerMeta.level).toBe(1);
      expect(codeBlockMeta.language).toBe('typescript');
    });
  });

  describe('DocumentMetadata Interface', () => {
    it('should have required metadata fields', () => {
      const metadata: DocumentMetadata = {
        title: 'Test Document',
        lastModified: '2024-01-15T10:00:00Z',
        size: 2048
      };

      expect(metadata.title).toBe('Test Document');
      expect(metadata.lastModified).toBeDefined();
      expect(metadata.size).toBe(2048);
    });

    it('should support optional metadata', () => {
      const metadata: DocumentMetadata = {
        title: 'API Guide',
        lastModified: '2024-01-15T10:00:00Z',
        size: 4096,
        author: 'Jane Smith',
        tags: ['api', 'guide', 'reference'],
        version: '2.0.0',
        language: 'en',
        custom: {
          category: 'technical',
          difficulty: 'intermediate'
        }
      };

      expect(metadata.author).toBe('Jane Smith');
      expect(metadata.tags).toHaveLength(3);
      expect(metadata.version).toBe('2.0.0');
      expect(metadata.custom!.category).toBe('technical');
    });
  });

  describe('IndexingOptions Interface', () => {
    it('should have indexing configuration', () => {
      const options: IndexingOptions = {
        forceReindex: false,
        updateOnly: true,
        collection: 'default'
      };

      expect(options.forceReindex).toBe(false);
      expect(options.updateOnly).toBe(true);
      expect(options.collection).toBe('default');
    });

    it('should support progress callback', () => {
      let progressCalled = false;
      const options: IndexingOptions = {
        forceReindex: true,
        updateOnly: false,
        collection: 'docs',
        onProgress: (current, total) => {
          progressCalled = true;
          expect(current).toBeLessThanOrEqual(total);
        }
      };

      // Simulate progress callback
      options.onProgress!(5, 10);
      expect(progressCalled).toBe(true);
    });
  });

  describe('RAGStats Interface', () => {
    it('should contain comprehensive statistics', () => {
      const stats: RAGStats = {
        totalDocuments: 50,
        totalChunks: 1000,
        totalCollections: 3,
        indexSize: 1024 * 1024 * 10, // 10MB
        lastIndexed: new Date().toISOString(),
        collections: {
          default: {
            documentCount: 30,
            chunkCount: 600,
            sizeBytes: 1024 * 1024 * 6
          },
          'api-docs': {
            documentCount: 20,
            chunkCount: 400,
            sizeBytes: 1024 * 1024 * 4
          }
        }
      };

      expect(stats.totalDocuments).toBe(50);
      expect(stats.totalChunks).toBe(1000);
      expect(stats.totalCollections).toBe(3);
      expect(stats.collections.default.documentCount).toBe(30);
      expect(stats.collections['api-docs'].chunkCount).toBe(400);
    });
  });
});