/**
 * Redis client singleton for idempotency and other features
 * Gracefully handles Redis unavailability (fail-open design)
 */

import IORedis from 'ioredis';
import { env, createLogger } from '@4runr/shared';

const logger = createLogger('Gateway:Redis');

let redisClient: IORedis | null = null;
let redisPromise: Promise<IORedis | null> | null = null;

/**
 * Get or create Redis client
 * Returns null if Redis is not configured or unavailable
 */
export async function getRedisClient(): Promise<IORedis | null> {
  if (redisClient) {
    return redisClient;
  }

  if (redisPromise) {
    return await redisPromise;
  }

  redisPromise = (async () => {
    // Check both env object (parsed at startup) and process.env (set at runtime by Docker init)
    const redisUrl = env.REDIS_URL || process.env['REDIS_URL'];
    if (!redisUrl) {
      return null;
    }

    try {
      const client = new IORedis(redisUrl, {
        maxRetriesPerRequest: 3,
        lazyConnect: true,
        retryStrategy: (times) => {
          if (times > 3) {
            return null; // Stop retrying after 3 attempts
          }
          return Math.min(times * 200, 2000);
        },
      });

      client.on('error', (error) => {
        logger.warn('Redis connection error', { error: error.message });
        // Don't set client to null - allow retries
      });

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

      await client.connect();
      redisClient = client;
      return client;
    } catch (error) {
      logger.warn('Redis connection failed', { 
        error: error instanceof Error ? error.message : String(error),
        fallback: 'in-memory idempotency store'
      });
      return null;
    }
  })();

  return await redisPromise;
}

/**
 * Disconnect Redis client
 */
export async function disconnectRedis(): Promise<void> {
  if (redisClient) {
    await redisClient.quit();
    redisClient = null;
    redisPromise = null;
  }
}

/**
 * Check if Redis is available
 */
export async function isRedisAvailable(): Promise<boolean> {
  const client = await getRedisClient();
  if (!client) {
    return false;
  }

  try {
    await client.ping();
    return true;
  } catch {
    return false;
  }
}

