/**
 * Global shutdown-hook lifecycle — a library must never keep its host process
 * alive. init() registers process-level SIGTERM/SIGINT/beforeExit listeners
 * (once, globally) whose signal handles are ref'd by Node; if they outlive the
 * last brain, a bare script hangs forever after close() (consumer-reported on
 * the GA pair). The contract pinned here:
 *
 *   - first init()  → exactly one listener added per signal
 *   - more inits    → no additional listeners (registered once)
 *   - close()       → listeners survive while OTHER instances remain live
 *   - last close()  → listeners removed, counts back to baseline
 *   - re-init       → hooks re-register (the once-flag resets)
 */
import { describe, it, expect } from 'vitest'
import { Brainy, NounType } from '../../src/index.js'

const SIGNALS = ['SIGTERM', 'SIGINT', 'beforeExit'] as const

function listenerCounts(): Record<string, number> {
  return Object.fromEntries(SIGNALS.map((s) => [s, process.listeners(s as any).length]))
}

function makeBrain(): any {
  return new Brainy({ requireSubtype: false, storage: { type: 'memory' }, plugins: [], silent: true })
}

describe('Shutdown-hook lifecycle (process must exit after the last close)', () => {
  it('registers each hook once on first init and removes ALL of them on the last close', async () => {
    const baseline = listenerCounts()

    const brain = makeBrain()
    await brain.init()
    for (const s of SIGNALS) {
      expect(process.listeners(s as any).length).toBe(baseline[s] + 1) // exactly one each
    }

    await brain.close()
    expect(listenerCounts()).toEqual(baseline) // gone — the loop can drain
  })

  it('keeps the hooks while any other instance is live; only the LAST close deregisters', async () => {
    const baseline = listenerCounts()

    const first = makeBrain()
    const second = makeBrain()
    await first.init()
    await second.init()
    for (const s of SIGNALS) {
      expect(process.listeners(s as any).length).toBe(baseline[s] + 1) // once globally, not per instance
    }

    await first.close()
    for (const s of SIGNALS) {
      expect(process.listeners(s as any).length).toBe(baseline[s] + 1) // second is still live
    }

    await second.close()
    expect(listenerCounts()).toEqual(baseline)
  })

  it('re-registers after a full close/re-init cycle (the once-flag resets)', async () => {
    const baseline = listenerCounts()

    const first = makeBrain()
    await first.init()
    await first.close()
    expect(listenerCounts()).toEqual(baseline)

    const second = makeBrain()
    await second.init()
    const id = await second.add({ data: 'still fully functional', type: NounType.Concept })
    expect(id).toBeTruthy()
    for (const s of SIGNALS) {
      expect(process.listeners(s as any).length).toBe(baseline[s] + 1)
    }
    await second.close()
    expect(listenerCounts()).toEqual(baseline)
  })
})
