/**
 * @module tests/unit/brainy/degraded-reads-surfaced
 * @description Finding 10: a known-degraded derived index must be SURFACED, not
 * silently served as authoritative. Two degraded sources:
 *   (a) a non-fatal index rebuild failure at init() (`_indexRebuildFailed`), and
 *   (b) an adopt-forward failed-rollback recovery (`commitSingleOp`'s `degraded`
 *       ids, previously dropped on the floor by persistSingleOp).
 * Both now fold into checkHealth()/validateIndexConsistency() and emit ONE loud
 * read-path warning per degraded window; repairIndex() reconciles + clears them.
 *
 * The private fields are the observable contract of the fix, so the test drives
 * them directly (a real failed rollback is exercised elsewhere).
 */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { Brainy } from '../../../src/brainy.js'
import { NounType } from '../../../src/types/graphTypes.js'
import { prodLog } from '../../../src/utils/logger.js'

const UUID = (suffix: string): string => `00000000-0000-4000-8000-0000000000${suffix}`

describe('Finding 10 — degraded derived-index state is surfaced on reads', () => {
  beforeEach(() => {
    process.env.BRAINY_DETERMINISTIC_EMBEDDINGS = 'true'
  })
  afterEach(() => vi.restoreAllMocks())

  it('checkHealth() reports adopt-forward degraded ids as unhealthy', async () => {
    const brain = new Brainy({ storage: { type: 'memory' }, dimensions: 384, requireSubtype: false })
    await brain.init()
    ;(brain as any)._indexDegradedIds.add(UUID('de'))

    const health = await brain.checkHealth()
    expect(health.healthy).toBe(false)
    expect(health.recommendation).toMatch(/repairIndex\(\)/)
  })

  it('find()/get() warn loudly while degraded, ONCE, then repairIndex() clears it', async () => {
    const warn = vi.spyOn(prodLog, 'warn').mockImplementation(() => {})
    const brain = new Brainy({ storage: { type: 'memory' }, dimensions: 384, requireSubtype: false })
    await brain.init()
    await brain.add({ id: UUID('a1'), data: 'x', type: NounType.Document })
    ;(brain as any)._indexRebuildFailed = new Error('rebuild boom')

    await brain.find({ type: NounType.Document })
    await brain.get(UUID('a1')) // second read: must NOT double-warn
    const degradedWarns = warn.mock.calls.filter((c) =>
      String(c[0]).includes('derived index is INCOMPLETE')
    )
    expect(degradedWarns.length).toBe(1)

    await brain.repairIndex()
    expect((brain as any)._indexRebuildFailed).toBeNull()
    expect((brain as any)._indexDegradedIds.size).toBe(0)

    warn.mockClear()
    await brain.find({ type: NounType.Document })
    expect(warn.mock.calls.filter((c) => String(c[0]).includes('INCOMPLETE')).length).toBe(0)
  })

  it('persistSingleOp records receipt.degraded (widened return type, not dropped)', async () => {
    const brain = new Brainy({ storage: { type: 'memory' }, dimensions: 384, requireSubtype: false })
    await brain.init()
    // Simulate a degraded receipt by wrapping the generation store's commitSingleOp.
    const gs: any = (brain as any).generationStore
    const realCommit = gs.commitSingleOp.bind(gs)
    vi.spyOn(gs, 'commitSingleOp').mockImplementation(async (args: any) => {
      const r = await realCommit(args)
      return { ...r, degraded: [...(args.touched.nouns ?? [])] }
    })

    await brain.add({ id: UUID('b2'), data: 'y', type: NounType.Document })
    expect((brain as any)._indexDegradedIds.size).toBeGreaterThan(0)
    expect((brain as any)._indexDegradedIds.has(UUID('b2'))).toBe(true)

    const health = await brain.checkHealth()
    expect(health.healthy).toBe(false)
    expect(health.recommendation).toMatch(/repairIndex\(\)/)
  })
})
