// ABOUTME: BullMQ Queue definitions and Redis connection for all job types
// ABOUTME: Exports named queues and a shared IORedis connection

import { Queue } from 'bullmq';
import IORedis from 'ioredis';
import config from '../config';
import logger from '../utils/logger';

// ============================================================================
// Queue Name Constants
// ============================================================================

export const QUEUE_NAMES = {
  MORNING_SCRAPE: 'morning-scrape',
  PRE_RACE: 'pre-race',
  POST_RACE: 'post-race',
  CLEANUP: 'cleanup',
  DEAD_LETTER: 'dead-letter',
} as const;

// ============================================================================
// Redis Connection (shared between Queue producers and Worker consumers)
// ============================================================================

export const connection = new IORedis(config.redis.url, {
  maxRetriesPerRequest: null,
  enableOfflineQueue: false,
  retryStrategy(times) {
    const delay = Math.min(times * 100, 3000);
    return delay;
  },
});

connection.on('connect', () => {
  logger.info('BullMQ Redis connected');
});

connection.on('error', (err) => {
  logger.error({ error: err.message }, 'BullMQ Redis connection error');
});

// ============================================================================
// Queue Instances
// ============================================================================

/**
 * Morning scrape queue - daily 6 AM scrape of all racing meetings
 * Job data: { dates?: string[], categories?: string[], countries?: string[] }
 */
export const morningScrapeQueue = new Queue(QUEUE_NAMES.MORNING_SCRAPE, {
  connection,
  defaultJobOptions: {
    attempts: 3,
    backoff: {
      type: 'exponential',
      delay: 30000, // 30s initial backoff
    },
    removeOnComplete: 100,
    removeOnFail: 50,
  },
});

/**
 * Pre-race update queue - odds refresh at T-60 and T-15
 * Job data: { raceIds: string[], scrapeType: 'pre_race_t60' | 'pre_race_t15' }
 */
export const preRaceQueue = new Queue(QUEUE_NAMES.PRE_RACE, {
  connection,
  defaultJobOptions: {
    attempts: 2,
    backoff: {
      type: 'fixed',
      delay: 60000, // 1 minute
    },
    removeOnComplete: 200,
    removeOnFail: 50,
  },
});

/**
 * Post-race results queue - results + dividends collection
 * Job data: { raceIds: string[], retryCount?: number }
 */
export const postRaceQueue = new Queue(QUEUE_NAMES.POST_RACE, {
  connection,
  defaultJobOptions: {
    attempts: 3,
    backoff: {
      type: 'fixed',
      delay: 300000, // 5 minutes
    },
    removeOnComplete: 200,
    removeOnFail: 50,
  },
});

/**
 * Cleanup queue - daily maintenance of old records
 * Job data: {}
 */
export const cleanupQueue = new Queue(QUEUE_NAMES.CLEANUP, {
  connection,
  defaultJobOptions: {
    attempts: 1,
    removeOnComplete: true,
    removeOnFail: true,
  },
});

/**
 * Dead letter queue - captures jobs from all queues after max retries are exhausted
 * Job data: { ...originalJobData, _failedReason, _sourceQueue, _failedAt, _originalJobId, _stackTrace? }
 *
 * Keeps all jobs indefinitely for debugging/auditing. Consumers should process and
 * archive or alert on dead letter entries to prevent unbounded growth.
 */
export const deadLetterQueue = new Queue(QUEUE_NAMES.DEAD_LETTER, {
  connection,
  defaultJobOptions: {
    attempts: 1,       // Dead letter processing itself should not retry
    removeOnComplete: false, // Keep for durable auditing
    removeOnFail: false,     // Keep even if dead letter worker fails
  },
});

// ============================================================================
// Helper: Close all queues and Redis connection gracefully
// ============================================================================

export async function closeQueues(): Promise<void> {
  logger.info('Closing BullMQ queues...');

  await Promise.allSettled([
    morningScrapeQueue.close(),
    preRaceQueue.close(),
    postRaceQueue.close(),
    cleanupQueue.close(),
    deadLetterQueue.close(),
  ]);

  await connection.quit();

  logger.info('BullMQ queues closed');
}
