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

/**
 * Configuración de Redis para blacklist de tokens JWT
 */
const redisConfig = {
  host: process.env.REDIS_HOST || 'localhost',
  port: parseInt(process.env.REDIS_PORT || '6379', 10),
  password: process.env.REDIS_PASSWORD || undefined,

  // Configuración de reconexión
  retryStrategy(times: number) {
    const delay = Math.min(times * 50, 2000);
    return delay;
  },

  // Timeouts
  connectTimeout: 10000,
  maxRetriesPerRequest: 3,

  // Logging de eventos
  lazyConnect: true, // No conectar automáticamente, esperar a connect()
};

/**
 * Cliente Redis para blacklist de tokens
 */
export const redisClient = new Redis(redisConfig);

// Event listeners
redisClient.on('connect', () => {
  logger.info('Redis client connecting...', {
    host: redisConfig.host,
    port: redisConfig.port,
  });
});

redisClient.on('ready', () => {
  logger.info('Redis client ready');
});

redisClient.on('error', (error) => {
  logger.error('Redis client error', {
    error: error.message,
    stack: error.stack,
  });
});

redisClient.on('close', () => {
  logger.warn('Redis connection closed');
});

redisClient.on('reconnecting', () => {
  logger.info('Redis client reconnecting...');
});

/**
 * Inicializa la conexión a Redis
 * @returns Promise<boolean> - true si la conexión fue exitosa
 */
export async function initializeRedis(): Promise<boolean> {
  try {
    await redisClient.connect();

    // Test de conexión
    await redisClient.ping();

    logger.info('Redis connection successful', {
      host: redisConfig.host,
      port: redisConfig.port,
    });

    return true;
  } catch (error) {
    logger.error('Failed to connect to Redis', {
      error: (error as Error).message,
      host: redisConfig.host,
      port: redisConfig.port,
    });

    // Redis es opcional, no debe detener la app
    logger.warn('Application will continue without Redis (token blacklist disabled)');
    return false;
  }
}

/**
 * Cierra la conexión a Redis
 */
export async function closeRedis(): Promise<void> {
  try {
    await redisClient.quit();
    logger.info('Redis connection closed gracefully');
  } catch (error) {
    logger.error('Error closing Redis connection', {
      error: (error as Error).message,
    });

    // Forzar desconexión si quit() falla
    redisClient.disconnect();
  }
}

/**
 * Verifica si Redis está conectado
 */
export function isRedisConnected(): boolean {
  return redisClient.status === 'ready';
}

// ===== Utilidades para Token Blacklist =====

/**
 * Agrega un token a la blacklist
 * @param token - Token JWT a invalidar
 * @param expiresIn - Tiempo de expiración en segundos
 */
export async function blacklistToken(token: string, expiresIn: number): Promise<void> {
  if (!isRedisConnected()) {
    logger.warn('Cannot blacklist token: Redis not connected');
    return;
  }

  try {
    const key = `blacklist:${token}`;
    await redisClient.setex(key, expiresIn, '1');
    logger.debug('Token added to blacklist', { expiresIn });
  } catch (error) {
    logger.error('Error blacklisting token', {
      error: (error as Error).message,
    });
  }
}

/**
 * Verifica si un token está en la blacklist
 * @param token - Token JWT a verificar
 * @returns Promise<boolean> - true si el token está en blacklist
 */
export async function isTokenBlacklisted(token: string): Promise<boolean> {
  if (!isRedisConnected()) {
    logger.warn('Cannot check blacklist: Redis not connected');
    return false; // Si Redis no está disponible, permitir el acceso
  }

  try {
    const key = `blacklist:${token}`;
    const result = await redisClient.get(key);
    return result !== null;
  } catch (error) {
    logger.error('Error checking token blacklist', {
      error: (error as Error).message,
    });
    return false; // En caso de error, no bloquear el acceso
  }
}

/**
 * Limpia tokens expirados de la blacklist
 * Nota: Redis hace esto automáticamente con SETEX, esta función es para uso manual si es necesario
 */
export async function cleanupBlacklist(): Promise<void> {
  if (!isRedisConnected()) {
    logger.warn('Cannot cleanup blacklist: Redis not connected');
    return;
  }

  try {
    // Redis maneja automáticamente la expiración con SETEX
    // Esta función está disponible para limpieza manual si se necesita
    logger.info('Blacklist cleanup completed (handled by Redis TTL)');
  } catch (error) {
    logger.error('Error during blacklist cleanup', {
      error: (error as Error).message,
    });
  }
}
