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

/**
 * Token blacklist using Redis for immediate access token revocation.
 * When a user logs out or changes password, their access tokens
 * are blacklisted until they expire naturally.
 *
 * Supports Redis Cluster for distributed deployment.
 */
@Injectable()
export class TokenBlacklistService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger('TokenBlacklist');
  private redis: Redis;

  async onModuleInit() {
    const host = process.env.REDIS_HOST || 'localhost';
    const port = parseInt(process.env.REDIS_PORT || '6379', 10);
    const password = process.env.REDIS_PASSWORD || undefined;

    this.redis = new Redis({
      host,
      port,
      password,
      keyPrefix: 'auth:blacklist:',
      retryStrategy: (times) => Math.min(times * 50, 2000),
    });

    this.redis.on('connect', () => this.logger.log('Redis connected for token blacklist'));
    this.redis.on('error', (err) => this.logger.error(`Redis error: ${err.message}`));
  }

  async onModuleDestroy() {
    if (this.redis) {
      await this.redis.quit();
    }
  }

  /**
   * Blacklist a JWT token ID. TTL matches the token's remaining lifetime.
   */
  async blacklist(jti: string, ttlSeconds: number): Promise<void> {
    await this.redis.set(jti, '1', 'EX', ttlSeconds);
  }

  /**
   * Blacklist all tokens for a user. Uses a user-level key with version counter.
   */
  async blacklistUser(userId: string, ttlSeconds: number): Promise<void> {
    await this.redis.set(`user:${userId}`, Date.now().toString(), 'EX', ttlSeconds);
  }

  /**
   * Check if a token or user is blacklisted.
   */
  async isBlacklisted(jti: string): Promise<boolean> {
    const result = await this.redis.get(jti);
    return result !== null;
  }

  async isUserBlacklisted(userId: string): Promise<boolean> {
    const result = await this.redis.get(`user:${userId}`);
    return result !== null;
  }
}
