import Redis from 'ioredis';

class RedisService {
  private client: Redis;
  private connected: boolean = false;

  constructor() {
    this.client = new Redis({
      host: process.env.REDIS_HOST || 'localhost',
      port: parseInt(process.env.REDIS_PORT || '6379'),
      password: process.env.REDIS_PASSWORD || undefined,
      db: parseInt(process.env.REDIS_DB || '0'),
      retryStrategy: (times: number) => {
        if (times > 10) {
          console.error('[Redis] 连接失败次数过多，停止重试');
          return null;
        }
        const delay = Math.min(times * 100, 3000);
        console.log(`[Redis] ${delay}ms 后重试连接...`);
        return delay;
      },
    });

    this.client.on('connect', () => {
      console.log('[Redis] 连接成功');
      this.connected = true;
    });

    this.client.on('error', (error) => {
      console.error('[Redis] 连接错误:', error.message);
      this.connected = false;
    });

    this.client.on('close', () => {
      console.log('[Redis] 连接关闭');
      this.connected = false;
    });
  }

  /**
   * 检查 Redis 是否可用
   */
  isAvailable(): boolean {
    return this.connected;
  }

  /**
   * 获取缓存
   * @param key 缓存键
   * @returns 缓存值
   */
  async get(key: string): Promise<string | null> {
    try {
      if (!this.connected) return null;
      return await this.client.get(key);
    } catch (error) {
      console.error(`[Redis] GET 失败 [${key}]:`, (error as Error).message);
      return null;
    }
  }

  /**
   * 设置缓存
   * @param key 缓存键
   * @param value 缓存值
   * @param ttl 过期时间（秒），0 表示永不过期
   */
  async set(key: string, value: string, ttl: number = 0): Promise<boolean> {
    try {
      if (!this.connected) return false;
      
      if (ttl > 0) {
        await this.client.setex(key, ttl, value);
      } else {
        await this.client.set(key, value);
      }
      return true;
    } catch (error) {
      console.error(`[Redis] SET 失败 [${key}]:`, (error as Error).message);
      return false;
    }
  }

  /**
   * 获取并解析 JSON 缓存
   * @param key 缓存键
   * @returns 解析后的对象
   */
  async getJSON<T>(key: string): Promise<T | null> {
    try {
      const data = await this.get(key);
      if (!data) return null;
      return JSON.parse(data) as T;
    } catch (error) {
      console.error(`[Redis] GET JSON 失败 [${key}]:`, (error as Error).message);
      return null;
    }
  }

  /**
   * 设置 JSON 缓存
   * @param key 缓存键
   * @param value 缓存对象
   * @param ttl 过期时间（秒）
   */
  async setJSON(key: string, value: any, ttl: number = 0): Promise<boolean> {
    try {
      const jsonStr = JSON.stringify(value);
      return await this.set(key, jsonStr, ttl);
    } catch (error) {
      console.error(`[Redis] SET JSON 失败 [${key}]:`, (error as Error).message);
      return false;
    }
  }

  /**
   * 删除缓存
   * @param key 缓存键
   */
  async del(key: string): Promise<boolean> {
    try {
      if (!this.connected) return false;
      await this.client.del(key);
      return true;
    } catch (error) {
      console.error(`[Redis] DEL 失败 [${key}]:`, (error as Error).message);
      return false;
    }
  }

  /**
   * 批量删除缓存（支持通配符）
   * @param pattern 匹配模式，如 "user:*"
   */
  async delPattern(pattern: string): Promise<boolean> {
    try {
      if (!this.connected) return false;
      
      const keys = await this.client.keys(pattern);
      if (keys.length > 0) {
        await this.client.del(...keys);
        console.log(`[Redis] 批量删除: ${pattern} (${keys.length} 个键)`);
      }
      return true;
    } catch (error) {
      console.error(`[Redis] 批量删除失败 [${pattern}]:`, (error as Error).message);
      return false;
    }
  }

  /**
   * 设置 Hash 字段
   * @param key Hash 键
   * @param field 字段名
   * @param value 字段值
   */
  async hset(key: string, field: string, value: string): Promise<boolean> {
    try {
      if (!this.connected) return false;
      await this.client.hset(key, field, value);
      return true;
    } catch (error) {
      console.error(`[Redis] HSET 失败 [${key}.${field}]:`, (error as Error).message);
      return false;
    }
  }

  /**
   * 获取 Hash 字段
   * @param key Hash 键
   * @param field 字段名
   */
  async hget(key: string, field: string): Promise<string | null> {
    try {
      if (!this.connected) return null;
      return await this.client.hget(key, field);
    } catch (error) {
      console.error(`[Redis] HGET 失败 [${key}.${field}]:`, (error as Error).message);
      return null;
    }
  }

  /**
   * 获取整个 Hash
   * @param key Hash 键
   */
  async hgetall(key: string): Promise<Record<string, string> | null> {
    try {
      if (!this.connected) return null;
      return await this.client.hgetall(key);
    } catch (error) {
      console.error(`[Redis] HGETALL 失败 [${key}]:`, (error as Error).message);
      return null;
    }
  }

  /**
   * 递增计数器
   * @param key 计数器键
   * @returns 新值
   */
  async incr(key: string): Promise<number> {
    try {
      if (!this.connected) return 0;
      return await this.client.incr(key);
    } catch (error) {
      console.error(`[Redis] INCR 失败 [${key}]:`, (error as Error).message);
      return 0;
    }
  }

  /**
   * 设置键的过期时间
   * @param key 键
   * @param seconds 过期时间（秒）
   */
  async expire(key: string, seconds: number): Promise<boolean> {
    try {
      if (!this.connected) return false;
      await this.client.expire(key, seconds);
      return true;
    } catch (error) {
      console.error(`[Redis] EXPIRE 失败 [${key}]:`, (error as Error).message);
      return false;
    }
  }

  /**
   * 检查键是否存在
   * @param key 键
   */
  async exists(key: string): Promise<boolean> {
    try {
      if (!this.connected) return false;
      const result = await this.client.exists(key);
      return result === 1;
    } catch (error) {
      console.error(`[Redis] EXISTS 失败 [${key}]:`, (error as Error).message);
      return false;
    }
  }

  /**
   * 测试 Redis 连接
   */
  async testConnection(): Promise<boolean> {
    try {
      const result = await this.client.ping();
      console.log('[Redis] 连接测试成功:', result);
      return true;
    } catch (error) {
      console.error('[Redis] 连接测试失败:', (error as Error).message);
      return false;
    }
  }

  /**
   * 关闭连接
   */
  async disconnect(): Promise<void> {
    try {
      await this.client.quit();
      console.log('[Redis] 连接已关闭');
    } catch (error) {
      console.error('[Redis] 关闭连接失败:', (error as Error).message);
    }
  }
}

// 导出单例
export const redisService = new RedisService();
export default redisService;

