import { EventEmitter } from 'node:events'
import { describe, expect, it, vi } from 'vitest'
import { composeWorker, installGracefulShutdown } from './composition.js'
import type { HealthStatus, WorkerLoop } from './contracts.js'
import { parseWorkerEnvironment } from './environment.js'
import { createHealthReporter } from './observability.js'

const environment = parseWorkerEnvironment({
  NODE_ENV: 'test',
  PDF2HTML_WORKER_ID: 'worker-test',
  PDF2HTML_WORKER_SHUTDOWN_TIMEOUT_MS: '1000',
})

describe('worker composition shell', () => {
  it('rejects unknown and malformed environment values', () => {
    expect(() => parseWorkerEnvironment({ PDF2HTML_WORKER_ID: 'x', SURPRISE: '1' })).toThrow('Unknown')
    expect(() => parseWorkerEnvironment({ PDF2HTML_WORKER_ID: 'x', PDF2HTML_WORKER_HEALTH_PORT: '0' })).toThrow('between')
    expect(() => parseWorkerEnvironment({})).toThrow('required')
  })

  it('injects dependencies and drains the loop on shutdown', async () => {
    let release!: () => void
    const loop: WorkerLoop = {
      start: vi.fn(async ({ signal }) => new Promise<void>((resolve) => {
        release = resolve
        signal.addEventListener('abort', resolve, { once: true })
      })),
    }
    const statuses: HealthStatus[] = []
    const reporter = createHealthReporter('worker-test')
    const health = { ...reporter, setStatus(status: HealthStatus, detail?: string) { statuses.push(status); reporter.setStatus(status, detail) } }
    const metrics = { increment: vi.fn(), observe: vi.fn() }
    const worker = composeWorker(environment, { loop, health, metrics })

    await worker.start()
    expect(worker.health().status).toBe('ready')
    await worker.stop('test')
    release()
    expect(statuses).toEqual(['starting', 'ready', 'stopping', 'stopped'])
    expect(metrics.increment).toHaveBeenCalledWith('worker_shutdowns_total', { reason: 'test' })
  })

  it('registers and removes graceful-shutdown signal handlers', async () => {
    const source = new EventEmitter()
    const worker = { start: vi.fn(), stop: vi.fn(async () => {}), health: vi.fn() }
    const dispose = installGracefulShutdown(worker, source)
    source.emit('SIGTERM')
    await vi.waitFor(() => expect(worker.stop).toHaveBeenCalledWith('SIGTERM'))
    dispose()
    expect(source.listenerCount('SIGTERM')).toBe(0)
    expect(source.listenerCount('SIGINT')).toBe(0)
  })
})
