import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import { handlerRegistry, QueueManager } from '../src';

// Suppress Redis connection errors in tests
process.on('unhandledRejection', (reason, promise) => {
  if (reason && typeof reason === 'object' && 'message' in reason) {
    const message = (reason as Error).message;
    if (
      message.includes('Connection is closed') ||
      message.includes('Connection is in monitoring mode')
    ) {
      // Suppress these specific Redis errors in tests
      return;
    }
  }
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

describe('QueueManager Integration Tests', () => {
  let queueManager: QueueManager;

  // Use local Redis/Dragonfly
  const redisConfig = {
    host: 'localhost',
    port: 6379,
    password: '',
    db: 0,
  };

  beforeEach(() => {
    handlerRegistry.clear();
  });

  afterEach(async () => {
    if (queueManager) {
      try {
        await Promise.race([
          queueManager.shutdown(),
          new Promise((_, reject) => setTimeout(() => reject(new Error('Shutdown timeout')), 3000)),
        ]);
      } catch (error) {
        // Ignore shutdown errors in tests
        console.warn('Shutdown error:', error.message);
      } finally {
        queueManager = null as any;
      }
    }

    // Clear handler registry to prevent conflicts
    handlerRegistry.clear();

    // Add delay to allow connections to close
    await new Promise(resolve => setTimeout(resolve, 100));
  });

  test('should initialize queue manager', async () => {
    queueManager = new QueueManager({
      queueName: 'test-queue',
      redis: redisConfig,
      workers: 1,
      concurrency: 5,
    });

    await queueManager.initialize();
    expect(queueManager.queueName).toBe('test-queue');
  });

  test('should add and process a job', async () => {
    let processedPayload: any;

    // Register handler
    handlerRegistry.register('test-handler', {
      'test-operation': async payload => {
        processedPayload = payload;
        return { success: true, data: payload };
      },
    });

    queueManager = new QueueManager({
      queueName: 'test-queue',
      redis: redisConfig,
      workers: 1,
    });

    await queueManager.initialize();

    // Add job
    const job = await queueManager.add('test-job', {
      handler: 'test-handler',
      operation: 'test-operation',
      payload: { message: 'Hello, Queue!' },
    });

    expect(job.name).toBe('test-job');

    // Wait for processing
    await new Promise(resolve => setTimeout(resolve, 100));

    expect(processedPayload).toEqual({ message: 'Hello, Queue!' });
  });

  test('should handle job errors with retries', async () => {
    let attemptCount = 0;

    handlerRegistry.register('retry-handler', {
      'failing-operation': async () => {
        attemptCount++;
        if (attemptCount < 3) {
          throw new Error(`Attempt ${attemptCount} failed`);
        }
        return { success: true };
      },
    });

    queueManager = new QueueManager({
      queueName: 'test-queue-retry',
      redis: redisConfig,
      workers: 1,
      defaultJobOptions: {
        attempts: 3,
        backoff: {
          type: 'fixed',
          delay: 50,
        },
      },
    });

    await queueManager.initialize();

    const job = await queueManager.add('retry-job', {
      handler: 'retry-handler',
      operation: 'failing-operation',
      payload: {},
    });

    // Wait for retries
    await new Promise(resolve => setTimeout(resolve, 500));

    const completed = await job.isCompleted();
    expect(completed).toBe(true);
    expect(attemptCount).toBe(3);
  });

  test('should collect metrics when enabled', async () => {
    queueManager = new QueueManager({
      queueName: 'test-queue-metrics',
      redis: redisConfig,
      workers: 0,
      enableMetrics: true,
    });

    await queueManager.initialize();

    // Add some jobs
    await queueManager.add('job1', {
      handler: 'test',
      operation: 'test',
      payload: { id: 1 },
    });

    await queueManager.add('job2', {
      handler: 'test',
      operation: 'test',
      payload: { id: 2 },
    });

    const metrics = await queueManager.getMetrics();
    expect(metrics).toBeDefined();
    expect(metrics.waiting).toBeDefined();
    expect(metrics.active).toBeDefined();
    expect(metrics.completed).toBeDefined();
    expect(metrics.failed).toBeDefined();
    expect(metrics.processingTime).toBeDefined();
    expect(metrics.throughput).toBeDefined();
  });

  test('should handle rate limiting when enabled', async () => {
    let processedCount = 0;

    handlerRegistry.register('rate-limited-handler', {
      'limited-op': async () => {
        processedCount++;
        return { processed: true };
      },
    });

    queueManager = new QueueManager({
      queueName: 'test-queue-rate',
      redis: redisConfig,
      workers: 1,
      enableRateLimit: true,
      rateLimitRules: [
        {
          level: 'handler',
          handler: 'rate-limited-handler',
          config: {
            points: 2, // 2 requests
            duration: 1, // per 1 second
          },
        },
      ],
    });

    await queueManager.initialize();

    // Add 3 jobs quickly
    for (let i = 0; i < 3; i++) {
      await queueManager.add(`job${i}`, {
        handler: 'rate-limited-handler',
        operation: 'limited-op',
        payload: { id: i },
      });
    }

    // Wait for processing
    await new Promise(resolve => setTimeout(resolve, 200));

    // Only 2 should be processed due to rate limit
    expect(processedCount).toBe(2);
  });
});
