/**
 * RealRedisService集成测试
 * 
 * 功能描述：
 * - 使用真实Redis连接进行集成测试
 * - 测试Redis服务器连接和断开
 * - 验证数据持久性和一致性
 * - 测试Redis服务的完整工作流程
 * 
 * 职责分离：
 * - 集成测试：测试与真实Redis服务器的交互
 * - 连接测试：验证Redis连接管理
 * - 数据一致性：测试数据的持久化和读取
 * - 性能测试：验证Redis操作的性能表现
 * 
 * 最近修改：
 * - 2025-01-07: 功能新增 - 创建RealRedisService完整集成测试，验证真实Redis交互
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2025-01-07
 * @lastModified 2025-01-07
 */
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { RealRedisService } from './real_redis.service';
import Redis from 'ioredis';

describe('RealRedisService Integration', () => {
  let service: RealRedisService;
  let module: TestingModule;
  let configService: ConfigService;

  // 测试配置 - 使用测试Redis实例
  const testRedisConfig = {
    REDIS_HOST: process.env.TEST_REDIS_HOST || 'localhost',
    REDIS_PORT: parseInt(process.env.TEST_REDIS_PORT || '6379'),
    REDIS_PASSWORD: process.env.TEST_REDIS_PASSWORD,
    REDIS_DB: parseInt(process.env.TEST_REDIS_DB || '15'), // 使用DB 15进行测试
  };

  beforeAll(async () => {
    // 检查是否有可用的Redis服务器
    const testRedis = new Redis({
      host: testRedisConfig.REDIS_HOST,
      port: testRedisConfig.REDIS_PORT,
      password: testRedisConfig.REDIS_PASSWORD,
      db: testRedisConfig.REDIS_DB,
      lazyConnect: true,
      maxRetriesPerRequest: 1,
    });

    try {
      await testRedis.ping();
      // 确保连接被正确断开
      testRedis.disconnect(false);
    } catch (error) {
      console.warn('Redis服务器不可用，跳过集成测试:', (error as Error).message);
      // 确保连接被正确断开
      testRedis.disconnect(false);
      return;
    }

    // 创建测试模块
    module = await Test.createTestingModule({
      providers: [
        RealRedisService,
        {
          provide: ConfigService,
          useValue: {
            get: jest.fn((key: string, defaultValue?: any) => {
              return testRedisConfig[key] || defaultValue;
            }),
          },
        },
      ],
    }).compile();

    service = module.get<RealRedisService>(RealRedisService);
    configService = module.get<ConfigService>(ConfigService);

    // 清空测试数据库
    try {
      await service.flushall();
    } catch (error) {
      // 忽略清空数据时的错误
    }
  });

  afterAll(async () => {
    if (service) {
      try {
        // 清空测试数据
        await service.flushall();
      } catch (error) {
        // 忽略清空数据时的错误
      }
      
      try {
        // 断开连接
        service.onModuleDestroy();
      } catch (error) {
        // 忽略断开连接时的错误
      }
    }
    if (module) {
      await module.close();
    }
  });

  beforeEach(async () => {
    // 每个测试前清空数据
    if (service) {
      try {
        await service.flushall();
      } catch (error) {
        // 忽略清空数据时的错误
      }
    }
  });

  // 检查Redis是否可用的辅助函数
  const skipIfRedisUnavailable = () => {
    if (!service) {
      return true; // 返回true表示应该跳过测试
    }
    return false;
  };

  describe('Redis连接管理', () => {
    it('should connect to Redis server successfully', () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }
      expect(service).toBeDefined();
    });

    it('should use correct Redis configuration', () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }
      expect(configService.get).toHaveBeenCalledWith('REDIS_HOST', 'localhost');
      expect(configService.get).toHaveBeenCalledWith('REDIS_PORT', 6379);
      expect(configService.get).toHaveBeenCalledWith('REDIS_DB', 0);
    });
  });

  describe('基础键值操作', () => {
    it('should set and get string values', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('test:string', 'Hello Redis');
      const result = await service.get('test:string');

      expect(result).toBe('Hello Redis');
    });

    it('should handle non-existent keys', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      const result = await service.get('test:nonexistent');

      expect(result).toBeNull();
    });

    it('should delete existing keys', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('test:delete', 'to be deleted');
      const deleted = await service.del('test:delete');
      const result = await service.get('test:delete');

      expect(deleted).toBe(true);
      expect(result).toBeNull();
    });

    it('should return false when deleting non-existent keys', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      const deleted = await service.del('test:nonexistent');

      expect(deleted).toBe(false);
    });

    it('should check key existence', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('test:exists', 'exists');
      const exists = await service.exists('test:exists');
      const notExists = await service.exists('test:notexists');

      expect(exists).toBe(true);
      expect(notExists).toBe(false);
    });
  });

  describe('过期时间管理', () => {
    it('should set keys with TTL', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('test:ttl', 'expires soon', 2);
      const ttl = await service.ttl('test:ttl');

      expect(ttl).toBeGreaterThan(0);
      expect(ttl).toBeLessThanOrEqual(2);
    });

    it('should expire keys after TTL', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('test:expire', 'will expire', 1);
      
      // 等待过期
      await new Promise(resolve => setTimeout(resolve, 1100));
      
      const result = await service.get('test:expire');
      expect(result).toBeNull();
    }, 2000);

    it('should set expiration on existing keys', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('test:expire_later', 'set expiration later');
      await service.expire('test:expire_later', 2);
      
      const ttl = await service.ttl('test:expire_later');
      expect(ttl).toBeGreaterThan(0);
      expect(ttl).toBeLessThanOrEqual(2);
    });

    it('should return -1 for keys without expiration', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('test:no_expire', 'never expires');
      const ttl = await service.ttl('test:no_expire');

      expect(ttl).toBe(-1);
    });

    it('should return -2 for non-existent keys', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      const ttl = await service.ttl('test:nonexistent');

      expect(ttl).toBe(-2);
    });

    it('should use setex for atomic set with expiration', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.setex('test:setex', 2, 'atomic set with expiration');
      const value = await service.get('test:setex');
      const ttl = await service.ttl('test:setex');

      expect(value).toBe('atomic set with expiration');
      expect(ttl).toBeGreaterThan(0);
      expect(ttl).toBeLessThanOrEqual(2);
    });
  });

  describe('数值操作', () => {
    it('should increment numeric values', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      const result1 = await service.incr('test:counter');
      const result2 = await service.incr('test:counter');
      const result3 = await service.incr('test:counter');

      expect(result1).toBe(1);
      expect(result2).toBe(2);
      expect(result3).toBe(3);
    });

    it('should increment existing numeric values', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('test:existing_counter', '10');
      const result = await service.incr('test:existing_counter');

      expect(result).toBe(11);
    });
  });

  describe('集合操作', () => {
    it('should add and retrieve set members', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.sadd('test:set', 'member1');
      await service.sadd('test:set', 'member2');
      await service.sadd('test:set', 'member3');

      const members = await service.smembers('test:set');

      expect(members).toHaveLength(3);
      expect(members).toContain('member1');
      expect(members).toContain('member2');
      expect(members).toContain('member3');
    });

    it('should remove set members', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.sadd('test:set_remove', 'member1');
      await service.sadd('test:set_remove', 'member2');
      await service.sadd('test:set_remove', 'member3');

      await service.srem('test:set_remove', 'member2');

      const members = await service.smembers('test:set_remove');

      expect(members).toHaveLength(2);
      expect(members).toContain('member1');
      expect(members).toContain('member3');
      expect(members).not.toContain('member2');
    });

    it('should return empty array for non-existent sets', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      const members = await service.smembers('test:nonexistent_set');

      expect(members).toEqual([]);
    });

    it('should handle duplicate set members', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.sadd('test:duplicate_set', 'member1');
      await service.sadd('test:duplicate_set', 'member1'); // 重复添加
      await service.sadd('test:duplicate_set', 'member2');

      const members = await service.smembers('test:duplicate_set');

      expect(members).toHaveLength(2);
      expect(members).toContain('member1');
      expect(members).toContain('member2');
    });
  });

  describe('数据持久性和一致性', () => {
    it('should persist data across operations', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      // 设置多种类型的数据
      await service.set('test:persist:string', 'persistent string');
      await service.set('test:persist:number', '42');
      await service.sadd('test:persist:set', 'set_member');
      await service.incr('test:persist:counter');

      // 验证数据持久性
      const stringValue = await service.get('test:persist:string');
      const numberValue = await service.get('test:persist:number');
      const setMembers = await service.smembers('test:persist:set');
      const counterValue = await service.get('test:persist:counter');

      expect(stringValue).toBe('persistent string');
      expect(numberValue).toBe('42');
      expect(setMembers).toContain('set_member');
      expect(counterValue).toBe('1');
    });

    it('should maintain data consistency during concurrent operations', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      // 并发执行多个操作
      const promises = [];
      for (let i = 0; i < 10; i++) {
        promises.push(service.incr('test:concurrent:counter'));
        promises.push(service.sadd('test:concurrent:set', `member${i}`));
      }

      await Promise.all(promises);

      // 验证结果一致性
      const counterValue = await service.get('test:concurrent:counter');
      const setMembers = await service.smembers('test:concurrent:set');

      expect(parseInt(counterValue)).toBe(10);
      expect(setMembers).toHaveLength(10);
    });
  });

  describe('清空操作', () => {
    it('should clear all data with flushall', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      // 设置一些测试数据
      await service.set('test:flush1', 'value1');
      await service.set('test:flush2', 'value2');
      await service.sadd('test:flush_set', 'member');

      // 清空所有数据
      await service.flushall();

      // 验证数据已清空
      const value1 = await service.get('test:flush1');
      const value2 = await service.get('test:flush2');
      const setMembers = await service.smembers('test:flush_set');

      expect(value1).toBeNull();
      expect(value2).toBeNull();
      expect(setMembers).toEqual([]);
    });
  });

  describe('错误处理和边界情况', () => {
    it('should handle empty string keys and values', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      await service.set('', 'empty key');
      await service.set('empty_value', '');

      const emptyKeyValue = await service.get('');
      const emptyValue = await service.get('empty_value');

      expect(emptyKeyValue).toBe('empty key');
      expect(emptyValue).toBe('');
    });

    it('should handle very long keys and values', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      const longKey = 'test:' + 'a'.repeat(1000);
      const longValue = 'b'.repeat(10000);

      await service.set(longKey, longValue);
      const result = await service.get(longKey);

      expect(result).toBe(longValue);
    });

    it('should handle special characters in keys and values', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      const specialKey = 'test:特殊字符:🚀:key';
      const specialValue = 'Special value with 特殊字符 and 🎉 emojis';

      await service.set(specialKey, specialValue);
      const result = await service.get(specialKey);

      expect(result).toBe(specialValue);
    });
  });

  describe('性能测试', () => {
    it('should handle multiple operations efficiently', async () => {
      if (skipIfRedisUnavailable()) {
        console.log('跳过测试：Redis服务器不可用');
        return;
      }

      const startTime = Date.now();
      const operations = 100;

      // 执行大量操作
      const promises = [];
      for (let i = 0; i < operations; i++) {
        promises.push(service.set(`test:perf:${i}`, `value${i}`));
      }
      await Promise.all(promises);

      // 读取所有数据
      const readPromises = [];
      for (let i = 0; i < operations; i++) {
        readPromises.push(service.get(`test:perf:${i}`));
      }
      const results = await Promise.all(readPromises);

      const endTime = Date.now();
      const duration = endTime - startTime;

      // 验证结果正确性
      expect(results).toHaveLength(operations);
      results.forEach((result, index) => {
        expect(result).toBe(`value${index}`);
      });

      // 性能检查（应该在合理时间内完成）
      expect(duration).toBeLessThan(5000); // 5秒内完成
    }, 10000);
  });
});