/**
 * FileRedisService集成测试
 * 
 * 功能描述：
 * - 测试文件系统的真实读写操作
 * - 验证数据文件的创建和清理
 * - 测试过期清理任务的执行
 * - 验证文件Redis服务的完整工作流程
 * 
 * 职责分离：
 * - 集成测试：测试与真实文件系统的交互
 * - 文件操作：验证数据文件的读写和管理
 * - 过期机制：测试自动过期清理功能
 * - 数据持久化：验证数据的持久化和恢复
 * 
 * 最近修改：
 * - 2025-01-07: 功能新增 - 创建FileRedisService完整集成测试，验证真实文件系统交互
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2025-01-07
 * @lastModified 2025-01-07
 */
import { Test, TestingModule } from '@nestjs/testing';
import { FileRedisService } from './file_redis.service';
import { promises as fs } from 'fs';
import * as path from 'path';
import * as os from 'os';

describe('FileRedisService Integration', () => {
  let service: FileRedisService;
  let module: TestingModule;
  let testDataDir: string;
  let testDataFile: string;

  beforeAll(async () => {
    // 创建临时测试目录
    testDataDir = path.join(os.tmpdir(), 'redis-test-' + Date.now());
    testDataFile = path.join(testDataDir, 'redis.json');

    // 确保测试目录存在
    await fs.mkdir(testDataDir, { recursive: true });

    module = await Test.createTestingModule({
      providers: [FileRedisService],
    }).compile();

    service = module.get<FileRedisService>(FileRedisService);

    // 修改服务实例的数据目录路径
    (service as any).DATA_DIR = testDataDir;
    (service as any).DATA_FILE = testDataFile;

    // 重新初始化存储以使用新的路径
    await (service as any).initializeStorage();

    // 等待服务初始化完成
    await new Promise(resolve => setTimeout(resolve, 100));
  });

  afterAll(async () => {
    if (module) {
      await module.close();
    }

    // 清理测试文件
    try {
      await fs.rm(testDataDir, { recursive: true, force: true });
    } catch (error) {
      // 忽略清理错误
    }
  });

  beforeEach(async () => {
    // 每个测试前清空数据
    await service.flushall();
  });

  describe('文件系统初始化', () => {
    it('should create data directory on initialization', async () => {
      const stats = await fs.stat(testDataDir);
      expect(stats.isDirectory()).toBe(true);
    });

    it('should create data file after first write operation', async () => {
      await service.set('test:init', 'initialization test');

      const stats = await fs.stat(testDataFile);
      expect(stats.isFile()).toBe(true);
    });

    it('should load existing data from file on restart', async () => {
      // 设置一些数据
      await service.set('test:persist', 'persistent data');
      await service.set('test:number', '42');
      await service.sadd('test:set', 'member1');

      // 创建新的服务实例来模拟重启
      const newModule = await Test.createTestingModule({
        providers: [FileRedisService],
      }).compile();
      
      const newService = newModule.get<FileRedisService>(FileRedisService);
      
      // 修改新服务实例的数据目录路径
      (newService as any).DATA_DIR = testDataDir;
      (newService as any).DATA_FILE = testDataFile;
      
      // 重新初始化存储
      await newService.initializeStorage();
      
      // 等待新服务初始化完成
      await new Promise(resolve => setTimeout(resolve, 100));

      // 验证数据是否被正确加载
      const persistentData = await newService.get('test:persist');
      const numberData = await newService.get('test:number');
      const setMembers = await newService.smembers('test:set');

      expect(persistentData).toBe('persistent data');
      expect(numberData).toBe('42');
      expect(setMembers).toContain('member1');
      
      // 清理新服务
      await newModule.close();
    });

    it('should handle corrupted data file gracefully', async () => {
      // 写入无效的JSON数据
      await fs.writeFile(testDataFile, 'invalid json content');

      // 创建新的服务实例
      const newModule = await Test.createTestingModule({
        providers: [FileRedisService],
      }).compile();
      
      const newService = newModule.get<FileRedisService>(FileRedisService);
      
      // 修改新服务实例的数据目录路径
      (newService as any).DATA_DIR = testDataDir;
      (newService as any).DATA_FILE = testDataFile;
      
      // 重新初始化存储
      await newService.initializeStorage();
      
      await new Promise(resolve => setTimeout(resolve, 100));

      // 应该能正常工作，从空数据开始
      await newService.set('test:recovery', 'recovered');
      const result = await newService.get('test:recovery');

      expect(result).toBe('recovered');
      
      // 清理新服务
      await newModule.close();
    });

    it('should handle missing data file gracefully', async () => {
      // 删除数据文件
      try {
        await fs.unlink(testDataFile);
      } catch (error) {
        // 文件可能不存在，忽略错误
      }

      // 创建新的服务实例
      const newModule = await Test.createTestingModule({
        providers: [FileRedisService],
      }).compile();
      
      const newService = newModule.get<FileRedisService>(FileRedisService);
      
      // 修改新服务实例的数据目录路径
      (newService as any).DATA_DIR = testDataDir;
      (newService as any).DATA_FILE = testDataFile;
      
      // 重新初始化存储
      await newService.initializeStorage();
      
      await new Promise(resolve => setTimeout(resolve, 100));

      // 应该能正常工作
      await newService.set('test:new_start', 'new beginning');
      const result = await newService.get('test:new_start');

      expect(result).toBe('new beginning');
      
      // 清理新服务
      await newModule.close();
    });
  });

  describe('数据持久化', () => {
    it('should persist data to file after each operation', async () => {
      await service.set('test:file_persist', 'file persistence test');

      // 读取文件内容
      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);

      expect(data['test:file_persist']).toBeDefined();
      expect(data['test:file_persist'].value).toBe('file persistence test');
    });

    it('should maintain data format in JSON file', async () => {
      await service.set('test:string', 'string value');
      await service.set('test:with_ttl', 'ttl value', 3600);
      await service.sadd('test:set', 'set member');

      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);

      // 验证字符串数据格式
      expect(data['test:string']).toEqual({
        value: 'string value'
      });

      // 验证带TTL的数据格式
      expect(data['test:with_ttl']).toEqual({
        value: 'ttl value',
        expireAt: expect.any(Number)
      });

      // 验证集合数据格式
      expect(data['test:set']).toEqual({
        value: expect.stringContaining('set member')
      });
    });

    it('should handle concurrent write operations', async () => {
      // 并发执行多个写操作
      const promises = [];
      for (let i = 0; i < 10; i++) {
        promises.push(service.set(`test:concurrent:${i}`, `value${i}`));
      }

      await Promise.all(promises);

      // 验证所有数据都被正确保存
      for (let i = 0; i < 10; i++) {
        const value = await service.get(`test:concurrent:${i}`);
        expect(value).toBe(`value${i}`);
      }

      // 验证文件内容
      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);

      for (let i = 0; i < 10; i++) {
        expect(data[`test:concurrent:${i}`]).toBeDefined();
      }
    });
  });

  describe('过期机制集成测试', () => {
    it('should automatically clean expired keys', async () => {
      jest.useFakeTimers();

      // 设置一些带过期时间的键
      await service.set('test:expire1', 'expires in 1 sec', 1);
      await service.set('test:expire2', 'expires in 2 sec', 2);
      await service.set('test:permanent', 'never expires');

      // 模拟时间流逝
      jest.advanceTimersByTime(1500); // 1.5秒后

      // 手动触发清理（模拟定时器执行）
      await (service as any).cleanExpiredKeys();

      // 验证过期键被清理
      const expired1 = await service.get('test:expire1');
      const notExpired = await service.get('test:expire2');
      const permanent = await service.get('test:permanent');

      expect(expired1).toBeNull();
      expect(notExpired).toBe('expires in 2 sec');
      expect(permanent).toBe('never expires');

      // 验证文件中也被清理
      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);

      expect(data['test:expire1']).toBeUndefined();
      expect(data['test:expire2']).toBeDefined();
      expect(data['test:permanent']).toBeDefined();

      jest.useRealTimers();
    });

    it('should filter expired data during file loading', async () => {
      const now = Date.now();

      // 手动创建包含过期数据的文件
      const testData = {
        'valid_key': { value: 'valid value' },
        'expired_key': { value: 'expired value', expireAt: now - 1000 },
        'future_key': { value: 'future value', expireAt: now + 3600000 }
      };

      await fs.writeFile(testDataFile, JSON.stringify(testData, null, 2));

      // 创建新的服务实例来加载数据
      const newModule = await Test.createTestingModule({
        providers: [FileRedisService],
      }).compile();
      
      const newService = newModule.get<FileRedisService>(FileRedisService);
      
      // 修改新服务实例的数据目录路径
      (newService as any).DATA_DIR = testDataDir;
      (newService as any).DATA_FILE = testDataFile;
      
      // 重新初始化存储
      await newService.initializeStorage();
      
      await new Promise(resolve => setTimeout(resolve, 100));

      // 验证只有有效数据被加载
      const validValue = await newService.get('valid_key');
      const expiredValue = await newService.get('expired_key');
      const futureValue = await newService.get('future_key');

      expect(validValue).toBe('valid value');
      expect(expiredValue).toBeNull();
      expect(futureValue).toBe('future value');
      
      // 清理新服务
      await newModule.close();
    }, 10000);

    it('should handle TTL operations correctly', async () => {
      jest.useFakeTimers();
      const now = Date.now();
      jest.setSystemTime(now);

      // 设置带TTL的键
      await service.set('test:ttl', 'ttl test', 3600);

      // 检查TTL
      const ttl1 = await service.ttl('test:ttl');
      expect(ttl1).toBe(3600);

      // 模拟时间流逝
      jest.advanceTimersByTime(1800 * 1000); // 30分钟

      const ttl2 = await service.ttl('test:ttl');
      expect(ttl2).toBe(1800);

      // 设置新的过期时间
      await service.expire('test:ttl', 600);
      const ttl3 = await service.ttl('test:ttl');
      expect(ttl3).toBe(600);

      jest.useRealTimers();
    });
  });

  describe('集合操作集成测试', () => {
    it('should persist set operations to file', async () => {
      await service.sadd('test:file_set', 'member1');
      await service.sadd('test:file_set', 'member2');
      await service.sadd('test:file_set', 'member3');

      // 验证文件内容
      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);

      const setData = JSON.parse(data['test:file_set'].value);
      expect(setData).toContain('member1');
      expect(setData).toContain('member2');
      expect(setData).toContain('member3');
    });

    it('should handle set operations with expiration', async () => {
      jest.useFakeTimers();

      await service.sadd('test:expire_set', 'member1');
      await service.expire('test:expire_set', 2);
      await service.sadd('test:expire_set', 'member2');

      // 验证过期时间被保持
      const ttl = await service.ttl('test:expire_set');
      expect(ttl).toBe(2);

      // 验证成员都存在
      const members = await service.smembers('test:expire_set');
      expect(members).toContain('member1');
      expect(members).toContain('member2');

      jest.useRealTimers();
    });

    it('should clean up empty sets after member removal', async () => {
      await service.sadd('test:cleanup_set', 'only_member');
      await service.srem('test:cleanup_set', 'only_member');

      // 验证集合被删除
      const members = await service.smembers('test:cleanup_set');
      expect(members).toEqual([]);

      // 验证文件中也被删除
      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);
      expect(data['test:cleanup_set']).toBeUndefined();
    });
  });

  describe('数值操作集成测试', () => {
    it('should persist counter increments', async () => {
      await service.incr('test:file_counter');
      await service.incr('test:file_counter');
      await service.incr('test:file_counter');

      // 验证内存中的值
      const memoryValue = await service.get('test:file_counter');
      expect(memoryValue).toBe('3');

      // 验证文件中的值
      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);
      expect(data['test:file_counter'].value).toBe('3');
    });

    it('should maintain counter state across service restarts', async () => {
      await service.incr('test:persistent_counter');
      await service.incr('test:persistent_counter');

      // 创建新的服务实例
      const newModule = await Test.createTestingModule({
        providers: [FileRedisService],
      }).compile();
      
      const newService = newModule.get<FileRedisService>(FileRedisService);
      
      // 修改新服务实例的数据目录路径
      (newService as any).DATA_DIR = testDataDir;
      (newService as any).DATA_FILE = testDataFile;
      
      // 重新初始化存储
      await newService.initializeStorage();
      
      await new Promise(resolve => setTimeout(resolve, 100));

      // 继续递增
      const result = await newService.incr('test:persistent_counter');
      expect(result).toBe(3);
      
      // 清理新服务
      await newModule.close();
    });
  });

  describe('错误处理和恢复', () => {
    it('should handle file system permission errors gracefully', async () => {
      // 这个测试在某些环境下可能无法执行，所以使用try-catch
      try {
        // 尝试创建只读目录（在某些系统上可能不起作用）
        const readOnlyDir = path.join(os.tmpdir(), 'readonly-redis-test');
        await fs.mkdir(readOnlyDir, { mode: 0o444 });

        // 修改服务的数据目录
        (service as any).DATA_DIR = readOnlyDir;
        (service as any).DATA_FILE = path.join(readOnlyDir, 'redis.json');

        // 尝试写入数据（应该不会抛出异常）
        await expect(service.set('test:readonly', 'test')).resolves.not.toThrow();

        // 清理
        await fs.rm(readOnlyDir, { recursive: true, force: true });
      } catch (error) {
        // 如果无法创建只读目录，跳过此测试
        console.warn('无法测试文件系统权限错误，跳过此测试');
      }
    });

    it('should recover from disk space issues', async () => {
      // 模拟磁盘空间不足的情况比较困难，这里主要测试错误处理逻辑
      const originalWriteFile = fs.writeFile;
      
      // Mock writeFile to simulate disk space error
      (fs.writeFile as jest.Mock) = jest.fn().mockRejectedValueOnce(
        new Error('ENOSPC: no space left on device')
      );

      // 应该不会抛出异常
      await expect(service.set('test:disk_full', 'test')).resolves.not.toThrow();

      // 恢复原始函数
      (fs.writeFile as any) = originalWriteFile;
    });
  });

  describe('性能和大数据量测试', () => {
    it('should handle large amounts of data efficiently', async () => {
      const startTime = Date.now();
      const dataCount = 1000;

      // 写入大量数据
      const writePromises = [];
      for (let i = 0; i < dataCount; i++) {
        writePromises.push(service.set(`test:large:${i}`, `value${i}`));
      }
      await Promise.all(writePromises);

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

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

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

      // 验证文件大小合理
      const stats = await fs.stat(testDataFile);
      expect(stats.size).toBeGreaterThan(0);

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

      console.log(`处理${dataCount}条数据耗时: ${duration}ms, 文件大小: ${stats.size} bytes`);
    }, 15000);

    it('should handle very large values', async () => {
      const largeValue = 'x'.repeat(100000); // 100KB的数据

      await service.set('test:large_value', largeValue);
      const result = await service.get('test:large_value');

      expect(result).toBe(largeValue);

      // 验证文件能正确存储大数据
      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);
      expect(data['test:large_value'].value).toBe(largeValue);
    });
  });

  describe('数据完整性验证', () => {
    it('should maintain data integrity across multiple operations', async () => {
      // 执行各种操作的组合
      await service.set('test:integrity:string', 'string value');
      await service.set('test:integrity:number', '42');
      await service.set('test:integrity:ttl', 'ttl value', 3600);
      await service.sadd('test:integrity:set', 'member1');
      await service.sadd('test:integrity:set', 'member2');
      await service.incr('test:integrity:counter');
      await service.incr('test:integrity:counter');

      // 验证所有数据的完整性
      const stringValue = await service.get('test:integrity:string');
      const numberValue = await service.get('test:integrity:number');
      const ttlValue = await service.get('test:integrity:ttl');
      const setMembers = await service.smembers('test:integrity:set');
      const counterValue = await service.get('test:integrity:counter');
      const ttl = await service.ttl('test:integrity:ttl');

      expect(stringValue).toBe('string value');
      expect(numberValue).toBe('42');
      expect(ttlValue).toBe('ttl value');
      expect(setMembers).toHaveLength(2);
      expect(setMembers).toContain('member1');
      expect(setMembers).toContain('member2');
      expect(counterValue).toBe('2');
      expect(ttl).toBeGreaterThan(0);

      // 验证文件内容的完整性
      const fileContent = await fs.readFile(testDataFile, 'utf-8');
      const data = JSON.parse(fileContent);

      expect(Object.keys(data)).toHaveLength(5);
      expect(data['test:integrity:string'].value).toBe('string value');
      expect(data['test:integrity:number'].value).toBe('42');
      expect(data['test:integrity:ttl'].value).toBe('ttl value');
      expect(data['test:integrity:ttl'].expireAt).toBeGreaterThan(Date.now());
      expect(data['test:integrity:set'].value).toContain('member1');
      expect(data['test:integrity:counter'].value).toBe('2');
    });
  });
});