/**
 * Zulip聊天性能测试
 * 
 * 功能描述：
 * - 测试优化后聊天架构的性能表现
 * - 验证游戏内实时广播 + Zulip异步同步的效果
 * - 测试高并发场景下的系统稳定性
 * 
 * 测试场景：
 * - 单用户消息发送性能
 * - 多用户并发聊天性能
 * - 大量消息批量处理性能
 * - 内存使用和资源清理
 * 
 * 更新记录：
 * - 2026-01-14: 重构后更新 - 使用新的四层架构模块
 *   - ChatService 替代 ZulipService
 *   - ChatSessionService 替代 SessionManagerService
 *   - ChatFilterService 替代 MessageFilterService
 * 
 * @author moyin
 * @version 2.0.0
 * @since 2026-01-10
 * @lastModified 2026-01-14
 */

import { Test, TestingModule } from '@nestjs/testing';
import { ChatService } from '../../../src/business/chat/chat.service';
import { ChatSessionService } from '../../../src/business/chat/services/chat_session.service';
import { ChatFilterService } from '../../../src/business/chat/services/chat_filter.service';
import { ZulipClientPoolService } from '../../../src/core/zulip_core/services/zulip_client_pool.service';

// 模拟WebSocket网关
class MockWebSocketGateway {
  private sentMessages: Array<{ socketId: string; data: any }> = [];
  private broadcastMessages: Array<{ mapId: string; data: any }> = [];

  sendToPlayer(socketId: string, data: any): void {
    this.sentMessages.push({ socketId, data });
  }

  broadcastToMap(mapId: string, data: any, excludeId?: string): void {
    this.broadcastMessages.push({ mapId, data });
  }

  getSentMessages() { return this.sentMessages; }
  getBroadcastMessages() { return this.broadcastMessages; }
  clearMessages() { 
    this.sentMessages = []; 
    this.broadcastMessages = []; 
  }
}

describe('Zulip聊天性能测试', () => {
  let chatService: ChatService;
  let sessionManager: ChatSessionService;
  let mockWebSocketGateway: MockWebSocketGateway;
  let mockZulipClientPool: any;

  beforeAll(async () => {
    // 创建模拟服务
    mockZulipClientPool = {
      sendMessage: jest.fn().mockResolvedValue({
        success: true,
        messageId: 'zulip-msg-123',
      }),
      createUserClient: jest.fn(),
      destroyUserClient: jest.fn(),
    };

    const mockSessionManager = {
      getSession: jest.fn().mockResolvedValue({
        sessionId: 'test-session',
        userId: 'user-123',
        username: 'TestPlayer',
        currentMap: 'whale_port',
        position: { x: 100, y: 200 },
      }),
      injectContext: jest.fn().mockResolvedValue({
        stream: 'Whale Port',
        topic: 'Town Square Chat',
      }),
      getSocketsInMap: jest.fn().mockResolvedValue(['socket-1', 'socket-2', 'socket-3']),
      createSession: jest.fn(),
      destroySession: jest.fn(),
      updatePlayerPosition: jest.fn().mockResolvedValue(true),
    };

    const mockMessageFilter = {
      validateMessage: jest.fn().mockResolvedValue({
        allowed: true,
        filteredContent: null,
      }),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ChatService,
        {
          provide: 'ZULIP_CLIENT_POOL_SERVICE',
          useValue: mockZulipClientPool,
        },
        {
          provide: ChatSessionService,
          useValue: mockSessionManager,
        },
        {
          provide: ChatFilterService,
          useValue: mockMessageFilter,
        },
        {
          provide: 'API_KEY_SECURITY_SERVICE',
          useValue: {
            getApiKey: jest.fn().mockResolvedValue({
              success: true,
              apiKey: 'test-api-key',
            }),
          },
        },
        {
          provide: 'LoginCoreService',
          useValue: {
            verifyToken: jest.fn().mockResolvedValue({
              sub: 'user-123',
              username: 'TestPlayer',
              email: 'test@example.com',
            }),
          },
        },
      ],
    }).compile();

    chatService = module.get<ChatService>(ChatService);
    sessionManager = module.get<ChatSessionService>(ChatSessionService);

    // 设置WebSocket网关
    mockWebSocketGateway = new MockWebSocketGateway();
    chatService.setWebSocketGateway(mockWebSocketGateway as any);
  });

  beforeEach(() => {
    jest.clearAllMocks();
    mockWebSocketGateway.clearMessages();
  });

  describe('单用户消息发送性能', () => {
    it('应该在50ms内完成游戏内广播', async () => {
      const startTime = Date.now();

      const result = await chatService.sendChatMessage({
        socketId: 'test-socket',
        content: 'Performance test message',
        scope: 'local',
      });

      const duration = Date.now() - startTime;

      expect(result.success).toBe(true);
      expect(duration).toBeLessThan(50); // 游戏内广播应该在50ms内完成
      
      console.log(`游戏内广播耗时: ${duration}ms`);
    });

    it('应该异步处理Zulip同步，不阻塞游戏聊天', async () => {
      // 模拟Zulip同步延迟
      mockZulipClientPool.sendMessage.mockImplementation(() => 
        new Promise(resolve => setTimeout(() => resolve({
          success: true,
          messageId: 'delayed-msg',
        }), 200))
      );

      const startTime = Date.now();

      const result = await chatService.sendChatMessage({
        socketId: 'test-socket',
        content: 'Async test message',
        scope: 'local',
      });

      const duration = Date.now() - startTime;

      expect(result.success).toBe(true);
      expect(duration).toBeLessThan(100); // 不应该等待Zulip同步完成
      
      console.log(`异步处理耗时: ${duration}ms`);
    });
  });

  describe('多用户并发聊天性能', () => {
    it('应该处理50个并发消息', async () => {
      const messageCount = 50;
      const startTime = Date.now();

      const promises = Array.from({ length: messageCount }, (_, i) =>
        chatService.sendChatMessage({
          socketId: `socket-${i}`,
          content: `Concurrent message ${i}`,
          scope: 'local',
        })
      );

      const results = await Promise.all(promises);
      const duration = Date.now() - startTime;

      // 验证所有消息都成功处理
      expect(results).toHaveLength(messageCount);
      results.forEach(result => {
        expect(result.success).toBe(true);
      });

      const avgTimePerMessage = duration / messageCount;
      console.log(`处理${messageCount}条并发消息耗时: ${duration}ms, 平均每条: ${avgTimePerMessage.toFixed(2)}ms`);
      
      // 期望平均每条消息处理时间不超过20ms
      expect(avgTimePerMessage).toBeLessThan(20);
    }, 10000);

    it('应该正确广播给地图内的所有玩家', async () => {
      await chatService.sendChatMessage({
        socketId: 'sender-socket',
        content: 'Broadcast test message',
        scope: 'local',
      });

      // 验证广播消息
      const broadcastMessages = mockWebSocketGateway.getBroadcastMessages();
      expect(broadcastMessages).toHaveLength(1);
      
      const broadcastMessage = broadcastMessages[0];
      expect(broadcastMessage.mapId).toBe('whale_port');
      expect(broadcastMessage.data.t).toBe('chat_render');
      expect(broadcastMessage.data.txt).toBe('Broadcast test message');
    });
  });

  describe('批量消息处理性能', () => {
    it('应该高效处理大量消息', async () => {
      const batchSize = 100;
      const startTime = Date.now();

      // 创建批量消息
      const batchPromises = Array.from({ length: batchSize }, (_, i) =>
        chatService.sendChatMessage({
          socketId: 'batch-socket',
          content: `Batch message ${i}`,
          scope: 'local',
        })
      );

      const results = await Promise.all(batchPromises);
      const duration = Date.now() - startTime;

      // 验证处理结果
      expect(results).toHaveLength(batchSize);
      results.forEach((result, index) => {
        expect(result.success).toBe(true);
        expect(result.messageId).toBeDefined();
      });

      const throughput = (batchSize / duration) * 1000; // 每秒处理的消息数
      console.log(`批量处理${batchSize}条消息耗时: ${duration}ms, 吞吐量: ${throughput.toFixed(2)} msg/s`);
      
      // 期望吞吐量至少达到500 msg/s
      expect(throughput).toBeGreaterThan(500);
    }, 15000);
  });

  describe('内存使用和资源清理', () => {
    it('应该正确清理会话资源', async () => {
      // 创建多个会话
      const sessionCount = 10;
      const sessionIds = Array.from({ length: sessionCount }, (_, i) => `session-${i}`);

      // 模拟会话创建
      for (const sessionId of sessionIds) {
        await chatService.handlePlayerLogin({
          socketId: sessionId,
          token: 'valid-jwt-token',
        });
      }

      // 清理所有会话
      for (const sessionId of sessionIds) {
        await chatService.handlePlayerLogout(sessionId);
      }

      // 验证资源清理
      expect(mockZulipClientPool.destroyUserClient).toHaveBeenCalledTimes(sessionCount);
    });

    it('应该处理内存压力测试', async () => {
      const initialMemory = process.memoryUsage();
      
      // 创建大量临时对象
      const largeDataSet = Array.from({ length: 1000 }, (_, i) => ({
        id: i,
        data: 'x'.repeat(1000), // 1KB per object
        timestamp: new Date(),
      }));

      // 处理大量消息
      const promises = largeDataSet.map((item, i) =>
        chatService.sendChatMessage({
          socketId: `memory-test-${i}`,
          content: `Memory test ${item.id}: ${item.data.substring(0, 50)}...`,
          scope: 'local',
        })
      );

      await Promise.all(promises);

      // 强制垃圾回收（如果可用）
      if (global.gc) {
        global.gc();
      }

      const finalMemory = process.memoryUsage();
      const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed;
      
      console.log(`内存使用增加: ${(memoryIncrease / 1024 / 1024).toFixed(2)} MB`);
      
      // 期望内存增加不超过50MB
      expect(memoryIncrease).toBeLessThan(50 * 1024 * 1024);
    }, 20000);
  });

  describe('错误处理性能', () => {
    it('应该快速处理无效会话', async () => {
      const startTime = Date.now();

      const result = await chatService.sendChatMessage({
        socketId: 'invalid-socket',
        content: 'This should fail quickly',
        scope: 'local',
      });

      const duration = Date.now() - startTime;

      expect(result.success).toBe(false);
      expect(result.error).toContain('会话不存在');
      expect(duration).toBeLessThan(10); // 错误处理应该很快
      
      console.log(`错误处理耗时: ${duration}ms`);
    });

    it('应该处理Zulip服务异常而不影响游戏聊天', async () => {
      // 模拟Zulip服务异常
      mockZulipClientPool.sendMessage.mockRejectedValue(new Error('Zulip service unavailable'));

      const result = await chatService.sendChatMessage({
        socketId: 'test-socket',
        content: 'Message during Zulip outage',
        scope: 'local',
      });

      // 游戏内聊天应该仍然成功
      expect(result.success).toBe(true);
      
      // 验证游戏内广播仍然工作
      const broadcastMessages = mockWebSocketGateway.getBroadcastMessages();
      expect(broadcastMessages).toHaveLength(1);
    });
  });
});
