/**
 * Update Field Asymmetry Regression Tests (v7.5.0)
 *
 * Tests for the critical bug where update() didn't remove all indexed fields,
 * causing 7 fields to accumulate on EVERY update. This eventually made queries
 * return 0 results (77x overcounting at scale).
 *
 * Root cause: removalMetadata only contained custom metadata + type, while
 * entityForIndexing contained ALL indexed fields (confidence, weight, createdAt,
 * updatedAt, service, data, createdBy).
 *
 * NO MOCKS - Real integration tests with actual storage
 */

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

describe('Update Field Asymmetry Fix (v7.5.0)', () => {
  let brain: Brainy
  const testPath = './test-brainy-update-asymmetry'

  beforeEach(async () => {
    // Clean up test directory thoroughly
    try {
      await fs.rm(testPath, { recursive: true, force: true })
    } catch {
      // Ignore if doesn't exist
    }

    // Create fresh Brainy instance
    brain = new Brainy({ requireSubtype: false,
      storage: { type: 'filesystem', path: testPath },
      silent: true
    })
    await brain.init()
  })

  afterEach(async () => {
    // Clean up test directory
    try {
      await fs.rm(testPath, { recursive: true, force: true })
    } catch {
      // Ignore cleanup errors
    }
  })

  describe('Query Accuracy After Updates', () => {
    it('should return correct results from find() by type after many updates', async () => {
      // Create entity with specific type
      const id = await brain.add({
        data: 'Findable Entity',
        type: NounType.Person,
        metadata: { name: 'Alice' }
      })

      // Update 20 times with changing metadata
      for (let i = 0; i < 20; i++) {
        await brain.update({
          id,
          metadata: { name: 'Alice', updateCount: i }
        })
      }

      await brain.flush()

      // find() by type should still return the entity
      // (this would fail if index corruption caused overcounting)
      // Note: 'type' is a top-level param, not inside 'where'
      const byType = await brain.find({
        type: NounType.Person
      })

      expect(byType.length).toBeGreaterThanOrEqual(1)
      const found = byType.find(r => r.id === id)
      expect(found).toBeDefined()
      expect(found?.id).toBe(id)
    })

    it('should return correct results from find() by metadata after many updates', async () => {
      // Create entity with specific metadata
      const id = await brain.add({
        data: 'Searchable Entity',
        type: NounType.Document,
        metadata: { category: 'important', status: 'active' }
      })

      // Update 20 times - changing some fields but keeping 'category'
      for (let i = 0; i < 20; i++) {
        await brain.update({
          id,
          metadata: { category: 'important', status: 'active', version: i }
        })
      }

      await brain.flush()

      // find() by metadata should still work
      const results = await brain.find({
        where: { category: 'important' }
      })

      expect(results.length).toBeGreaterThanOrEqual(1)
      const found = results.find(r => r.id === id)
      expect(found).toBeDefined()
    })

    it('should not return duplicate results after multiple updates', async () => {
      // Create 3 entities
      const ids: string[] = []
      for (let i = 0; i < 3; i++) {
        const id = await brain.add({
          data: `Entity ${i}`,
          type: NounType.Document,
          metadata: { group: 'test-group', index: i }
        })
        ids.push(id)
      }

      // Update each entity 10 times
      for (const id of ids) {
        for (let i = 0; i < 10; i++) {
          await brain.update({
            id,
            metadata: { group: 'test-group', version: i }
          })
        }
      }

      await brain.flush()

      // find() should return entities without duplicates
      const results = await brain.find({
        where: { group: 'test-group' }
      })

      // Get unique IDs from results
      const uniqueIds = new Set(results.map(r => r.id))

      // Should have exactly 3 unique entities
      expect(uniqueIds.size).toBe(3)
      // No duplicates - results length should equal unique count
      expect(results.length).toBe(uniqueIds.size)
    })
  })

  describe('Entity Integrity After Updates', () => {
    it('should preserve entity data after many updates', async () => {
      // Create entity
      const id = await brain.add({
        data: 'Original Data',
        type: NounType.Event,
        metadata: { title: 'Original Title' }
      })

      const originalEntity = await brain.get(id)
      const originalCreatedAt = originalEntity?.createdAt

      // Update 15 times
      for (let i = 0; i < 15; i++) {
        await brain.update({
          id,
          metadata: { title: `Updated Title ${i}`, iteration: i }
        })
      }

      await brain.flush()

      // Get final entity
      const finalEntity = await brain.get(id)

      // Entity should still exist and be retrievable
      expect(finalEntity).toBeDefined()
      expect(finalEntity?.id).toBe(id)

      // Original data should be preserved
      expect(finalEntity?.data).toBe('Original Data')
      expect(finalEntity?.type).toBe(NounType.Event)
      expect(finalEntity?.createdAt).toBe(originalCreatedAt)

      // Updated metadata should be reflected
      expect(finalEntity?.metadata?.title).toBe('Updated Title 14')
      expect(finalEntity?.metadata?.iteration).toBe(14)

      // updatedAt should have changed
      expect(finalEntity?.updatedAt).not.toBe(originalCreatedAt)
    })

    it('should handle rapid consecutive updates', async () => {
      // Create entity
      const id = await brain.add({
        data: 'Rapid Update Test',
        type: NounType.Task,
        metadata: { status: 'pending' }
      })

      // Rapid-fire 50 updates without waiting
      const updatePromises = []
      for (let i = 0; i < 50; i++) {
        updatePromises.push(
          brain.update({
            id,
            metadata: { status: 'processing', step: i }
          })
        )
      }

      // Wait for all updates to complete
      await Promise.all(updatePromises)
      await brain.flush()

      // Entity should still be findable and correct
      const entity = await brain.get(id)
      expect(entity).toBeDefined()
      expect(entity?.type).toBe(NounType.Task)

      // Should be findable by type
      // Note: 'type' is a top-level param, not inside 'where'
      const results = await brain.find({
        type: NounType.Task
      })
      expect(results.some(r => r.id === id)).toBe(true)
    })
  })
})
