/**
 * @module tests/unit/brainy/metadata-provider-contract
 * @description Brainy-side wiring of the two metadata-provider contract additions
 * confirmed with cor for the lockstep:
 *
 *  1. `probeConsistency()` — an OPTIONAL O(1) cold-open consistency sampler. On the
 *     first read, brainy calls it once; on `false` it self-heals via
 *     `detectAndRepairCorruption()` (the metadata counterpart of the graph cold-load
 *     guard). The native provider implements it; the JS index omits it (no-op).
 *  2. `getIdsForFilter(filter, opts?)` — brainy passes a page bound on the UNSORTED
 *     `find({ type, where, limit })` path so a native provider can early-stop. The JS
 *     index ignores `opts`.
 *
 * These are unit tests of brainy's CALL behaviour (the real end-to-end honoring is
 * exercised by cor's combined matrix); they inject probe/spy hooks onto the live JS
 * metadata index, which has neither method by default.
 */
import { describe, it, expect, beforeEach } from 'vitest'
import { Brainy } from '../../../src/brainy'
import { NounType } from '../../../src/types/graphTypes'

describe('metadata-provider contract wiring (probeConsistency + getIdsForFilter opts)', () => {
  let brain: Brainy<any>
  let mi: any

  beforeEach(async () => {
    brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' }, silent: true })
    await brain.init()
    await brain.add({ data: 'a', type: NounType.Thing, metadata: { kind: 'x' } })
    await brain.add({ data: 'b', type: NounType.Thing, metadata: { kind: 'y' } })
    mi = (brain as any).metadataIndex
    ;(brain as any)._metadataConsistencyProbed = false // reset the one-shot guard
  })

  it('calls probeConsistency once on cold open and self-heals via detectAndRepairCorruption on false', async () => {
    let probes = 0
    let repairs = 0
    mi.probeConsistency = async () => { probes++; return false } // corrupt → must repair
    const origRepair = mi.detectAndRepairCorruption.bind(mi)
    mi.detectAndRepairCorruption = async () => { repairs++; return origRepair() }

    await brain.find({ where: { kind: 'x' } })
    expect(probes).toBe(1)
    expect(repairs).toBe(1)

    // Second read must NOT re-probe (once per brain).
    await brain.find({ where: { kind: 'y' } })
    expect(probes).toBe(1)
    expect(repairs).toBe(1)
  })

  it('does NOT repair when the probe reports healthy', async () => {
    let repairs = 0
    mi.probeConsistency = async () => true // clean
    const origRepair = mi.detectAndRepairCorruption.bind(mi)
    mi.detectAndRepairCorruption = async () => { repairs++; return origRepair() }

    await brain.find({ where: { kind: 'x' } })
    expect(repairs).toBe(0)
  })

  it('a probe failure never breaks the read (best-effort, retried next time)', async () => {
    let probes = 0
    mi.probeConsistency = async () => { probes++; throw new Error('probe boom') }

    // The read still succeeds despite the throwing probe.
    const rows = await brain.find({ where: { kind: 'x' } })
    expect(rows.length).toBe(1)
    expect(probes).toBe(1)
    // Guard reset on failure → the next read retries the probe.
    await brain.find({ where: { kind: 'y' } })
    expect(probes).toBe(2)
  })

  it('passes a page bound to getIdsForFilter on the unsorted find path (offset 0, brainy re-windows)', async () => {
    let seenOpts: { limit?: number; offset?: number } | undefined
    const orig = mi.getIdsForFilter.bind(mi)
    mi.getIdsForFilter = async (filter: any, opts?: { limit?: number; offset?: number }) => {
      seenOpts = opts
      return orig(filter, opts)
    }

    const rows = await brain.find({ where: { kind: 'x' }, limit: 5 })
    expect(rows.length).toBe(1) // result correctness preserved (JS ignores opts)
    expect(seenOpts).toBeDefined()
    expect(typeof seenOpts!.limit).toBe('number')
    expect(seenOpts!.limit).toBeGreaterThanOrEqual(5)
    expect(seenOpts!.offset).toBe(0) // brainy applies offset itself via slice
  })
})
