// src/services/worker.test.ts
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

// --- Hoisted Mocks ---
const mocks = vi.hoisted(() => {
  return {
    gracefulShutdown: vi.fn(),
    logger: {
      info: vi.fn(),
      error: vi.fn(),
      warn: vi.fn(),
      debug: vi.fn(),
    },
    // Mock process events
    processOn: vi.fn(),
    processExit: vi.fn(),
  };
});

// --- Mock Modules ---
vi.mock('./workers.server', () => ({
  gracefulShutdown: mocks.gracefulShutdown,
}));

vi.mock('./logger.server', () => ({
  logger: mocks.logger,
}));

describe('Worker Entry Point', () => {
  let originalProcessOn: typeof process.on;
  let originalProcessExit: typeof process.exit;
  let eventHandlers: Record<string, (...args: any[]) => void> = {};

  beforeEach(() => {
    vi.clearAllMocks();
    vi.resetModules(); // This is key to re-run the top-level code in worker.ts

    // Reset default mock implementations
    mocks.gracefulShutdown.mockResolvedValue(undefined);

    // Spy on and mock process methods
    originalProcessOn = process.on;
    originalProcessExit = process.exit;

    // Capture event handlers registered with process.on
    eventHandlers = {};
    process.on = vi.fn((event, listener) => {
      eventHandlers[event] = listener;
      return process;
    }) as any;

    process.exit = mocks.processExit as any;
  });

  afterEach(() => {
    // Restore original process methods
    process.on = originalProcessOn;
    process.exit = originalProcessExit;
  });

  it('should log initialization messages on import', async () => {
    // Act: Import the module to trigger top-level code
    await import('./worker');

    // Assert
    expect(mocks.logger.info).toHaveBeenCalledWith('[Worker] Initializing worker process...');
    expect(mocks.logger.info).toHaveBeenCalledWith(
      '[Worker] Worker process is running and listening for jobs.',
    );
  });

  it('should register handlers for SIGINT, SIGTERM, uncaughtException, and unhandledRejection', async () => {
    // Act
    await import('./worker');

    // Assert
    expect(process.on).toHaveBeenCalledWith('SIGINT', expect.any(Function));
    expect(process.on).toHaveBeenCalledWith('SIGTERM', expect.any(Function));
    expect(process.on).toHaveBeenCalledWith('uncaughtException', expect.any(Function));
    expect(process.on).toHaveBeenCalledWith('unhandledRejection', expect.any(Function));
  });

  describe('Shutdown Handling', () => {
    it('should call gracefulShutdown on SIGINT', async () => {
      // Arrange
      await import('./worker');
      const sigintHandler = eventHandlers['SIGINT'];
      expect(sigintHandler).toBeDefined();

      // Act
      sigintHandler();

      // Assert
      expect(mocks.logger.info).toHaveBeenCalledWith(
        '[Worker] Received SIGINT. Initiating graceful shutdown...',
      );
      expect(mocks.gracefulShutdown).toHaveBeenCalledWith('SIGINT');
    });

    it('should call gracefulShutdown on SIGTERM', async () => {
      // Arrange
      await import('./worker');
      const sigtermHandler = eventHandlers['SIGTERM'];
      expect(sigtermHandler).toBeDefined();

      // Act
      sigtermHandler();

      // Assert
      expect(mocks.logger.info).toHaveBeenCalledWith(
        '[Worker] Received SIGTERM. Initiating graceful shutdown...',
      );
      expect(mocks.gracefulShutdown).toHaveBeenCalledWith('SIGTERM');
    });

    it('should log an error and exit if gracefulShutdown rejects', async () => {
      // Arrange
      const shutdownError = new Error('Shutdown failed');
      mocks.gracefulShutdown.mockRejectedValue(shutdownError);
      await import('./worker');
      const sigintHandler = eventHandlers['SIGINT'];

      // Act
      // The handler catches the rejection, so we don't need to wrap this in expect().rejects
      await sigintHandler();

      // Assert
      expect(mocks.logger.error).toHaveBeenCalledWith(
        { err: shutdownError },
        '[Worker] Error during shutdown.',
      );
      expect(mocks.processExit).toHaveBeenCalledWith(1);
    });
  });

  describe('Error Handling', () => {
    it('should log uncaught exceptions', async () => {
      // Arrange
      await import('./worker');
      const exceptionHandler = eventHandlers['uncaughtException'];
      expect(exceptionHandler).toBeDefined();
      const testError = new Error('Test uncaught exception');

      // Act
      exceptionHandler(testError);

      // Assert
      expect(mocks.logger.error).toHaveBeenCalledWith(
        { err: testError },
        '[Worker] Uncaught exception',
      );
    });

    it('should log unhandled promise rejections', async () => {
      // Arrange
      await import('./worker');
      const rejectionHandler = eventHandlers['unhandledRejection'];
      expect(rejectionHandler).toBeDefined();
      const testReason = 'Promise rejected';
      const testPromise = Promise.reject(testReason);
      // We must handle this rejection in the test to prevent Vitest/Node from flagging it as unhandled
      testPromise.catch((err) => {
        console.log('Handled expected test rejection to prevent test runner error:', err);
      });

      // Act
      rejectionHandler(testReason, testPromise);

      // Assert
      expect(mocks.logger.error).toHaveBeenCalledWith(
        { reason: testReason, promise: testPromise },
        '[Worker] Unhandled Rejection',
      );
    });
  });
});