/**
 * 真实Redis服务实现
 *
 * 功能描述：
 * - 连接真实的Redis服务器进行数据操作
 * - 实现完整的Redis基础操作功能
 * - 提供连接管理和错误处理机制
 * - 支持自动重连和连接状态监控
 *
 * 职责分离：
 * - 连接管理：负责Redis服务器的连接建立和维护
 * - 数据操作：实现IRedisService接口的所有方法
 * - 错误处理：处理网络异常和Redis操作错误
 * - 日志记录：记录连接状态和操作日志
 *
 * 最近修改：
 * - 2025-01-07: 代码规范优化 - 为所有公共方法添加完整的三级注释，包含业务逻辑和示例代码
 * - 2025-01-07: 代码规范优化 - 为主要方法添加完整的三级注释，包含业务逻辑和示例代码
 * - 2025-01-07: 代码规范优化 - 完善文件头注释和方法注释，添加详细业务逻辑说明
 *
 * @author moyin
 * @version 1.0.3
 * @since 2025-01-07
 * @lastModified 2025-01-07
 */
import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Redis from 'ioredis';
import { IRedisService } from './redis.interface';

/**
 * 真实Redis服务
 *
 * 职责：
 * - 连接到真实的Redis服务器
 * - 实现完整的Redis操作接口
 * - 管理连接生命周期和错误处理
 *
 * 主要方法：
 * - initializeRedis() - 初始化Redis连接
 * - set/get/del() - 基础键值操作
 * - expire/ttl() - 过期时间管理
 * - sadd/srem/smembers() - 集合操作
 *
 * 使用场景：
 * - 生产环境的Redis数据存储
 * - 高性能和高并发的数据访问需求
 */
@Injectable()
export class RealRedisService implements IRedisService, OnModuleDestroy {
  private readonly logger = new Logger(RealRedisService.name);
  private redis: Redis;

  constructor(private configService: ConfigService) {
    this.initializeRedis();
  }

  /**
   * 初始化Redis连接
   *
   * 业务逻辑：
   * 1. 从环境变量读取Redis连接配置
   * 2. 创建Redis客户端实例并配置连接参数
   * 3. 设置连接事件监听器
   * 4. 配置重连策略和错误处理
   *
   * @throws Error Redis连接配置错误时
   *
   * @example
   * ```typescript
   * // 在构造函数中自动调用
   * constructor(configService: ConfigService) {
   *   this.initializeRedis();
   * }
   * ```
   */
  private initializeRedis(): void {
    const redisConfig = {
      host: this.configService.get<string>('REDIS_HOST', 'localhost'),
      port: this.configService.get<number>('REDIS_PORT', 6379),
      password: this.configService.get<string>('REDIS_PASSWORD') || undefined,
      db: this.configService.get<number>('REDIS_DB', 0),
      retryDelayOnFailover: 100,
      maxRetriesPerRequest: 3,
      lazyConnect: true,
    };

    this.redis = new Redis(redisConfig);

    this.redis.on('connect', () => {
      this.logger.log('Redis连接成功');
    });

    this.redis.on('error', (error) => {
      this.logger.error('Redis连接错误', error);
    });

    this.redis.on('close', () => {
      this.logger.warn('Redis连接关闭');
    });
  }

  /**
   * 设置键值对
   *
   * 业务逻辑：
   * 1. 验证键和值的有效性
   * 2. 根据TTL参数决定使用set还是setex命令
   * 3. 执行Redis设置操作
   * 4. 记录操作日志和错误处理
   *
   * @param key 键名，不能为空
   * @param value 值，支持字符串类型
   * @param ttl 可选的过期时间（秒），不设置则永不过期
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * await redisService.set('user:123', 'userData', 3600);
   * ```
   */
  async set(key: string, value: string, ttl?: number): Promise<void> {
    try {
      if (ttl && ttl > 0) {
        await this.redis.setex(key, ttl, value);
      } else {
        await this.redis.set(key, value);
      }
      this.logger.debug(`设置Redis键: ${key}, TTL: ${ttl || '永不过期'}`);
    } catch (error) {
      this.logger.error(`设置Redis键失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 获取键对应的值
   *
   * 业务逻辑：
   * 1. 验证键名的有效性
   * 2. 执行Redis get命令
   * 3. 返回查询结果
   * 4. 处理查询异常
   *
   * @param key 键名，不能为空
   * @returns Promise<string | null> 键对应的值，不存在返回null
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * const value = await redisService.get('user:123');
   * ```
   */
  async get(key: string): Promise<string | null> {
    try {
      return await this.redis.get(key);
    } catch (error) {
      this.logger.error(`获取Redis键失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 删除指定的键
   *
   * 业务逻辑：
   * 1. 验证键名的有效性
   * 2. 执行Redis del命令删除键
   * 3. 检查删除操作的结果
   * 4. 记录删除操作日志
   * 5. 返回删除是否成功
   *
   * @param key 键名，不能为空
   * @returns Promise<boolean> 删除成功返回true，键不存在返回false
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * const deleted = await redisService.del('user:123');
   * console.log(deleted ? '删除成功' : '键不存在');
   * ```
   */
  async del(key: string): Promise<boolean> {
    try {
      const result = await this.redis.del(key);
      this.logger.debug(`删除Redis键: ${key}, 结果: ${result > 0}`);
      return result > 0;
    } catch (error) {
      this.logger.error(`删除Redis键失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 检查键是否存在
   *
   * 业务逻辑：
   * 1. 验证键名的有效性
   * 2. 执行Redis exists命令
   * 3. 检查返回结果是否大于0
   * 4. 处理查询异常
   * 5. 返回键的存在状态
   *
   * @param key 键名，不能为空
   * @returns Promise<boolean> 键存在返回true，不存在返回false
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * const exists = await redisService.exists('user:123');
   * if (exists) {
   *   console.log('用户数据存在');
   * }
   * ```
   */
  async exists(key: string): Promise<boolean> {
    try {
      const result = await this.redis.exists(key);
      return result > 0;
    } catch (error) {
      this.logger.error(`检查Redis键存在性失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 设置键的过期时间
   *
   * 业务逻辑：
   * 1. 验证键名和TTL参数的有效性
   * 2. 执行Redis expire命令设置过期时间
   * 3. 记录过期时间设置日志
   * 4. 处理设置异常
   *
   * @param key 键名，不能为空
   * @param ttl 过期时间（秒），必须大于0
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * await redisService.expire('user:123', 3600); // 1小时后过期
   * ```
   */
  async expire(key: string, ttl: number): Promise<void> {
    try {
      await this.redis.expire(key, ttl);
      this.logger.debug(`设置Redis键过期时间: ${key}, TTL: ${ttl}秒`);
    } catch (error) {
      this.logger.error(`设置Redis键过期时间失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 获取键的剩余过期时间
   *
   * 业务逻辑：
   * 1. 验证键名的有效性
   * 2. 执行Redis ttl命令查询剩余时间
   * 3. 返回剩余时间或状态码
   * 4. 处理查询异常
   *
   * @param key 键名，不能为空
   * @returns Promise<number> 剩余时间（秒），-1表示永不过期，-2表示键不存在
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * const ttl = await redisService.ttl('user:123');
   * if (ttl > 0) {
   *   console.log(`还有${ttl}秒过期`);
   * } else if (ttl === -1) {
   *   console.log('永不过期');
   * } else {
   *   console.log('键不存在');
   * }
   * ```
   */
  async ttl(key: string): Promise<number> {
    try {
      return await this.redis.ttl(key);
    } catch (error) {
      this.logger.error(`获取Redis键TTL失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 清空所有数据
   *
   * 业务逻辑：
   * 1. 执行Redis flushall命令清空所有数据
   * 2. 记录清空操作日志
   * 3. 处理清空异常
   *
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * await redisService.flushall();
   * console.log('所有数据已清空');
   * ```
   */
  async flushall(): Promise<void> {
    try {
      await this.redis.flushall();
      this.logger.log('清空所有Redis数据');
    } catch (error) {
      this.logger.error('清空Redis数据失败', error);
      throw error;
    }
  }

  /**
   * 设置键值对并指定过期时间
   *
   * 业务逻辑：
   * 1. 验证键、值和TTL参数的有效性
   * 2. 执行Redis setex命令同时设置值和过期时间
   * 3. 记录操作日志
   * 4. 处理设置异常
   *
   * @param key 键名，不能为空
   * @param ttl 过期时间（秒），必须大于0
   * @param value 值，支持字符串类型
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * await redisService.setex('session:abc', 1800, 'sessionData');
   * ```
   */
  async setex(key: string, ttl: number, value: string): Promise<void> {
    try {
      await this.redis.setex(key, ttl, value);
      this.logger.debug(`设置Redis键(setex): ${key}, TTL: ${ttl}秒`);
    } catch (error) {
      this.logger.error(`设置Redis键失败(setex): ${key}`, error);
      throw error;
    }
  }

  /**
   * 键值自增操作
   *
   * 业务逻辑：
   * 1. 验证键名的有效性
   * 2. 执行Redis incr命令进行自增操作
   * 3. 获取自增后的新值
   * 4. 记录自增操作日志
   * 5. 返回新值
   *
   * @param key 键名，不能为空
   * @returns Promise<number> 自增后的新值
   * @throws Error 当Redis操作失败或值不是数字时
   *
   * @example
   * ```typescript
   * const newValue = await redisService.incr('counter');
   * console.log(`计数器新值: ${newValue}`);
   * ```
   */
  async incr(key: string): Promise<number> {
    try {
      const result = await this.redis.incr(key);
      this.logger.debug(`自增Redis键: ${key}, 新值: ${result}`);
      return result;
    } catch (error) {
      this.logger.error(`自增Redis键失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 向集合添加成员
   *
   * 业务逻辑：
   * 1. 验证键名和成员的有效性
   * 2. 执行Redis sadd命令添加成员到集合
   * 3. 记录添加操作日志
   * 4. 处理添加异常
   *
   * @param key 集合键名，不能为空
   * @param member 要添加的成员，不能为空
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * await redisService.sadd('users', 'user123');
   * ```
   */
  async sadd(key: string, member: string): Promise<void> {
    try {
      await this.redis.sadd(key, member);
      this.logger.debug(`添加集合成员: ${key} -> ${member}`);
    } catch (error) {
      this.logger.error(`添加集合成员失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 从集合移除成员
   *
   * 业务逻辑：
   * 1. 验证键名和成员的有效性
   * 2. 执行Redis srem命令从集合中移除成员
   * 3. 记录移除操作日志
   * 4. 处理移除异常
   *
   * @param key 集合键名，不能为空
   * @param member 要移除的成员，不能为空
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * await redisService.srem('users', 'user123');
   * ```
   */
  async srem(key: string, member: string): Promise<void> {
    try {
      await this.redis.srem(key, member);
      this.logger.debug(`移除集合成员: ${key} -> ${member}`);
    } catch (error) {
      this.logger.error(`移除集合成员失败: ${key}`, error);
      throw error;
    }
  }

  /**
   * 获取集合的所有成员
   *
   * 业务逻辑：
   * 1. 验证键名的有效性
   * 2. 执行Redis smembers命令获取集合所有成员
   * 3. 返回成员列表
   * 4. 处理查询异常
   *
   * @param key 集合键名，不能为空
   * @returns Promise<string[]> 集合成员列表，集合不存在返回空数组
   * @throws Error 当Redis操作失败时
   *
   * @example
   * ```typescript
   * const members = await redisService.smembers('users');
   * console.log('用户列表:', members);
   * ```
   */
  async smembers(key: string): Promise<string[]> {
    try {
      return await this.redis.smembers(key);
    } catch (error) {
      this.logger.error(`获取集合成员失败: ${key}`, error);
      throw error;
    }
  }

  async keys(pattern: string): Promise<string[]> {
    try {
      return await this.redis.keys(pattern);
    } catch (error) {
      this.logger.error(`查找Redis键失败: ${pattern}`, error);
      throw error;
    }
  }

  /**
   * 模块销毁时的清理操作
   *
   * 业务逻辑：
   * 1. 检查Redis连接是否存在
   * 2. 断开Redis连接
   * 3. 记录连接断开日志
   * 4. 释放相关资源
   *
   * @returns void 无返回值
   *
   * @example
   * ```typescript
   * // NestJS框架会在模块销毁时自动调用
   * onModuleDestroy() {
   *   // 自动清理Redis连接
   * }
   * ```
   */
  onModuleDestroy(): void {
    if (this.redis) {
      this.redis.disconnect();
      this.logger.log('Redis连接已断开');
    }
  }
}
