/**
 * 并发用户测试
 * 
 * 功能描述：
 * - 测试系统在多用户并发场景下的正确性和稳定性
 * - 验证并发位置更新的数据一致性
 * - 确保会话管理在高并发下的可靠性
 * - 测试系统的并发处理能力和性能表现
 * 
 * 测试场景：
 * - 大量用户同时连接和断开
 * - 多用户同时加入/离开会话
 * - 并发位置更新和广播
 * - 数据竞争和一致性验证
 * - 系统资源使用和清理
 * 
 * 验证属性：
 * - Property 17: Concurrent update handling (并发更新处理)
 * - Property 5: Position storage consistency (位置存储一致性)
 * - Property 8: Session-scoped broadcasting (会话范围广播)
 * - Property 1: User session membership consistency (用户会话成员一致性)
 * 
 * 最近修改：
 * - 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 '../../src/business/location_broadcast/location_broadcast.module';

interface TestUser {
  id: string;
  client: Socket;
  sessionId?: string;
  position?: { x: number; y: number; mapId: string };
  connected: boolean;
  joined: boolean;
}

describe('并发用户测试', () => {
  let app: INestApplication;
  let authToken: string;
  let port: number;
  let activeTimers: Set<NodeJS.Timeout> = new Set();

  // 全局定时器管理
  const createTimer = (callback: () => void, delay: number): NodeJS.Timeout => {
    const timer = setTimeout(() => {
      activeTimers.delete(timer);
      callback();
    }, delay);
    activeTimers.add(timer);
    return timer;
  };

  const clearTimer = (timer: NodeJS.Timeout): void => {
    clearTimeout(timer);
    activeTimers.delete(timer);
  };

  const clearAllTimers = (): void => {
    activeTimers.forEach(timer => clearTimeout(timer));
    activeTimers.clear();
  };

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

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

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

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

  afterEach(() => {
    clearAllTimers();
  });

  /**
   * 创建测试用户连接
   */
  const createTestUser = (userId: string): Promise<TestUser> => {
    return new Promise((resolve, reject) => {
      const client = io(`http://localhost:${port}/location-broadcast`, {
        auth: { token: authToken },
        transports: ['websocket'],
      });

      const user: TestUser = {
        id: userId,
        client,
        connected: false,
        joined: false,
      };

      let timeoutId: NodeJS.Timeout | null = null;

      client.on('connect', () => {
        user.connected = true;
        if (timeoutId) {
          clearTimer(timeoutId);
          timeoutId = null;
        }
        resolve(user);
      });

      client.on('connect_error', (error) => {
        if (timeoutId) {
          clearTimer(timeoutId);
          timeoutId = null;
        }
        reject(error);
      });

      // 超时保护
      timeoutId = createTimer(() => {
        if (!user.connected) {
          client.disconnect();
          reject(new Error('Connection timeout'));
        }
        timeoutId = null;
      }, 5000);
    });
  };

  /**
   * 用户加入会话
   */
  const joinSession = (user: TestUser, sessionId: string, initialPosition?: { x: number; y: number; mapId: string }): Promise<void> => {
    return new Promise((resolve, reject) => {
      user.sessionId = sessionId;
      user.position = initialPosition || { x: Math.random() * 1000, y: Math.random() * 1000, mapId: 'plaza' };

      let timeoutId: NodeJS.Timeout | null = null;

      user.client.emit('join_session', {
        type: 'join_session',
        sessionId,
        initialPosition: user.position,
      });

      user.client.on('session_joined', () => {
        user.joined = true;
        if (timeoutId) {
          clearTimer(timeoutId);
          timeoutId = null;
        }
        resolve();
      });

      user.client.on('error', (error) => {
        if (timeoutId) {
          clearTimer(timeoutId);
          timeoutId = null;
        }
        reject(error);
      });

      // 超时保护
      timeoutId = createTimer(() => {
        if (!user.joined) {
          reject(new Error('Join session timeout'));
        }
        timeoutId = null;
      }, 5000);
    });
  };

  /**
   * 清理用户连接
   */
  const cleanupUsers = (users: TestUser[]) => {
    users.forEach(user => {
      if (user.client && user.client.connected) {
        user.client.disconnect();
      }
    });
  };

  describe('大规模并发连接测试', () => {
    it('应该支持100个用户同时连接', async () => {
      const userCount = 100;
      const users: TestUser[] = [];
      const startTime = Date.now();

      try {
        // 并发创建用户连接
        const connectionPromises = Array.from({ length: userCount }, (_, i) => 
          createTestUser(`concurrent-user-${i}`)
        );

        const connectedUsers = await Promise.all(connectionPromises);
        users.push(...connectedUsers);

        const connectionTime = Date.now() - startTime;
        console.log(`${userCount} users connected in ${connectionTime}ms`);
        console.log(`Average connection time: ${(connectionTime / userCount).toFixed(2)}ms per user`);

        // 验证所有用户都已连接
        expect(users.length).toBe(userCount);
        users.forEach(user => {
          expect(user.connected).toBe(true);
          expect(user.client.connected).toBe(true);
        });

        // 连接时间应该在合理范围内（每个用户平均不超过100ms）
        expect(connectionTime / userCount).toBeLessThan(100);

      } finally {
        cleanupUsers(users);
      }
    }, 30000);

    it('应该支持用户快速连接和断开', async () => {
      const userCount = 50;
      const users: TestUser[] = [];

      try {
        // 快速连接
        for (let i = 0; i < userCount; i++) {
          const user = await createTestUser(`rapid-user-${i}`);
          users.push(user);
          
          // 立即断开一半用户
          if (i % 2 === 0) {
            user.client.disconnect();
            user.connected = false;
          }
        }

        // 等待系统处理断开连接
        await new Promise(resolve => setTimeout(resolve, 1000));

        // 验证剩余用户仍然连接
        const connectedUsers = users.filter(user => user.connected);
        expect(connectedUsers.length).toBe(userCount / 2);

        connectedUsers.forEach(user => {
          expect(user.client.connected).toBe(true);
        });

      } finally {
        cleanupUsers(users);
      }
    }, 20000);
  });

  describe('并发会话管理测试', () => {
    it('应该支持多用户同时加入同一会话', async () => {
      const userCount = 50;
      const sessionId = 'concurrent-session-001';
      const users: TestUser[] = [];

      try {
        // 创建用户连接
        for (let i = 0; i < userCount; i++) {
          const user = await createTestUser(`session-user-${i}`);
          users.push(user);
        }

        const startTime = Date.now();

        // 并发加入会话
        const joinPromises = users.map(user => 
          joinSession(user, sessionId, {
            x: Math.random() * 1000,
            y: Math.random() * 1000,
            mapId: 'plaza'
          })
        );

        await Promise.all(joinPromises);

        const joinTime = Date.now() - startTime;
        console.log(`${userCount} users joined session in ${joinTime}ms`);

        // 验证所有用户都成功加入会话
        users.forEach(user => {
          expect(user.joined).toBe(true);
          expect(user.sessionId).toBe(sessionId);
        });

        // 加入时间应该在合理范围内
        expect(joinTime).toBeLessThan(10000);

      } finally {
        cleanupUsers(users);
      }
    }, 30000);
  });
});