/**
 * @module tests/integration/read-your-writes-contract
 * @description The read-your-writes contract — brainy's spine guarantee that a
 * write which has RETURNED is immediately readable, under the single writer, by
 * BOTH id and every index (metadata filter, vector search, graph traversal),
 * with NO await, delay, or retry between the write returning and the read.
 *
 * Why it holds by construction on the canonical + JS-index path: every mutation
 * runs its canonical writes AND all derived-index projections (HNSW, metadata
 * index, graph index) as operations inside ONE transaction that commits before
 * the method returns — there is no post-return "eventual" indexing window. get()
 * additionally rides the storage write-through cache (read-after-write within
 * the process). The generation counter is the {seq} a caller can pin: a write
 * returns generation N and every subsequent live read observes ≥ N.
 *
 * This pins the guarantee so no future change can quietly move index maintenance
 * off the commit path (which would turn a returned write into a not-yet-queryable
 * one — the "a 200 is still not durability/queryability" failure the spine
 * program exists to make impossible). The native accelerator must honor the same
 * in-commit contract; a background consolidation/optimization must never gate
 * queryability of an acknowledged write.
 */
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import * as fs from 'node:fs'
import * as os from 'node:os'
import * as path from 'node:path'
import { Brainy } from '../../src/brainy.js'
import { NounType, VerbType } from '../../src/types/graphTypes.js'

let seq = 0
const freshId = (): string =>
  `00000000-0000-4000-8000-${(++seq).toString(16).padStart(12, '0')}`

describe('read-your-writes contract (single-writer, no await between write and read)', () => {
  let dir: string
  let brain: any

  beforeEach(async () => {
    process.env.BRAINY_DETERMINISTIC_EMBEDDINGS = 'true'
    dir = fs.mkdtempSync(path.join(os.tmpdir(), 'brainy-ryw-'))
    brain = new Brainy({
      requireSubtype: false,
      storage: { type: 'filesystem', path: dir },
      dimensions: 384,
      silent: true
    })
    await brain.init()
  })

  afterEach(async () => {
    await brain.close()
    fs.rmSync(dir, { recursive: true, force: true })
  })

  it('add(): the entity is immediately readable by id, metadata filter, AND vector search', async () => {
    const id = freshId()
    const marker = `ryw-${id}`
    await brain.add({ id, data: 'read your writes probe alpha', type: NounType.Concept, metadata: { marker } })

    // id read (write-through cache / canonical)
    expect(await brain.get(id)).not.toBeNull()
    // metadata-filter read (metadata index committed in-transaction)
    const byMeta = await brain.find({ where: { marker }, limit: 10 })
    expect(byMeta.map((r: any) => r.id)).toContain(id)
    // vector read (HNSW committed in-transaction)
    const byVector = await brain.find({ query: 'read your writes probe alpha', limit: 10 })
    expect(byVector.map((r: any) => r.id)).toContain(id)
    // type-filter read
    const byType = await brain.find({ type: NounType.Concept, limit: 100 })
    expect(byType.map((r: any) => r.id)).toContain(id)
  })

  it('update(): the new metadata is immediately queryable and the old value is immediately gone', async () => {
    const id = freshId()
    await brain.add({ id, data: 'updatable', type: NounType.Thing, metadata: { phase: 'before' } })
    await brain.update({ id, metadata: { phase: 'after' } })

    const after = await brain.find({ where: { phase: 'after' }, limit: 10 })
    expect(after.map((r: any) => r.id)).toContain(id)
    const before = await brain.find({ where: { phase: 'before' }, limit: 10 })
    expect(before.map((r: any) => r.id)).not.toContain(id)
  })

  it('relate(): the edge is immediately traversable', async () => {
    const a = await brain.add({ id: freshId(), data: 'A', type: NounType.Thing })
    const b = await brain.add({ id: freshId(), data: 'B', type: NounType.Thing })
    await brain.relate({ from: a, to: b, type: VerbType.RelatedTo })
    expect((await brain.related(a)).map((r: any) => r.to)).toContain(b)
  })

  it('remove(): the entity is immediately gone from id, metadata, and vector reads', async () => {
    const id = freshId()
    const marker = `gone-${id}`
    await brain.add({ id, data: 'to be removed promptly', type: NounType.Thing, metadata: { marker } })
    expect(await brain.get(id)).not.toBeNull()

    await brain.remove(id)
    expect(await brain.get(id)).toBeNull()
    expect((await brain.find({ where: { marker }, limit: 10 })).map((r: any) => r.id)).not.toContain(id)
    expect((await brain.find({ query: 'to be removed promptly', limit: 10 })).map((r: any) => r.id)).not.toContain(id)
  })

  it('transact(): every item of a committed batch is immediately readable by id, index, and traversal', async () => {
    const a = freshId()
    const b = freshId()
    const marker = `batch-${a}`
    const db = await brain.transact([
      { op: 'add', id: a, data: 'batch node A', type: NounType.Thing, metadata: { marker } },
      { op: 'add', id: b, data: 'batch node B', type: NounType.Thing, metadata: { marker } },
      { op: 'relate', from: a, to: b, type: VerbType.Contains }
    ])
    expect(db.generation).toBeGreaterThan(0)

    expect(await brain.get(a)).not.toBeNull()
    expect(await brain.get(b)).not.toBeNull()
    const byMeta = (await brain.find({ where: { marker }, limit: 10 })).map((r: any) => r.id)
    expect(byMeta).toContain(a)
    expect(byMeta).toContain(b)
    expect((await brain.related(a)).map((r: any) => r.to)).toContain(b)
  })

  it('the write returns a generation ({seq}) and a live read observes it — the awaitable-read primitive', async () => {
    const id = freshId()
    const before = brain.generationStore.generation()
    await brain.add({ id, data: 'generation stamped', type: NounType.Thing })
    const after = brain.generationStore.generation()
    // The write advanced the generation…
    expect(after).toBeGreaterThan(before)
    // …and a read pinned to that generation observes the write (asOf ≥ N sees it).
    const pinned = await brain.asOf(after)
    expect(await pinned.get(id)).not.toBeNull()
  })
})
