import { Module } from '@nestjs/common';
import IORedis from 'ioredis';
import { Logger } from 'nestjs-pino';
import { assertRedisConfig } from '../config/check-redis-config';
import { REDIS_CLIENT, type Redis } from './redis.token';

/**
 * Redis module — owns the single shared `ioredis` connection that
 * downstream consumers (session store, OBO token cache, …) inject
 * via the `REDIS_CLIENT` token.
 *
 * Per ADR-0010:
 *  - Single instance for dev (one `REDIS_URL`); Sentinel-HA for
 *    prod (designed-in, plumbing lands with the production
 *    infrastructure ADR).
 *  - Connection is eager — ioredis opens the socket lazily but the
 *    factory builds the client at module init so DI consumers
 *    always receive an instance.
 *  - Connect / error / reconnect events route into Pino with the
 *    `redis` context so log search isolates them from app logs.
 *  - Single shared client. Pub/sub use-cases (when they appear)
 *    duplicate the connection via `redis.duplicate()` per ioredis
 *    convention.
 *
 * Module stays non-global; consumers state "I depend on Redis" by
 * importing it explicitly.
 */
@Module({
  providers: [
    {
      provide: REDIS_CLIENT,
      inject: [Logger],
      useFactory: (logger: Logger): Redis => {
        const config = assertRedisConfig();
        const client = new IORedis(config.url, {
          // Cap reconnect storms during bootstrap: if Redis is
          // unreachable we want the BFF to surface a clear error on
          // the first command path, not a never-ending reconnect
          // loop hiding the issue.
          maxRetriesPerRequest: 3,
        });

        client.on('connect', () => {
          logger.log({ event: 'redis.connect', host: config.host, port: config.port }, 'redis');
        });
        client.on('ready', () => {
          logger.log({ event: 'redis.ready' }, 'redis');
        });
        client.on('error', (err) => {
          logger.error({ event: 'redis.error', message: err.message }, 'redis');
        });
        client.on('close', () => {
          logger.warn({ event: 'redis.close' }, 'redis');
        });
        client.on('reconnecting', (delayMs: number) => {
          logger.warn({ event: 'redis.reconnecting', delayMs }, 'redis');
        });

        return client;
      },
    },
  ],
  exports: [REDIS_CLIENT],
})
export class RedisModule {}
