/**
 * Edge-case regression tests for three long-standing query/aggregation items
 * (#26 missing-field predicate, #27 type[]+orderBy first-call completeness,
 * #24 percentile/median/distinctCount aggregation ops) — verified against the 8.0 engine.
 */

import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { Brainy } from '../../../src/brainy'
import { createTestConfig } from '../../helpers/test-factory'
import { NounType } from '../../../src/types/graphTypes'

describe('find + aggregation edge cases', () => {
  let brain: Brainy

  beforeEach(async () => {
    brain = new Brainy(createTestConfig())
    await brain.init()
  })

  afterEach(async () => {
    await brain.close()
  })

  // #26
  it('find({ where: { field: { missing: true } } }) matches a never-registered field', async () => {
    const a = await brain.add({ data: 'a', type: NounType.Thing, subtype: 'x', metadata: { name: 'A' } })
    const b = await brain.add({
      data: 'b',
      type: NounType.Thing,
      subtype: 'x',
      metadata: { name: 'B', status: 'open' }
    })

    // Never-registered field → every entity "misses" it.
    const neverReg = await brain.find({ where: { neverRegistered: { missing: true } }, limit: 100 })
    const neverRegIds = neverReg.map((r) => r.id)
    expect(neverRegIds).toContain(a)
    expect(neverRegIds).toContain(b)

    // Registered field, absent on one entity.
    const noStatus = await brain.find({ where: { status: { missing: true } }, limit: 100 })
    const noStatusIds = noStatus.map((r) => r.id)
    expect(noStatusIds).toContain(a) // a has no status
    expect(noStatusIds).not.toContain(b) // b has status
  })

  // #27
  it('find({ type: [...], orderBy, limit }) returns the full set on the first call after mutate+query+get', async () => {
    const ids: string[] = []
    for (let i = 0; i < 6; i++) {
      ids.push(
        await brain.add({
          data: `e${i}`,
          type: i % 2 === 0 ? NounType.Document : NounType.Person,
          subtype: 'x',
          metadata: { n: i }
        })
      )
    }
    await brain.update({ id: ids[0], metadata: { n: 100 }, merge: true }) // mutate
    await brain.find({ query: 'e' }) // query
    await brain.get(ids[1]) // get

    const r = await brain.find({
      type: [NounType.Document, NounType.Person],
      orderBy: 'n',
      limit: 10
    })
    expect(r).toHaveLength(6) // all six, not a partial page
  })

  // #24
  it('queryAggregate supports percentile (median) and distinctCount', async () => {
    brain.defineAggregate({
      name: 'amounts',
      source: { type: NounType.Event },
      groupBy: ['subtype'], // all 'txn' → one group
      metrics: {
        median: { op: 'percentile', field: 'amount', p: 0.5 },
        distinctCats: { op: 'distinctCount', field: 'category' }
      }
    })

    const rows0: Array<[number, string]> = [
      [10, 'a'],
      [20, 'b'],
      [30, 'a'],
      [40, 'c'],
      [50, 'b']
    ]
    for (const [amount, category] of rows0) {
      await brain.add({ data: 'evt', type: NounType.Event, subtype: 'txn', metadata: { amount, category } })
    }

    const rows = await brain.queryAggregate('amounts', {})
    expect(rows.length).toBeGreaterThanOrEqual(1)
    const m = (rows[0] as any).metrics ?? rows[0]
    expect(m.median).toBeGreaterThanOrEqual(20)
    expect(m.median).toBeLessThanOrEqual(40)
    expect(m.distinctCats).toBe(3) // a, b, c
  })
})
