import { Injectable, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Redis, { RedisOptions } from 'ioredis';

@Injectable()
export class RedisService implements OnModuleDestroy {
  private client: Redis | null = null;

  constructor(private readonly configService: ConfigService) {}

  isEnabled(): boolean {
    return this.configService.get<boolean>('redis.enabled', { infer: true }) ?? false;
  }

  getKeyPrefix(): string {
    return this.configService.get<string>('redis.keyPrefix', { infer: true }) ?? 'oudelaa';
  }

  getClient(): Redis | null {
    if (!this.isEnabled()) {
      return null;
    }

    if (!this.client) {
      this.client = this.createClient({ maxRetriesPerRequest: null });
    }

    return this.client;
  }

  createPubSubClients(): { pubClient: Redis; subClient: Redis } | null {
    if (!this.isEnabled()) {
      return null;
    }

    const pubClient = this.createClient({ maxRetriesPerRequest: null });
    const subClient = pubClient.duplicate();
    return { pubClient, subClient };
  }

  createQueueClient(): Redis | null {
    if (!this.isEnabled()) {
      return null;
    }

    return this.createClient({ maxRetriesPerRequest: null });
  }

  onModuleDestroy(): void {
    if (this.client) {
      const client = this.client;
      void client.quit().catch(() => client.disconnect());
      this.client = null;
    }
  }

  private createClient(overrides: Partial<RedisOptions> = {}): Redis {
    const url = this.configService.get<string>('redis.url', { infer: true }) ?? '';
    const baseOptions: RedisOptions = {
      host: this.configService.get<string>('redis.host', { infer: true }) ?? '127.0.0.1',
      port: this.configService.get<number>('redis.port', { infer: true }) ?? 6379,
      username: this.configService.get<string>('redis.username', { infer: true }) || undefined,
      password: this.configService.get<string>('redis.password', { infer: true }) || undefined,
      db: this.configService.get<number>('redis.db', { infer: true }) ?? 0,
      lazyConnect: false,
      enableReadyCheck: true,
      ...overrides,
    };

    if (url) {
      return new Redis(url, {
        ...overrides,
        lazyConnect: false,
        enableReadyCheck: true,
      });
    }

    return new Redis(baseOptions);
  }
}
