import Redis from 'ioredis';
import { createLogger } from '../utils/logger';

const logger = createLogger('RedisService');

/**
 * Redis Service for handling pub/sub operations
 * 
 * This service maintains separate Redis connections for publishing and subscribing
 * to prevent blocking issues and provides a simple interface for pub/sub operations.
 */
class RedisService {
  private publisher: Redis;
  private subscriber: Redis;

  /**
   * Creates a new RedisService with separate connections for publishing and subscribing
   */
  constructor() {
    // Create separate connections for publishing and subscribing
    this.publisher = new Redis({
      host: process.env.REDIS_HOST || 'redis',
      port: Number(process.env.REDIS_PORT) || 6379,
      retryStrategy: (times) => {
        const delay = Math.min(times * 50, 2000);
        logger.info(`Retrying Redis connection in ${delay}ms...`);
        return delay;
      },
    });

    this.subscriber = new Redis({
      host: process.env.REDIS_HOST || 'redis',
      port: Number(process.env.REDIS_PORT) || 6379,
      retryStrategy: (times: number) => {
        const delay = Math.min(times * 50, 2000);
        logger.info(`Retrying Redis connection in ${delay}ms...`);
        return delay;
      },
    });

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

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    this.publisher.on('error', (err: any) => {
      logger.error('Redis publisher error:', err);
    });

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

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    this.subscriber.on('error', (err: any) => {
      logger.error('Redis subscriber error:', err);
    });
  }

  // Publishes a message to the specified Redis channel
  public async publish(channel: string, message: string): Promise<number> {
    try {
      const result = await this.publisher.publish(channel, message);
      logger.info(`Published message to channel ${channel}`);
      return result;
    } catch (err) {
      logger.error(`Error publishing to channel ${channel}:`, err);
      throw err;
    }
  }

  // Subscribes to a Redis channel and executes a callback when a message is received
  public subscribe(channel: string, callback: (message: string) => void): void {
    this.subscriber.subscribe(channel, (err) => {
      if (err) {
        logger.error(`Error subscribing to channel ${channel}:`, err);
        return;
      }
      logger.info(`Subscribed to channel ${channel}`);
    });

    this.subscriber.on('message', (receivedChannel: string, message: string) => {
      if (receivedChannel === channel) {
        logger.info(`Received message from channel ${channel}`);
        callback(message);
      }
    });
  }

  // Unsubscribe from a channel
  public unsubscribe(channel: string): void {
    this.subscriber.unsubscribe(channel, (err) => {
      if (err) {
        logger.error(`Error unsubscribing from channel ${channel}:`, err);
        return;
      }
      logger.info(`Unsubscribed from channel ${channel}`);
    });
  }
}

// Create a singleton instance
const redisService = new RedisService();
export default redisService;
