import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { SHUTDOWN_TIMEOUT_MS } from '../../server/utils/constants.js'
import { registerCleanup, graceful, initShutdownHandlers, clearCleanup } from '../../server/utils/shutdown.js'

describe('shutdown integration', () => {
  /** @type {import('vitest').MockInstance} */
  let exitSpy
  /** @type {typeof process.on} */
  let originalOn

  beforeEach(() => {
    clearCleanup()
    exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => {})
    originalOn = process.on
  })

  afterEach(() => {
    exitSpy.mockRestore()
    process.on = originalOn
    clearCleanup()
    vi.useRealTimers()
  })

  it('initializes signal handlers', () => {
    const handlers = {}
    process.on = vi.fn((signal, handler) => {
      handlers[signal] = handler
    })
    initShutdownHandlers()
    expect(process.on).toHaveBeenCalledWith('SIGTERM', expect.any(Function))
    expect(process.on).toHaveBeenCalledWith('SIGINT', expect.any(Function))
  })

  it('signal handler calls graceful', async () => {
    const handlers = {}
    process.on = vi.fn((signal, handler) => {
      handlers[signal] = handler
    })
    initShutdownHandlers()
    const sigtermHandler = handlers.SIGTERM
    expect(sigtermHandler).toBeDefined()
    sigtermHandler()
    await vi.waitFor(() => {
      expect(exitSpy).toHaveBeenCalled()
    })
  })

  it('signal handler handles graceful error', async () => {
    const handlers = {}
    process.on = vi.fn((signal, handler) => {
      handlers[signal] = handler
    })
    initShutdownHandlers()
    const sigintHandler = handlers.SIGINT
    vi.spyOn(console, 'error').mockImplementation(() => {})
    registerCleanup(async () => {
      throw new Error('Force error')
    })
    sigintHandler()
    await vi.waitFor(() => {
      expect(exitSpy).toHaveBeenCalled()
    })
  })

  it('covers timeout path in graceful', async () => {
    vi.useFakeTimers()
    registerCleanup(async () => {
      await new Promise(resolve => setTimeout(resolve, SHUTDOWN_TIMEOUT_MS + 5_000))
    })
    graceful()
    await vi.advanceTimersByTimeAsync(SHUTDOWN_TIMEOUT_MS + 1)
    expect(exitSpy).toHaveBeenCalledWith(1)
  })

  it('covers graceful catch block', async () => {
    registerCleanup(async () => {
      throw new Error('Test error')
    })
    await graceful()
    expect(exitSpy).toHaveBeenCalled()
  })
})
