interface WorkerStartupDependencies {
  initializeConfig: () => Promise<void>;
  waitForQueue: () => Promise<unknown>;
  startHeartbeat: () => void;
  startOutboxDrainer?: () => Promise<void> | void;
  onConfigFallback?: (_error: unknown) => Promise<void> | void;
}

interface WorkerShutdownDependencies {
  closeQueue: () => Promise<unknown>;
  stopHeartbeat: () => Promise<unknown>;
  closeConfigSubscription: () => Promise<unknown>;
  disconnectDatabase: () => Promise<unknown>;
  stopOutboxDrainer?: () => Promise<unknown> | unknown;
  drainTimeoutMs?: number;
}

export async function startWorkerRuntime(dependencies: WorkerStartupDependencies): Promise<void> {
  try {
    await dependencies.initializeConfig();
  } catch (error) {
    await dependencies.onConfigFallback?.(error);
  }

  await dependencies.waitForQueue();
  await dependencies.startOutboxDrainer?.();
  dependencies.startHeartbeat();
}

export async function shutdownWorkerRuntime(
  dependencies: WorkerShutdownDependencies
): Promise<void> {
  const failures: unknown[] = [];
  let queueDrained = false;

  if (dependencies.stopOutboxDrainer) {
    try {
      await dependencies.stopOutboxDrainer();
    } catch (error) {
      failures.push(error);
    }
  }

  try {
    const drainTimeoutMs = dependencies.drainTimeoutMs ?? 25_000;
    let timeout: NodeJS.Timeout | undefined;
    try {
      await Promise.race([
        dependencies.closeQueue(),
        new Promise<never>((_, reject) => {
          timeout = setTimeout(
            () => reject(new Error(`Worker queue drain timed out after ${drainTimeoutMs}ms`)),
            drainTimeoutMs
          );
        }),
      ]);
      queueDrained = true;
    } finally {
      if (timeout) {
        clearTimeout(timeout);
      }
    }
  } catch (error) {
    failures.push(error);
  }

  const redisClosures = await Promise.allSettled([
    dependencies.stopHeartbeat(),
    dependencies.closeConfigSubscription(),
  ]);
  for (const result of redisClosures) {
    if (result.status === 'rejected') {
      failures.push(result.reason);
    }
  }

  if (queueDrained) {
    try {
      await dependencies.disconnectDatabase();
    } catch (error) {
      failures.push(error);
    }
  }

  if (failures.length > 0) {
    throw new AggregateError(failures, 'Worker shutdown failed');
  }
}
