// Optional Redis connection manager.
//
// Redis is entirely optional. When REDIS_URL is unset the app runs exactly as
// before with in-memory backends. When set, this module owns a single shared
// command connection plus a dedicated subscriber connection (a connection in
// subscribe mode cannot run normal commands), with auto-reconnect, capped
// backoff, and a health flag that callers and the health endpoint can read.

import Redis from 'ioredis';

let client: Redis | null = null;
let subscriber: Redis | null = null;
let healthy = false;
let initialized = false;

/** Whether Redis is configured via REDIS_URL. */
export function isRedisEnabled(): boolean {
  return !!process.env.REDIS_URL;
}

/** Whether the Redis connection is currently usable. */
export function isRedisHealthy(): boolean {
  return isRedisEnabled() && healthy;
}

function buildClient(label: string): Redis {
  const url = process.env.REDIS_URL as string;
  const instance = new Redis(url, {
    // Keep the process responsive: fail fast on a per-command basis and let the
    // callers degrade to their in-memory fallback rather than hanging.
    maxRetriesPerRequest: 1,
    enableOfflineQueue: false,
    lazyConnect: false,
    retryStrategy(times) {
      // Capped exponential backoff for reconnects: 200ms, 400ms ... max 5s.
      const delay = Math.min(times * 200, 5000);
      return delay;
    },
  });

  instance.on('connect', () => {
    console.log(`[redis] (${label}) connecting`);
  });
  instance.on('ready', () => {
    healthy = true;
    console.log(`[redis] (${label}) ready`);
  });
  instance.on('error', (err) => {
    healthy = false;
    console.error(`[redis] (${label}) error:`, err?.message || err);
  });
  instance.on('reconnecting', () => {
    healthy = false;
    console.warn(`[redis] (${label}) reconnecting`);
  });
  instance.on('end', () => {
    healthy = false;
    console.warn(`[redis] (${label}) connection closed`);
  });

  return instance;
}

function ensureInit(): void {
  if (initialized || !isRedisEnabled()) return;
  initialized = true;
  client = buildClient('commands');
  subscriber = buildClient('subscriber');
}

/** Shared command connection, or null when Redis is not configured. */
export function getRedis(): Redis | null {
  ensureInit();
  return client;
}

/** Dedicated subscriber connection, or null when Redis is not configured. */
export function getSubscriber(): Redis | null {
  ensureInit();
  return subscriber;
}

/** Close connections (used for graceful shutdown). */
export async function closeRedis(): Promise<void> {
  const tasks: Promise<unknown>[] = [];
  if (client) tasks.push(client.quit().catch(() => undefined));
  if (subscriber) tasks.push(subscriber.quit().catch(() => undefined));
  await Promise.all(tasks);
  client = null;
  subscriber = null;
  initialized = false;
  healthy = false;
}
