import { Global, Module, Injectable, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Redis from 'ioredis';

@Injectable()
export class RedisService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(RedisService.name);
  public client!: Redis;

  constructor(private readonly configService: ConfigService) {}

  async onModuleInit() {
    const url = this.configService.get<string>('REDIS_URL', 'redis://localhost:6379');
    this.client = new Redis(url, {
      maxRetriesPerRequest: 3,
      retryStrategy: (times) => Math.min(times * 200, 2000),
    });

    this.client.on('connect', () => this.logger.log('✅ Redis connected'));
    this.client.on('error', (err) => this.logger.error(`Redis error: ${err.message}`));
  }

  async onModuleDestroy() {
    await this.client?.quit();
  }

  // Cache helpers
  async get(key: string): Promise<string | null> {
    return this.client.get(key);
  }

  async set(key: string, value: string, ttlSeconds?: number): Promise<void> {
    if (ttlSeconds) {
      await this.client.set(key, value, 'EX', ttlSeconds);
    } else {
      await this.client.set(key, value);
    }
  }

  async del(key: string): Promise<void> {
    await this.client.del(key);
  }

  async exists(key: string): Promise<boolean> {
    const result = await this.client.exists(key);
    return result === 1;
  }

  // Presence helpers
  async setPresence(userId: string, status: string, ttl = 60): Promise<void> {
    await this.set(`presence:${userId}`, status, ttl);
  }

  async getPresence(userId: string): Promise<string | null> {
    return this.get(`presence:${userId}`);
  }

  // Typing indicators
  async setTyping(channelId: string, userId: string): Promise<void> {
    await this.set(`typing:${channelId}:${userId}`, Date.now().toString(), 5);
  }

  async getTyping(channelId: string): Promise<string[]> {
    // Use SCAN instead of KEYS to avoid blocking Redis
    const userIds: string[] = [];
    let cursor = '0';
    do {
      const [nextCursor, keys] = await this.client.scan(
        cursor,
        'MATCH',
        `typing:${channelId}:*`,
        'COUNT',
        100,
      );
      cursor = nextCursor;
      for (const k of keys) {
        const userId = k.split(':').pop();
        if (userId) userIds.push(userId);
      }
    } while (cursor !== '0');
    return userIds;
  }
}

@Global()
@Module({
  providers: [RedisService],
  exports: [RedisService],
})
export class RedisModule {}