import { beforeEach, describe, expect, it, mock } from 'bun:test';
import { Queue } from '../src/queue';
import type { JobData, QueueWorkerConfig, RedisConfig } from '../src/types';

describe('Queue Class', () => {
  const mockRedisConfig: RedisConfig = {
    host: 'localhost',
    port: 6379,
  };

  describe('basic functionality', () => {
    it('should create queue with minimal config', () => {
      const queue = new Queue('test-queue', mockRedisConfig);
      expect(queue).toBeDefined();
      expect(queue.getName()).toBe('test-queue');
    });

    it('should create queue with default job options', () => {
      const defaultJobOptions = {
        attempts: 5,
        backoff: { type: 'exponential' as const, delay: 2000 },
      };

      const queue = new Queue('test-queue', mockRedisConfig, defaultJobOptions);
      expect(queue).toBeDefined();
      expect(queue.getName()).toBe('test-queue');
    });

    it('should create queue with custom logger', () => {
      const mockLogger = {
        info: mock(() => {}),
        error: mock(() => {}),
        warn: mock(() => {}),
        debug: mock(() => {}),
        trace: mock(() => {}),
      };

      const queue = new Queue('test-queue', mockRedisConfig, {}, {}, mockLogger);
      expect(queue).toBeDefined();
    });

    it('should create queue with worker config', () => {
      const workerConfig: QueueWorkerConfig = {
        workers: 2,
        concurrency: 5,
        startWorker: false, // Don't actually start workers
        serviceName: 'test-service',
      };

      const queue = new Queue('test-queue', mockRedisConfig, {}, workerConfig);
      expect(queue).toBeDefined();
    });
  });

  describe('queue naming and utilities', () => {
    it('should return queue name', () => {
      const queue = new Queue('my-test-queue', mockRedisConfig);
      expect(queue.getName()).toBe('my-test-queue');
    });

    it('should get bull queue instance', () => {
      const queue = new Queue('test-queue', mockRedisConfig);
      const bullQueue = queue.getBullQueue();
      expect(bullQueue).toBeDefined();
    });

    it('should create child logger with logger that supports child', () => {
      const mockChildLogger = {
        info: mock(() => {}),
        error: mock(() => {}),
        warn: mock(() => {}),
        debug: mock(() => {}),
        trace: mock(() => {}),
      };

      const mockLogger = {
        info: mock(() => {}),
        error: mock(() => {}),
        warn: mock(() => {}),
        debug: mock(() => {}),
        trace: mock(() => {}),
        child: mock(() => mockChildLogger),
      };

      const queue = new Queue('test-queue', mockRedisConfig, {}, {}, mockLogger);
      const childLogger = queue.createChildLogger('batch', { batchId: '123' });

      expect(childLogger).toBe(mockChildLogger);
      expect(mockLogger.child).toHaveBeenCalledWith('batch', { batchId: '123' });
    });

    it('should fallback to main logger if child not supported', () => {
      const mockLogger = {
        info: mock(() => {}),
        error: mock(() => {}),
        warn: mock(() => {}),
        debug: mock(() => {}),
        trace: mock(() => {}),
      };

      const queue = new Queue('test-queue', mockRedisConfig, {}, {}, mockLogger);
      const childLogger = queue.createChildLogger('batch', { batchId: '123' });

      expect(childLogger).toBe(mockLogger);
    });
  });

  describe('worker count methods', () => {
    it('should get worker count when no workers', () => {
      const queue = new Queue('test-queue', mockRedisConfig);
      expect(queue.getWorkerCount()).toBe(0);
    });

    it('should handle worker count with workers config', () => {
      const workerConfig: QueueWorkerConfig = {
        workers: 3,
        startWorker: false, // Don't actually start
      };

      const queue = new Queue('test-queue', mockRedisConfig, {}, workerConfig);
      // Workers aren't actually started with startWorker: false
      expect(queue.getWorkerCount()).toBe(0);
    });
  });
});
