/**
 * Redis holds all *ephemeral* real-time state: presence, typing, the live
 * queue index, socket→conversation maps. Postgres stays the source of truth
 * for anything that must survive a restart.
 */
import { Redis } from 'ioredis';
import { env } from '../config/env.js';
import { logger } from '../utils/logger.js';
import { describeError } from '../utils/describeError.js';

const opts = { maxRetriesPerRequest: null as null, lazyConnect: false };

export const redis = new Redis(env.REDIS_URL, opts);
/** Socket.IO's Redis adapter needs its own pub/sub pair. */
export const redisPub = new Redis(env.REDIS_URL, opts);
export const redisSub = new Redis(env.REDIS_URL, opts);

/**
 * The most recent connection error, kept so the startup check can say *why*
 * it gave up - ioredis retries in the background, so the failure never
 * surfaces through a command's own rejection.
 */
export const redisHealth: { lastError: string | null } = { lastError: null };

for (const [name, client] of [['redis', redis], ['redis:pub', redisPub], ['redis:sub', redisSub]] as const) {
  // Errors here are already retried by ioredis; log at warn so a transient
  // blip during a Redis restart does not read as a fatal application error.
  client.on('error', (err) => {
    redisHealth.lastError = describeError(err);
    logger.warn(`${name} error`, { err: redisHealth.lastError, url: env.REDIS_URL });
  });
  client.on('connect', () => {
    redisHealth.lastError = null;
    logger.debug(`${name} connected`);
  });
}

export const KEYS = {
  agentPresence: 'presence:agents',                          // hash agentId -> status
  agentSockets: (agentId: string) => `sockets:agent:${agentId}`,   // set of socket ids
  convSockets: (convId: string) => `sockets:conv:${convId}`,       // set of socket ids
  queue: 'queue:conversations',                              // zset convId -> score
  typing: (convId: string) => `typing:${convId}`,            // hash actorId -> ts
  geo: (ip: string) => `geo:${ip}`,                          // cached geo-IP lookup
  agentLoad: 'load:agents',                                  // hash agentId -> active chat count
} as const;
