/**
 * 位置更新性能测试
 * 
 * 功能描述：
 * - 测试位置更新的性能指标
 * - 验证系统在高负载下的表现
 * - 确保响应时间满足要求
 * - 提供性能基准数据
 * 
 * 测试指标：
 * - 位置更新响应时间
 * - 并发用户处理能力
 * - 内存使用情况
 * - 系统吞吐量
 * 
 * 最近修改：
 * - 2026-01-08: 文件重命名 - 修正kebab-case为snake_case命名规范 (修改者: moyin)
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { io, Socket } from 'socket.io-client';
import { LocationBroadcastModule } from '../../location_broadcast.module';
import { RedisModule } from '../../../../core/redis/redis.module';
import { LoginCoreModule } from '../../../../core/login_core/login_core.module';
import { UserProfilesModule } from '../../../../core/db/user_profiles/user_profiles.module';

describe('位置更新性能测试', () => {
  let app: INestApplication;
  let authToken: string;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [
        LocationBroadcastModule,
        RedisModule,
        LoginCoreModule,
        UserProfilesModule.forMemory(),
      ],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
    await app.listen(0);

    authToken = 'test-jwt-token';
  });

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

  describe('单用户位置更新性能', () => {
    let client: Socket;

    beforeEach((done) => {
      const port = app.getHttpServer().address().port;
      client = io(`http://localhost:${port}/location-broadcast`, {
        auth: { token: authToken },
        transports: ['websocket'],
      });

      client.on('connect', () => {
        client.emit('join_session', {
          type: 'join_session',
          sessionId: 'perf-session-001',
          initialPosition: { x: 100, y: 200, mapId: 'plaza' },
        });
      });

      client.on('session_joined', () => {
        done();
      });
    });

    afterEach(() => {
      if (client) {
        client.disconnect();
      }
    });

    it('应该在100ms内响应位置更新', (done) => {
      const startTime = Date.now();
      
      client.emit('position_update', {
        type: 'position_update',
        x: 150,
        y: 250,
        mapId: 'plaza',
        timestamp: startTime,
      });

      client.on('position_update_success', () => {
        const responseTime = Date.now() - startTime;
        console.log(`位置更新响应时间: ${responseTime}ms`);
        
        expect(responseTime).toBeLessThan(100);
        done();
      });

      client.on('error', (error) => {
        done(error);
      });
    });

    it('应该支持高频率位置更新', (done) => {
      const updateCount = 100;
      let completedUpdates = 0;

      const startTime = Date.now();

      for (let i = 0; i < updateCount; i++) {
        const updateStartTime = Date.now();
        
        client.emit('position_update', {
          type: 'position_update',
          x: 100 + i,
          y: 200 + i,
          mapId: 'plaza',
          timestamp: updateStartTime,
        });
      }

      client.on('position_update_success', () => {
        completedUpdates++;
        
        if (completedUpdates === updateCount) {
          const totalTime = Date.now() - startTime;
          const avgTime = totalTime / updateCount;
          
          console.log(`${updateCount}次位置更新总耗时: ${totalTime}ms`);
          console.log(`平均每次更新耗时: ${avgTime}ms`);
          console.log(`更新频率: ${(updateCount / totalTime * 1000).toFixed(2)} updates/sec`);
          
          expect(avgTime).toBeLessThan(50); // 平均响应时间应小于50ms
          done();
        }
      });

      client.on('error', (error) => {
        done(error);
      });

      // 超时保护
      const timeoutId = setTimeout(() => {
        if (completedUpdates < updateCount) {
          done(new Error(`只完成了 ${completedUpdates}/${updateCount} 次更新`));
        }
      }, 10000);
    });
  });

  describe('多用户并发性能', () => {
    it('应该支持100个并发用户', (done) => {
      const userCount = 100;
      const clients: Socket[] = [];
      const sessionId = 'perf-session-concurrent';
      let connectedUsers = 0;
      let joinedUsers = 0;
      let updateResponses = 0;

      const port = app.getHttpServer().address().port;
      const startTime = Date.now();

      // 创建多个客户端连接
      for (let i = 0; i < userCount; i++) {
        const client = io(`http://localhost:${port}/location-broadcast`, {
          auth: { token: authToken },
          transports: ['websocket'],
        });

        clients.push(client);

        client.on('connect', () => {
          connectedUsers++;
          
          if (connectedUsers === userCount) {
            const connectTime = Date.now() - startTime;
            console.log(`${userCount}个用户连接耗时: ${connectTime}ms`);
            
            // 所有用户加入会话
            clients.forEach((c, index) => {
              c.emit('join_session', {
                type: 'join_session',
                sessionId,
                initialPosition: {
                  x: 100 + index,
                  y: 200 + index,
                  mapId: 'plaza',
                },
              });
            });
          }
        });

        client.on('session_joined', () => {
          joinedUsers++;
          
          if (joinedUsers === userCount) {
            const joinTime = Date.now() - startTime;
            console.log(`${userCount}个用户加入会话耗时: ${joinTime}ms`);
            
            // 所有用户同时更新位置
            clients.forEach((c, index) => {
              c.emit('position_update', {
                type: 'position_update',
                x: 200 + index,
                y: 300 + index,
                mapId: 'plaza',
              });
            });
          }
        });

        client.on('position_update_success', () => {
          updateResponses++;
          
          if (updateResponses === userCount) {
            const totalTime = Date.now() - startTime;
            console.log(`${userCount}个用户完整流程耗时: ${totalTime}ms`);
            console.log(`平均每用户耗时: ${(totalTime / userCount).toFixed(2)}ms`);
            
            // 清理连接
            clients.forEach(c => c.disconnect());
            
            expect(totalTime).toBeLessThan(10000); // 总时间应小于10秒
            done();
          }
        });

        client.on('error', (error) => {
          clients.forEach(c => c.disconnect());
          done(error);
        });
      }

      // 超时保护
      const timeoutId = setTimeout(() => {
        clients.forEach(c => c.disconnect());
        done(new Error(`测试超时，连接用户: ${connectedUsers}, 加入用户: ${joinedUsers}, 更新响应: ${updateResponses}`));
      }, 30000);
    });

    it('应该支持持续的位置广播', (done) => {
      const userCount = 10;
      const updatesPerUser = 50;
      const clients: Socket[] = [];
      const sessionId = 'perf-session-broadcast';
      let totalBroadcasts = 0;
      let expectedBroadcasts = userCount * updatesPerUser * (userCount - 1); // 每次更新广播给其他用户

      const port = app.getHttpServer().address().port;
      const startTime = Date.now();

      // 创建多个客户端
      for (let i = 0; i < userCount; i++) {
        const client = io(`http://localhost:${port}/location-broadcast`, {
          auth: { token: authToken },
          transports: ['websocket'],
        });

        clients.push(client);

        client.on('connect', () => {
          client.emit('join_session', {
            type: 'join_session',
            sessionId,
            initialPosition: {
              x: 100 + i * 10,
              y: 200 + i * 10,
              mapId: 'plaza',
            },
          });
        });

        client.on('position_broadcast', () => {
          totalBroadcasts++;
          
          if (totalBroadcasts >= expectedBroadcasts * 0.8) { // 允许80%的广播成功
            const totalTime = Date.now() - startTime;
            const broadcastRate = totalBroadcasts / totalTime * 1000;
            
            console.log(`广播测试完成: ${totalBroadcasts}/${expectedBroadcasts} 条广播`);
            console.log(`总耗时: ${totalTime}ms`);
            console.log(`广播频率: ${broadcastRate.toFixed(2)} broadcasts/sec`);
            
            clients.forEach(c => c.disconnect());
            done();
          }
        });
      }

      // 等待所有用户连接后开始更新
      const startUpdateTimer = setTimeout(() => {
        clients.forEach((client, userIndex) => {
          for (let updateIndex = 0; updateIndex < updatesPerUser; updateIndex++) {
            const updateTimer = setTimeout(() => {
              client.emit('position_update', {
                type: 'position_update',
                x: 100 + userIndex * 10 + updateIndex,
                y: 200 + userIndex * 10 + updateIndex,
                mapId: 'plaza',
              });
            }, updateIndex * 10); // 每10ms发送一次更新
          }
        });
      }, 1000);

      // 超时保护
      const timeoutId = setTimeout(() => {
        clients.forEach(c => c.disconnect());
        console.log(`测试超时，收到广播: ${totalBroadcasts}/${expectedBroadcasts}`);
        done();
      }, 20000);
    });
  });

  describe('内存和资源使用', () => {
    it('应该在合理范围内使用内存', async () => {
      const initialMemory = process.memoryUsage();
      const userCount = 50;
      const clients: Socket[] = [];
      const port = app.getHttpServer().address().port;

      // 创建多个连接
      for (let i = 0; i < userCount; i++) {
        const client = io(`http://localhost:${port}/location-broadcast`, {
          auth: { token: authToken },
          transports: ['websocket'],
        });
        clients.push(client);
      }

      // 等待连接建立
      await new Promise(resolve => setTimeout(resolve, 2000));

      const peakMemory = process.memoryUsage();
      const memoryIncrease = peakMemory.heapUsed - initialMemory.heapUsed;
      const memoryPerUser = memoryIncrease / userCount;

      console.log(`初始内存使用: ${(initialMemory.heapUsed / 1024 / 1024).toFixed(2)} MB`);
      console.log(`峰值内存使用: ${(peakMemory.heapUsed / 1024 / 1024).toFixed(2)} MB`);
      console.log(`内存增长: ${(memoryIncrease / 1024 / 1024).toFixed(2)} MB`);
      console.log(`每用户内存: ${(memoryPerUser / 1024).toFixed(2)} KB`);

      // 清理连接
      clients.forEach(c => c.disconnect());

      // 等待清理完成
      await new Promise(resolve => setTimeout(resolve, 1000));

      const finalMemory = process.memoryUsage();
      console.log(`清理后内存: ${(finalMemory.heapUsed / 1024 / 1024).toFixed(2)} MB`);

      // 每个用户的内存使用应该小于100KB
      expect(memoryPerUser).toBeLessThan(100 * 1024);
    });

    it('应该正确清理断开连接的用户', (done) => {
      const port = app.getHttpServer().address().port;
      const sessionId = 'cleanup-test-session';
      
      const client = io(`http://localhost:${port}/location-broadcast`, {
        auth: { token: authToken },
        transports: ['websocket'],
      });

      client.on('connect', () => {
        client.emit('join_session', {
          type: 'join_session',
          sessionId,
          initialPosition: { x: 100, y: 200, mapId: 'plaza' },
        });
      });

      client.on('session_joined', () => {
        // 突然断开连接
        client.disconnect();
        
        // 等待系统清理
        setTimeout(() => {
          // 创建新连接验证清理是否成功
          const newClient = io(`http://localhost:${port}/location-broadcast`, {
            auth: { token: authToken },
            transports: ['websocket'],
          });

          newClient.on('connect', () => {
            newClient.emit('join_session', {
              type: 'join_session',
              sessionId,
              initialPosition: { x: 100, y: 200, mapId: 'plaza' },
            });
          });

          newClient.on('session_joined', (response) => {
            // 如果能成功加入，说明之前的用户已被清理
            expect(response.success).toBe(true);
            newClient.disconnect();
            done();
          });

          newClient.on('error', (error) => {
            newClient.disconnect();
            done(error);
          });
        }, 2000);
      });

      client.on('error', (error) => {
        done(error);
      });
    });
  });

  describe('压力测试', () => {
    it('应该在高频率更新下保持稳定', (done) => {
      const port = app.getHttpServer().address().port;
      const sessionId = 'stress-test-session';
      const updateInterval = 10; // 10ms间隔
      const testDuration = 5000; // 5秒测试
      
      let updateCount = 0;
      let responseCount = 0;
      let errorCount = 0;
      let updateTimer: NodeJS.Timeout | null = null;
      let timeoutTimer: NodeJS.Timeout | null = null;

      const client = io(`http://localhost:${port}/location-broadcast`, {
        auth: { token: authToken },
        transports: ['websocket'],
      });

      const cleanup = () => {
        if (updateTimer) {
          clearInterval(updateTimer);
          updateTimer = null;
        }
        if (timeoutTimer) {
          clearTimeout(timeoutTimer);
          timeoutTimer = null;
        }
        if (client && client.connected) {
          client.disconnect();
        }
      };

      client.on('connect', () => {
        client.emit('join_session', {
          type: 'join_session',
          sessionId,
          initialPosition: { x: 100, y: 200, mapId: 'plaza' },
        });
      });

      client.on('session_joined', () => {
        const startTime = Date.now();
        
        updateTimer = setInterval(() => {
          if (Date.now() - startTime >= testDuration) {
            clearInterval(updateTimer!);
            updateTimer = null;
            
            // 等待最后的响应
            setTimeout(() => {
              const successRate = (responseCount / updateCount) * 100;
              const errorRate = (errorCount / updateCount) * 100;
              
              console.log(`压力测试结果:`);
              console.log(`- 发送更新: ${updateCount}`);
              console.log(`- 成功响应: ${responseCount}`);
              console.log(`- 错误数量: ${errorCount}`);
              console.log(`- 成功率: ${successRate.toFixed(2)}%`);
              console.log(`- 错误率: ${errorRate.toFixed(2)}%`);
              
              cleanup();
              
              // 成功率应该大于95%
              expect(successRate).toBeGreaterThan(95);
              done();
            }, 1000);
            return;
          }

          updateCount++;
          client.emit('position_update', {
            type: 'position_update',
            x: 100 + (updateCount % 100),
            y: 200 + (updateCount % 100),
            mapId: 'plaza',
          });
        }, updateInterval);
      });

      client.on('position_update_success', () => {
        responseCount++;
      });

      client.on('error', () => {
        errorCount++;
      });

      // 超时保护
      timeoutTimer = setTimeout(() => {
        cleanup();
        done(new Error('压力测试超时'));
      }, testDuration + 5000);
    });
  });
});