/**
 * Read-After-Write Consistency Integration Tests (v5.7.2)
 *
 * These tests verify the fix for the critical v5.7.x bug where:
 * - brain.add() would create an entity
 * - brain.relate() would immediately try to verify the entity exists
 * - brain.get() would return null (entity not yet queryable)
 * - Error thrown: "Source entity 19f3f6dd-2102-4b60-956c-bfc1d8213838 not found"
 *
 * The write-through cache in BaseStorage now guarantees that entities are
 * immediately queryable after add() returns, fixing this race condition.
 *
 * Test Coverage:
 * - brain.add() → brain.get() (basic read-after-write)
 * - brain.add() → brain.relate() (the actual bug scenario)
 * - Rapid entity creation + relationship creation (import workflow)
 * - Update then read
 * - Delete then verify deleted
 */

import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType, VerbType } from '../../src/types/graphTypes.js'
import { tmpdir } from 'os'
import { join } from 'path'
import { rmSync } from 'fs'

describe('Read-After-Write Consistency (v5.7.2 Bug Fix)', () => {
  let brain: Brainy
  let testDir: string

  beforeEach(async () => {
    // Create unique test directory for each test
    testDir = join(tmpdir(), `brainy-consistency-${Date.now()}-${Math.random().toString(36).substring(7)}`)

    brain = new Brainy({ requireSubtype: false,
      storage: {
        type: 'filesystem',
        config: {
          baseDir: testDir,
          enableCompression: false // Faster tests
        }
      },
      dimensions: 384
    })

    await brain.init()
  })

  afterEach(async () => {
    // Cleanup
    try {
      await brain.close()
    } catch (err) {
      // Ignore close errors
    }

    try {
      rmSync(testDir, { recursive: true, force: true })
    } catch (err) {
      // Ignore cleanup errors
    }
  })

  it('should allow brain.add() followed by immediate brain.get()', async () => {
    // This tests the core read-after-write guarantee
    const entityId = await brain.add({
      data: 'Test Entity for Immediate Read',
      type: NounType.Thing
    })

    // Immediately query - should NOT return null
    const entity = await brain.get(entityId)

    expect(entity).not.toBeNull()
    expect(entity?.id).toBe(entityId)
    expect(entity?.data).toBe('Test Entity for Immediate Read')
    expect(entity?.type).toBe(NounType.Thing)
  })

  it('should allow brain.add() followed by immediate brain.relate() - THE BUG FIX', async () => {
    // This is the EXACT bug scenario from the v5.7.x report
    // ImportCoordinator would:
    // 1. Create an entity with brain.add()
    // 2. Immediately create a provenance link with brain.relate()
    // 3. brain.relate() calls brain.get() to verify entities exist
    // 4. brain.get() would return null → "Source entity not found" error

    const sourceId = await brain.add({
      data: 'Source Document',
      type: NounType.Document,
      metadata: { filename: 'test.pdf' }
    })

    const targetId = await brain.add({
      data: 'Extracted Entity',
      type: NounType.Thing,
      metadata: { extractedFrom: 'test.pdf' }
    })

    // This should NOT throw "Source entity not found" or "Target entity not found"
    // The write-through cache ensures both entities are immediately queryable
    const relationId = await brain.relate({
      from: sourceId,
      to: targetId,
      type: VerbType.Contains,
      metadata: {
        relationshipType: 'provenance',
        evidence: 'Extracted from test.pdf'
      }
    })

    expect(relationId).toBeTruthy()
    expect(typeof relationId).toBe('string')

    // Verify relationship exists and is correct
    const relations = await brain.related(sourceId)
    expect(relations).toHaveLength(1)
    expect(relations[0].to).toBe(targetId)
    expect(relations[0].type).toBe(VerbType.Contains)
  })

  it('should handle rapid entity creation + relationship creation (import scenario)', async () => {
    // Simulate the import workflow from the bug report:
    // - Extract 372 entities from PDF
    // - Create entities rapidly
    // - Create provenance relationships immediately after
    // - v5.7.x would fail with "Source entity not found"

    const documentId = await brain.add({
      data: 'Test PDF Document',
      type: NounType.Document,
      metadata: { filename: 'large-import.pdf', pages: 50 }
    })

    const entityIds: string[] = []
    const startTime = Date.now()

    // Create 100 entities rapidly (simulates extraction phase)
    for (let i = 0; i < 100; i++) {
      const entityId = await brain.add({
        data: `Entity ${i} extracted from PDF`,
        type: NounType.Thing,
        metadata: {
          entityIndex: i,
          extractedFrom: 'large-import.pdf',
          page: Math.floor(i / 2) + 1
        }
      })
      entityIds.push(entityId)
    }

    console.log(`Created 100 entities in ${Date.now() - startTime}ms`)

    // Create provenance relationships immediately after entity creation
    // This is where v5.7.x would fail
    const relStartTime = Date.now()
    for (let i = 0; i < entityIds.length; i++) {
      await brain.relate({
        from: documentId,
        to: entityIds[i],
        type: VerbType.Contains,
        metadata: {
          relationshipType: 'provenance',
          extractionOrder: i
        }
      })
    }

    console.log(`Created 100 relationships in ${Date.now() - relStartTime}ms`)

    // Verify all relationships created successfully
    const relations = await brain.related(documentId)
    expect(relations.length).toBe(100)

    // Verify each entity is still queryable
    for (const id of entityIds) {
      const entity = await brain.get(id)
      expect(entity).not.toBeNull()
      expect(entity?.id).toBe(id)
    }
  })

  it('should support read-after-update', async () => {
    // Create entity
    const entityId = await brain.add({
      data: 'Original Data',
      type: NounType.Thing,
      metadata: { version: 1 }
    })

    // Update immediately
    await brain.update({
      id: entityId,
      metadata: { version: 2, updated: true }
    })

    // Read immediately - should see updated data
    const entity = await brain.get(entityId)
    expect(entity).not.toBeNull()
    expect(entity?.metadata?.version).toBe(2)
    expect(entity?.metadata?.updated).toBe(true)
  })

  it('should verify entity is deleted after brain.remove()', async () => {
    // Create entity
    const entityId = await brain.add({
      data: 'Entity to be deleted',
      type: NounType.Thing
    })

    // Verify it exists
    const beforeDelete = await brain.get(entityId)
    expect(beforeDelete).not.toBeNull()

    // Delete
    await brain.remove(entityId)

    // Verify it's deleted (should return null)
    const afterDelete = await brain.get(entityId)
    expect(afterDelete).toBeNull()
  })

  it('should handle complex relationship graph creation', async () => {
    // Create a complex graph:
    // Document → contains → Concept1
    //                    → Concept2
    //                    → Concept3
    // Concept1 → relatedTo → Concept2
    // Concept2 → relatedTo → Concept3
    // All created in rapid succession

    const documentId = await brain.add({
      data: 'Complex Document',
      type: NounType.Document
    })

    const concept1Id = await brain.add({
      data: 'Concept 1',
      type: NounType.Concept
    })

    const concept2Id = await brain.add({
      data: 'Concept 2',
      type: NounType.Concept
    })

    const concept3Id = await brain.add({
      data: 'Concept 3',
      type: NounType.Concept
    })

    // Create containment relationships
    await brain.relate({ from: documentId, to: concept1Id, type: VerbType.Contains })
    await brain.relate({ from: documentId, to: concept2Id, type: VerbType.Contains })
    await brain.relate({ from: documentId, to: concept3Id, type: VerbType.Contains })

    // Create concept relationships
    await brain.relate({ from: concept1Id, to: concept2Id, type: VerbType.RelatedTo })
    await brain.relate({ from: concept2Id, to: concept3Id, type: VerbType.RelatedTo })

    // Verify all relationships exist
    const docRelations = await brain.related(documentId)
    expect(docRelations.length).toBe(3)

    const concept1Relations = await brain.related(concept1Id)
    expect(concept1Relations.length).toBeGreaterThan(0)

    const concept2Relations = await brain.related(concept2Id)
    expect(concept2Relations.length).toBeGreaterThan(0)
  })

  it('should handle the exact v5.7.x bug scenario with UUID from report', async () => {
    // The bug report mentioned entity ID: 19f3f6dd-2102-4b60-956c-bfc1d8213838
    // Let's use our own UUIDs but replicate the exact sequence

    // Step 1: PDF import creates document entity
    const documentId = await brain.add({
      data: 'TfT~Sapient Species.pdf',
      type: NounType.Document,
      metadata: {
        filename: 'TfT~Sapient Species.pdf',
        size: 2036.8 * 1024,
        format: 'pdf'
      }
    })

    // Step 2: Extract 372 entities (we'll do 10 for speed)
    const extractedIds: string[] = []
    for (let i = 0; i < 10; i++) {
      const id = await brain.add({
        data: `Species ${i}`,
        type: NounType.Thing,
        metadata: {
          extractedFrom: 'TfT~Sapient Species.pdf',
          entityIndex: i
        }
      })
      extractedIds.push(id)
    }

    // Step 3: VFSStructureGenerator creates provenance links
    // This is where "Source entity not found" would occur
    for (const entityId of extractedIds) {
      const relationId = await brain.relate({
        from: documentId,
        to: entityId,
        type: VerbType.Contains,
        metadata: {
          relationshipType: 'provenance',
          evidence: 'Extracted from PDF'
        }
      })
      expect(relationId).toBeTruthy()
    }

    // Step 4: Verify all entities are queryable
    const doc = await brain.get(documentId)
    expect(doc).not.toBeNull()

    for (const id of extractedIds) {
      const entity = await brain.get(id)
      expect(entity).not.toBeNull()
      expect(entity?.id).toBe(id)
    }

    // Step 5: Verify relationships exist
    const relations = await brain.related(documentId)
    expect(relations.length).toBe(10)
  })
})
