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

// --- Hoisted Mocks ---
const mocks = vi.hoisted(() => {
  const createMockQueue = (name: string) => ({
    name,
    close: vi.fn().mockResolvedValue(undefined),
    add: vi.fn(),
  });

  return {
    flyerQueue: createMockQueue('flyer-processing'),
    emailQueue: createMockQueue('email-sending'),
    analyticsQueue: createMockQueue('analytics-reporting'),
    weeklyAnalyticsQueue: createMockQueue('weekly-analytics-reporting'),
    cleanupQueue: createMockQueue('file-cleanup'),
    tokenCleanupQueue: createMockQueue('token-cleanup'),
    redisConnection: {
      quit: vi.fn().mockResolvedValue('OK'),
    },
    logger: {
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
      debug: vi.fn(),
    },
  };
});

// --- Mock Modules ---
vi.mock('./queues.server', () => ({
  flyerQueue: mocks.flyerQueue,
  emailQueue: mocks.emailQueue,
  analyticsQueue: mocks.analyticsQueue,
  weeklyAnalyticsQueue: mocks.weeklyAnalyticsQueue,
  cleanupQueue: mocks.cleanupQueue,
  tokenCleanupQueue: mocks.tokenCleanupQueue,
}));

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

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

// --- Test ---
describe('Queue Service (API Shutdown)', () => {
  let gracefulShutdown: (signal: string) => Promise<void>;
  let processExitSpy: Mock;

  beforeEach(async () => {
    vi.clearAllMocks();
    vi.resetModules();

    // Spy on process.exit and prevent it from actually exiting
    processExitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never);

    // Dynamically import the module under test
    const queueService = await import('./queueService.server');
    gracefulShutdown = queueService.gracefulShutdown;
  });

  afterEach(() => {
    processExitSpy.mockRestore();
  });

  it('should attempt to close all queues and the redis connection on shutdown', async () => {
    await gracefulShutdown('SIGINT');

    expect(mocks.flyerQueue.close).toHaveBeenCalledTimes(1);
    expect(mocks.emailQueue.close).toHaveBeenCalledTimes(1);
    expect(mocks.analyticsQueue.close).toHaveBeenCalledTimes(1);
    expect(mocks.cleanupQueue.close).toHaveBeenCalledTimes(1);
    expect(mocks.weeklyAnalyticsQueue.close).toHaveBeenCalledTimes(1);
    expect(mocks.tokenCleanupQueue.close).toHaveBeenCalledTimes(1);
    expect(mocks.redisConnection.quit).toHaveBeenCalledTimes(1);
  });

  it('should log success and exit with code 0 if all resources close successfully', async () => {
    await gracefulShutdown('SIGINT');

    expect(mocks.logger.info).toHaveBeenCalledWith(
      '[Shutdown] All queues and connections closed successfully.',
    );
    expect(processExitSpy).toHaveBeenCalledWith(0);
  });

  it('should log a warning and exit with code 1 if a queue fails to close', async () => {
    const closeError = new Error('Queue failed to close');
    mocks.emailQueue.close.mockRejectedValue(closeError);

    await gracefulShutdown('SIGTERM');

    expect(mocks.logger.error).toHaveBeenCalledWith(
      { err: closeError, resource: 'emailQueue' },
      '[Shutdown] Error closing resource.',
    );
    expect(mocks.logger.warn).toHaveBeenCalledWith('[Shutdown] Graceful shutdown completed with errors.');
    expect(processExitSpy).toHaveBeenCalledWith(1);
  });

  it('should log a warning and exit with code 1 if the redis connection fails to close', async () => {
    const redisError = new Error('Redis quit failed');
    mocks.redisConnection.quit.mockRejectedValue(redisError);

    await gracefulShutdown('SIGTERM');

    expect(mocks.logger.error).toHaveBeenCalledWith(
      { err: redisError, resource: 'redisConnection' },
      '[Shutdown] Error closing resource.',
    );
    expect(mocks.logger.warn).toHaveBeenCalledWith('[Shutdown] Graceful shutdown completed with errors.');
    expect(processExitSpy).toHaveBeenCalledWith(1);
  });
});