import type { WorkerDependencies, WorkerService } from './contracts.js'
import type { WorkerEnvironment } from './environment.js'

export function composeWorker(environment: WorkerEnvironment, dependencies: WorkerDependencies): WorkerService {
  const controller = new AbortController()
  let run: Promise<void> | undefined
  let stop: Promise<void> | undefined

  return {
    async start() {
      if (run) return
      if (controller.signal.aborted) throw new Error('Worker cannot restart after shutdown')
      dependencies.health.setStatus('starting')
      dependencies.metrics.increment('worker_starts_total')
      run = Promise.resolve().then(() => dependencies.loop.start({ environment, signal: controller.signal }))
      dependencies.health.setStatus('ready')
      run.catch((error: unknown) => {
        dependencies.health.setStatus('failed', error instanceof Error ? error.message : 'Worker loop failed')
        dependencies.metrics.increment('worker_failures_total')
      })
    },

    async stop(reason = 'shutdown') {
      if (stop) return stop
      stop = (async () => {
        dependencies.health.setStatus('stopping', reason)
        dependencies.metrics.increment('worker_shutdowns_total', { reason })
        controller.abort(reason)
        try {
          if (run) {
            let timer: ReturnType<typeof setTimeout> | undefined
            const timeout = new Promise<never>((_, reject) => {
              timer = (dependencies.setTimer ?? setTimeout)(
                () => reject(new Error(`Worker shutdown exceeded ${environment.shutdownTimeoutMs}ms`)),
                environment.shutdownTimeoutMs,
              )
            })
            try {
              await Promise.race([run, timeout])
            } finally {
              if (timer) (dependencies.clearTimer ?? clearTimeout)(timer)
            }
          }
          dependencies.health.setStatus('stopped', reason)
        } catch (error) {
          const detail = error instanceof Error ? error.message : 'Worker shutdown failed'
          dependencies.health.setStatus('failed', detail)
          dependencies.metrics.increment('worker_failures_total')
          throw error
        }
      })()
      return stop
    },

    health: () => dependencies.health.snapshot(),
  }
}

export interface SignalSource {
  on(event: NodeJS.Signals, listener: () => void): unknown
  off(event: NodeJS.Signals, listener: () => void): unknown
}

export function installGracefulShutdown(
  worker: WorkerService,
  source: SignalSource = process,
  onError: (error: unknown) => void = console.error,
): () => void {
  const handlers = new Map<NodeJS.Signals, () => void>()
  for (const signal of ['SIGINT', 'SIGTERM'] as const) {
    const handler = () => void worker.stop(signal).catch(onError)
    handlers.set(signal, handler)
    source.on(signal, handler)
  }
  return () => {
    for (const [signal, handler] of handlers) source.off(signal, handler)
  }
}
