/**
 * Unit Tests for Brainy 3.0 Core Functionality
 * 
 * Tests business logic with real embeddings - production ready
 * No mocks, no fakes, real implementation
 */

import { describe, it, expect, beforeEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType } from '../../src/types/graphTypes.js'

describe('Brainy 3.0 Core (Unit Tests)', () => {
  let brain: Brainy

  beforeEach(async () => {
    // Create instance with real embeddings for production-ready tests
    brain = new Brainy({ requireSubtype: false,
      storage: { type: 'memory' }
    })
    
    await brain.init()
  })

  describe('CRUD Operations', () => {
    it('should create items with add', async () => {
      const id = await brain.add({ 
        data: { name: 'JavaScript', type: 'language' },
        type: NounType.Concept,
        metadata: { category: 'programming' }
      })
      
      expect(id).toBeTypeOf('string')
      expect(id.length).toBeGreaterThan(0)
    })

    it('should retrieve items with get', async () => {
      const id = await brain.add({
        data: 'Python is a programming language created in 1991',
        type: NounType.Concept,
        metadata: { name: 'Python', category: 'programming', year: 1991 }
      })

      const retrieved = await brain.get(id)

      expect(retrieved).toBeTruthy()
      expect(retrieved?.metadata?.name).toBe('Python')
      expect(retrieved?.metadata?.category).toBe('programming')
      expect(retrieved?.metadata?.year).toBe(1991)
    })

    it('should update items with update', async () => {
      const id = await brain.add({
        data: 'TypeScript is a typed JavaScript superset',
        type: NounType.Concept,
        metadata: { name: 'TypeScript', version: '4.0', category: 'programming' }
      })

      await brain.update({
        id,
        metadata: { version: '5.0', popularity: 'high' }
      })

      const updated = await brain.get(id)
      expect(updated?.metadata?.version).toBe('5.0')
      expect(updated?.metadata?.popularity).toBe('high')
      expect(updated?.metadata?.name).toBe('TypeScript') // Original metadata preserved
    })

    it('should delete items with delete', async () => {
      const id = await brain.add({
        data: { name: 'ToDelete', temp: true },
        type: NounType.Concept
      })
      
      // Verify it exists
      expect(await brain.get(id)).toBeTruthy()
      
      // Delete it
      await brain.remove(id)
      
      // Verify it's gone
      expect(await brain.get(id)).toBeNull()
    })

    it('should handle non-existent IDs according to API contract', async () => {
      // Use valid UUID format (stricter validation in v5.1.0)
      // v5.10.0: Can't use 00000000... anymore (it's the VFS root)
      const fakeId = '11111111-1111-1111-1111-111111111111'

      expect(await brain.get(fakeId)).toBeNull()

      // update should handle non-existent ID gracefully
      await expect(brain.update({
        id: fakeId,
        data: { test: 'data' }
      })).rejects.toThrow()

      // delete should not throw for non-existent ID
      await expect(brain.remove(fakeId)).resolves.not.toThrow()
    })
  })

  describe('Search Operations', () => {
    beforeEach(async () => {
      // Add test data with real embeddings
      await brain.add({ 
        data: { name: 'React', type: 'framework', category: 'frontend' },
        type: NounType.Concept,
        metadata: { tags: ['ui', 'javascript'] }
      })
      await brain.add({
        data: { name: 'Vue', type: 'framework', category: 'frontend' },
        type: NounType.Concept,
        metadata: { tags: ['ui', 'javascript'] }
      })
      await brain.add({
        data: { name: 'Express', type: 'framework', category: 'backend' },
        type: NounType.Concept,
        metadata: { tags: ['server', 'nodejs'] }
      })
      await brain.add({
        data: { name: 'Java', type: 'language', category: 'backend' },
        type: NounType.Concept,
        metadata: { tags: ['jvm', 'enterprise'] }
      })
    })

    it('should return search results with real embeddings', async () => {
      const results = await brain.find({ 
        query: 'frontend framework',
        limit: 2
      })
      
      expect(results).toBeInstanceOf(Array)
      expect(results.length).toBeGreaterThan(0)
      expect(results.length).toBeLessThanOrEqual(2)
      
      // Results should have required properties
      results.forEach((result: any) => {
        expect(result).toHaveProperty('id')
        expect(result).toHaveProperty('score')
        expect(result).toHaveProperty('entity')
      })
    })

    it('should handle limit parameter', async () => {
      const limitedResults = await brain.find({ 
        query: 'framework',
        limit: 2
      })
      const unlimitedResults = await brain.find({
        query: 'framework',
        limit: 10
      })
      
      expect(limitedResults.length).toBeLessThanOrEqual(2)
      expect(unlimitedResults.length).toBeLessThanOrEqual(10)
    })

    it('should search by metadata filters', async () => {
      const results = await brain.find({
        where: { category: 'frontend' },
        limit: 10
      })
      
      expect(results).toBeInstanceOf(Array)
      // All results should have frontend category
      results.forEach((item: any) => {
        expect(item.entity.metadata?.category).toBe('frontend')
      })
    })

    it('should handle complex queries with Triple Intelligence', async () => {
      const results = await brain.find({
        query: 'javascript',
        where: { type: 'framework' },
        limit: 5,
        fusion: {
          strategy: 'adaptive',
          weights: { vector: 0.6, field: 0.4 }
        }
      })
      
      expect(results).toBeInstanceOf(Array)
      // Results should match both vector similarity and field filters
      results.forEach((item: any) => {
        expect(item.entity.metadata?.type).toBe('framework')
      })
    })
  })

  describe('Statistics and Metadata', () => {
    it('should track statistics', async () => {
      await brain.add({
        data: { name: 'Test1' },
        type: NounType.Concept
      })
      await brain.add({
        data: { name: 'Test2' },
        type: NounType.Concept
      })

      const stats = await brain.stats()
      expect(stats.mode).toBe('writer')
      // Exact, not >= : the per-type counter must equal the entity count even
      // after HNSW re-saves neighbor links (see stats-count-accuracy.test.ts).
      expect(stats.entityCount).toBe(2)
      expect(stats.entitiesByType[NounType.Concept]).toBe(2)
    })
  })

  describe('Clear Operations', () => {
    it('should clear all data', async () => {
      await brain.add({
        data: { name: 'Test1' },
        type: NounType.Concept
      })
      await brain.add({
        data: { name: 'Test2' },
        type: NounType.Concept
      })
      await brain.add({
        data: { name: 'Test3' },
        type: NounType.Concept
      })
      
      // Clear everything — entities, relationships, and all indexes
      await brain.clear()

      // Verify user data is cleared. clear() re-creates a fresh VFS root
      // entity, and a thresholdless vector search can surface it — so filter
      // VFS bookkeeping out and assert the added entities are gone.
      const results = await brain.find({
        query: 'Test',
        limit: 10
      })
      const userResults = results.filter(r => !r.metadata?.isVFS)
      expect(userResults.length).toBe(0)
    })
  })

  describe('Edge Cases and Error Handling', () => {
    it('should handle empty queries gracefully', async () => {
      const results = await brain.find({ 
        query: '',
        limit: 5
      })
      
      expect(results).toBeInstanceOf(Array)
    })

    it('should handle special characters in data', async () => {
      const id = await brain.add({
        data: 'Test with special chars: !@#$%^&*()',
        type: NounType.Concept,
        metadata: { name: 'Test !@#$%^&*()', description: 'Has "quotes" and \'apostrophes\'' }
      })

      const retrieved = await brain.get(id)
      expect(retrieved?.metadata?.name).toContain('!@#$%^&*()')
    })

    it('should handle very long text', async () => {
      const longText = 'x'.repeat(10000)
      const id = await brain.add({
        data: longText,
        type: NounType.Document
      })

      const retrieved = await brain.get(id)
      expect(retrieved?.data).toHaveLength(10000)
    })
  })
})