/**
 * Zulip集成主服务测试
 * 
 * 功能描述：
 * - 测试ZulipService的核心功能
 * - 包含属性测试验证玩家登录流程完整性
 * - 包含属性测试验证消息发送流程完整性
 * - 包含属性测试验证位置更新和上下文注入
 * 
 * **Feature: zulip-integration, Property 1: 玩家登录流程完整性**
 * **Validates: Requirements 1.1, 1.2, 1.3, 1.4, 1.5**
 * 
 * **Feature: zulip-integration, Property 3: 消息发送流程完整性**
 * **Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5**
 * 
 * **Feature: zulip-integration, Property 6: 位置更新和上下文注入**
 * **Validates: Requirements 4.1, 4.2, 4.3, 4.4**
 * 
 * @author angjustinl
 * @version 1.0.0
 * @since 2025-12-31
 */

import { Test, TestingModule } from '@nestjs/testing';
import * as fc from 'fast-check';
import { 
  ZulipService,
  PlayerLoginRequest,
  ChatMessageRequest,
  PositionUpdateRequest,
  LoginResponse,
  ChatMessageResponse,
} from './zulip.service';
import { SessionManagerService, GameSession } from './services/session_manager.service';
import { MessageFilterService } from './services/message_filter.service';
import { ZulipEventProcessorService } from './services/zulip_event_processor.service';
import { 
  IZulipClientPoolService,
  IZulipConfigService,
  ZulipClientInstance,
  SendMessageResult,
} from '../../core/zulip_core/zulip_core.interfaces';
import { ApiKeySecurityService } from '../../core/zulip_core/services/api_key_security.service';
import { LoginCoreService } from '../../core/login_core/login_core.service';

describe('ZulipService', () => {
  let service: ZulipService;
  let mockZulipClientPool: jest.Mocked<IZulipClientPoolService>;
  let mockSessionManager: jest.Mocked<SessionManagerService>;
  let mockMessageFilter: jest.Mocked<MessageFilterService>;
  let mockEventProcessor: jest.Mocked<ZulipEventProcessorService>;
  let mockConfigManager: jest.Mocked<IZulipConfigService>;
  let mockLoginCoreService: jest.Mocked<LoginCoreService>;

  // 创建模拟的Zulip客户端实例
  const createMockClientInstance = (overrides: Partial<ZulipClientInstance> = {}): ZulipClientInstance => ({
    userId: 'test-user-123',
    config: {
      username: 'test@example.com',
      apiKey: 'test-api-key',
      realm: 'https://zulip.example.com',
    },
    client: {},
    queueId: 'queue-123',
    lastEventId: 0,
    createdAt: new Date(),
    lastActivity: new Date(),
    isValid: true,
    ...overrides,
  });

  // 创建模拟的游戏会话
  const createMockSession = (overrides: Partial<GameSession> = {}): GameSession => ({
    socketId: 'socket-123',
    userId: 'user-123',
    username: 'TestPlayer',
    zulipQueueId: 'queue-123',
    currentMap: 'whale_port',
    position: { x: 400, y: 300 },
    lastActivity: new Date(),
    createdAt: new Date(),
    ...overrides,
  });

  beforeEach(async () => {
    jest.clearAllMocks();

    mockZulipClientPool = {
      createUserClient: jest.fn(),
      getUserClient: jest.fn(),
      hasUserClient: jest.fn(),
      sendMessage: jest.fn(),
      registerEventQueue: jest.fn(),
      deregisterEventQueue: jest.fn(),
      destroyUserClient: jest.fn(),
      getPoolStats: jest.fn(),
      cleanupIdleClients: jest.fn(),
    } as any;

    mockSessionManager = {
      createSession: jest.fn(),
      getSession: jest.fn(),
      destroySession: jest.fn(),
      updatePlayerPosition: jest.fn(),
      getSocketsInMap: jest.fn(),
      injectContext: jest.fn(),
      cleanupExpiredSessions: jest.fn(),
    } as any;

    mockMessageFilter = {
      validateMessage: jest.fn(),
      filterContent: jest.fn(),
      checkRateLimit: jest.fn(),
      validatePermission: jest.fn(),
      logViolation: jest.fn(),
    } as any;

    mockEventProcessor = {
      startEventProcessing: jest.fn(),
      stopEventProcessing: jest.fn(),
      registerEventQueue: jest.fn(),
      unregisterEventQueue: jest.fn(),
      processMessageEvent: jest.fn(),
      setMessageDistributor: jest.fn(),
      getProcessingStats: jest.fn(),
    } as any;

    mockConfigManager = {
      getStreamByMap: jest.fn(),
      getMapIdByStream: jest.fn(),
      getTopicByObject: jest.fn(),
      getZulipConfig: jest.fn(),
      hasMap: jest.fn(),
      hasStream: jest.fn(),
      getAllMapIds: jest.fn(),
      getAllStreams: jest.fn(),
      reloadConfig: jest.fn(),
      validateConfig: jest.fn(),
    } as any;

    mockLoginCoreService = {
      verifyToken: jest.fn(),
      generateTokens: jest.fn(),
      refreshTokens: jest.fn(),
      revokeToken: jest.fn(),
      validateTokenPayload: jest.fn(),
    } as any;

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ZulipService,
        {
          provide: 'ZULIP_CLIENT_POOL_SERVICE',
          useValue: mockZulipClientPool,
        },
        {
          provide: SessionManagerService,
          useValue: mockSessionManager,
        },
        {
          provide: MessageFilterService,
          useValue: mockMessageFilter,
        },
        {
          provide: ZulipEventProcessorService,
          useValue: mockEventProcessor,
        },
        {
          provide: 'ZULIP_CONFIG_SERVICE',
          useValue: mockConfigManager,
        },
        {
          provide: 'API_KEY_SECURITY_SERVICE',
          useValue: {
            extractApiKey: jest.fn(),
            validateApiKey: jest.fn(),
            encryptApiKey: jest.fn(),
            decryptApiKey: jest.fn(),
            getApiKey: jest.fn().mockResolvedValue({
              success: true,
              apiKey: 'lCPWCPfGh7WUHxwN56GF8oYXOpqNfGF8',
            }),
          },
        },
        {
          provide: LoginCoreService,
          useValue: mockLoginCoreService,
        },
      ],
    }).compile();

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

    // 配置LoginCoreService的默认mock行为
    mockLoginCoreService.verifyToken.mockImplementation(async (token: string) => {
      // 模拟token验证逻辑
      if (token.startsWith('invalid')) {
        throw new Error('Invalid token');
      }
      
      // 从token中提取用户信息（模拟JWT解析）
      const userId = `user_${token.substring(0, 8)}`;
      const username = `Player_${userId.substring(5, 10)}`;
      const email = `${userId}@example.com`;
      
      return {
        sub: userId,
        username,
        email,
        role: 1, // 数字类型的角色
        type: 'access' as const,
        iat: Math.floor(Date.now() / 1000),
        exp: Math.floor(Date.now() / 1000) + 3600,
        iss: 'whale-town',
        aud: 'whale-town-users',
      };
    });
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('handlePlayerLogin - 处理玩家登录', () => {
    it('应该成功处理有效Token的登录请求', async () => {
      const loginRequest: PlayerLoginRequest = {
        token: 'valid_token_123',
        socketId: 'socket-456',
      };

      const mockSession = createMockSession({
        socketId: 'socket-456',
        userId: 'user_valid_to',
        username: 'Player_lid_to',
      });

      mockConfigManager.getZulipConfig.mockReturnValue({
        zulipServerUrl: 'https://zulip.example.com',
      });

      mockZulipClientPool.createUserClient.mockResolvedValue(
        createMockClientInstance({
          userId: 'user_valid_to',
          queueId: 'queue-789',
        })
      );

      mockSessionManager.createSession.mockResolvedValue(mockSession);

      const result = await service.handlePlayerLogin(loginRequest);

      expect(result.success).toBe(true);
      expect(result.userId).toBe('user_valid_to');
      expect(result.username).toBe('Player_valid');
      expect(result.currentMap).toBe('whale_port');
      expect(mockSessionManager.createSession).toHaveBeenCalled();
    });

    it('应该拒绝无效Token的登录请求', async () => {
      const loginRequest: PlayerLoginRequest = {
        token: 'invalid_token',
        socketId: 'socket-456',
      };

      const result = await service.handlePlayerLogin(loginRequest);

      expect(result.success).toBe(false);
      expect(result.error).toBe('Token验证失败');
      expect(mockSessionManager.createSession).not.toHaveBeenCalled();
    });

    it('应该处理空Token的情况', async () => {
      const loginRequest: PlayerLoginRequest = {
        token: '',
        socketId: 'socket-456',
      };

      const result = await service.handlePlayerLogin(loginRequest);

      expect(result.success).toBe(false);
      expect(result.error).toBe('Token不能为空');
    });

    it('应该处理空socketId的情况', async () => {
      const loginRequest: PlayerLoginRequest = {
        token: 'valid_token',
        socketId: '',
      };

      const result = await service.handlePlayerLogin(loginRequest);

      expect(result.success).toBe(false);
      expect(result.error).toBe('socketId不能为空');
    });
    it('应该在Zulip客户端创建失败时使用本地模式', async () => {
      const loginRequest: PlayerLoginRequest = {
        token: 'real_user_token_with_zulip_key_123', // 有API Key的Token
        socketId: 'socket-456',
      };

      const mockSession = createMockSession({
        socketId: 'socket-456',
        userId: 'user_real_user_',
      });

      mockConfigManager.getZulipConfig.mockReturnValue({
        zulipServerUrl: 'https://zulip.example.com',
      });

      // 模拟Zulip客户端创建失败
      mockZulipClientPool.createUserClient.mockRejectedValue(new Error('Zulip连接失败'));
      mockSessionManager.createSession.mockResolvedValue(mockSession);

      const result = await service.handlePlayerLogin(loginRequest);

      // 应该成功登录（本地模式）
      expect(result.success).toBe(true);
      expect(mockSessionManager.createSession).toHaveBeenCalled();
    });
  });

  describe('handlePlayerLogout - 处理玩家登出', () => {
    it('应该成功处理玩家登出', async () => {
      const socketId = 'socket-123';
      const mockSession = createMockSession({ socketId, userId: 'user-123' });

      mockSessionManager.getSession.mockResolvedValue(mockSession);
      mockZulipClientPool.destroyUserClient.mockResolvedValue();
      mockSessionManager.destroySession.mockResolvedValue(undefined);

      await service.handlePlayerLogout(socketId);

      expect(mockSessionManager.getSession).toHaveBeenCalledWith(socketId);
      expect(mockZulipClientPool.destroyUserClient).toHaveBeenCalledWith('user-123');
      expect(mockSessionManager.destroySession).toHaveBeenCalledWith(socketId);
    });

    it('应该处理会话不存在的情况', async () => {
      const socketId = 'non-existent-socket';

      mockSessionManager.getSession.mockResolvedValue(null);

      await service.handlePlayerLogout(socketId);

      expect(mockSessionManager.getSession).toHaveBeenCalledWith(socketId);
      expect(mockZulipClientPool.destroyUserClient).not.toHaveBeenCalled();
      expect(mockSessionManager.destroySession).not.toHaveBeenCalled();
    });

    it('应该在Zulip客户端清理失败时继续执行会话清理', async () => {
      const socketId = 'socket-123';
      const mockSession = createMockSession({ socketId, userId: 'user-123' });

      mockSessionManager.getSession.mockResolvedValue(mockSession);
      mockZulipClientPool.destroyUserClient.mockRejectedValue(new Error('清理失败'));
      mockSessionManager.destroySession.mockResolvedValue(undefined);

      await service.handlePlayerLogout(socketId);

      expect(mockZulipClientPool.destroyUserClient).toHaveBeenCalledWith('user-123');
      expect(mockSessionManager.destroySession).toHaveBeenCalledWith(socketId);
    });
  });

  describe('sendChatMessage - 发送聊天消息', () => {
    it('应该成功发送聊天消息', async () => {
      const chatRequest: ChatMessageRequest = {
        socketId: 'socket-123',
        content: 'Hello, world!',
        scope: 'local',
      };

      const mockSession = createMockSession({
        socketId: 'socket-123',
        userId: 'user-123',
        currentMap: 'tavern',
      });

      mockSessionManager.getSession.mockResolvedValue(mockSession);
      mockSessionManager.injectContext.mockResolvedValue({
        stream: 'Tavern',
        topic: 'General',
      });

      mockMessageFilter.validateMessage.mockResolvedValue({
        allowed: true,
        filteredContent: 'Hello, world!',
      });

      mockZulipClientPool.sendMessage.mockResolvedValue({
        success: true,
        messageId: 12345,
      });

      const result = await service.sendChatMessage(chatRequest);

      expect(result.success).toBe(true);
      expect(result.messageId).toBe(12345);
      expect(mockZulipClientPool.sendMessage).toHaveBeenCalledWith(
        'user-123',
        'Tavern',
        'General',
        'Hello, world!'
      );
    });

    it('应该拒绝会话不存在的消息发送', async () => {
      const chatRequest: ChatMessageRequest = {
        socketId: 'non-existent-socket',
        content: 'Hello, world!',
        scope: 'local',
      };

      mockSessionManager.getSession.mockResolvedValue(null);

      const result = await service.sendChatMessage(chatRequest);

      expect(result.success).toBe(false);
      expect(result.error).toBe('会话不存在，请重新登录');
      expect(mockZulipClientPool.sendMessage).not.toHaveBeenCalled();
    });

    it('应该拒绝未通过验证的消息', async () => {
      const chatRequest: ChatMessageRequest = {
        socketId: 'socket-123',
        content: '敏感词内容',
        scope: 'local',
      };

      const mockSession = createMockSession({ socketId: 'socket-123' });

      mockSessionManager.getSession.mockResolvedValue(mockSession);
      mockSessionManager.injectContext.mockResolvedValue({
        stream: 'Tavern',
        topic: 'General',
      });

      mockMessageFilter.validateMessage.mockResolvedValue({
        allowed: false,
        reason: '消息包含敏感词',
      });

      const result = await service.sendChatMessage(chatRequest);

      expect(result.success).toBe(false);
      expect(result.error).toBe('消息包含敏感词');
      expect(mockZulipClientPool.sendMessage).not.toHaveBeenCalled();
    });

    it('应该在Zulip发送失败时仍返回成功（本地模式）', async () => {
      const chatRequest: ChatMessageRequest = {
        socketId: 'socket-123',
        content: 'Hello, world!',
        scope: 'local',
      };

      const mockSession = createMockSession({ socketId: 'socket-123' });

      mockSessionManager.getSession.mockResolvedValue(mockSession);
      mockSessionManager.injectContext.mockResolvedValue({
        stream: 'Tavern',
        topic: 'General',
      });

      mockMessageFilter.validateMessage.mockResolvedValue({
        allowed: true,
        filteredContent: 'Hello, world!',
      });

      mockZulipClientPool.sendMessage.mockResolvedValue({
        success: false,
        error: 'Zulip服务不可用',
      });

      const result = await service.sendChatMessage(chatRequest);

      expect(result.success).toBe(true); // 本地模式下仍返回成功
    });
  });

  describe('updatePlayerPosition - 更新玩家位置', () => {
    it('应该成功更新玩家位置', async () => {
      const positionRequest: PositionUpdateRequest = {
        socketId: 'socket-123',
        x: 500,
        y: 400,
        mapId: 'tavern',
      };

      mockSessionManager.updatePlayerPosition.mockResolvedValue(true);

      const result = await service.updatePlayerPosition(positionRequest);

      expect(result).toBe(true);
      expect(mockSessionManager.updatePlayerPosition).toHaveBeenCalledWith(
        'socket-123',
        'tavern',
        500,
        400
      );
    });

    it('应该拒绝空socketId的位置更新', async () => {
      const positionRequest: PositionUpdateRequest = {
        socketId: '',
        x: 500,
        y: 400,
        mapId: 'tavern',
      };

      const result = await service.updatePlayerPosition(positionRequest);

      expect(result).toBe(false);
      expect(mockSessionManager.updatePlayerPosition).not.toHaveBeenCalled();
    });

    it('应该拒绝空mapId的位置更新', async () => {
      const positionRequest: PositionUpdateRequest = {
        socketId: 'socket-123',
        x: 500,
        y: 400,
        mapId: '',
      };

      const result = await service.updatePlayerPosition(positionRequest);

      expect(result).toBe(false);
      expect(mockSessionManager.updatePlayerPosition).not.toHaveBeenCalled();
    });
  });
  /**
   * 属性测试: 玩家登录流程完整性
   * 
   * **Feature: zulip-integration, Property 1: 玩家登录流程完整性**
   * **Validates: Requirements 1.1, 1.2, 1.3, 1.4, 1.5**
   * 
   * 对于任何有效的游戏Token，系统应该能够验证Token，创建Zulip客户端，
   * 建立会话映射，并返回成功的登录响应
   */
  describe('Property 1: 玩家登录流程完整性', () => {
    /**
     * 属性: 对于任何有效的Token和socketId，登录应该成功并创建会话
     * 验证需求 1.1: 游戏客户端连接时系统应验证游戏Token的有效性
     * 验证需求 1.2: Token验证成功后系统应获取用户的Zulip API Key
     * 验证需求 1.3: 获取API Key后系统应创建用户专用的Zulip客户端实例
     */
    it('对于任何有效的Token和socketId，登录应该成功并创建会话', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的Token（不以'invalid'开头）
          fc.string({ minLength: 8, maxLength: 50 })
            .filter(s => !s.startsWith('invalid') && s.trim().length > 0),
          // 生成有效的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0),
          async (token, socketId) => {
            const trimmedToken = token.trim();
            const trimmedSocketId = socketId.trim();
            
            const loginRequest: PlayerLoginRequest = {
              token: trimmedToken,
              socketId: trimmedSocketId,
            };

            const expectedUserId = `user_${trimmedToken.substring(0, 8)}`;
            const expectedUsername = `Player_${expectedUserId.substring(5, 10)}`;

            const mockSession = createMockSession({
              socketId: trimmedSocketId,
              userId: expectedUserId,
              username: expectedUsername,
            });

            mockConfigManager.getZulipConfig.mockReturnValue({
              zulipServerUrl: 'https://zulip.example.com',
            });

            mockSessionManager.createSession.mockResolvedValue(mockSession);

            const result = await service.handlePlayerLogin(loginRequest);

            // 验证登录成功
            expect(result.success).toBe(true);
            expect(result.userId).toBe(expectedUserId);
            expect(result.username).toBe(expectedUsername);
            expect(result.currentMap).toBe('whale_port');
            expect(result.sessionId).toBeDefined();

            // 验证会话创建被调用
            expect(mockSessionManager.createSession).toHaveBeenCalledWith(
              trimmedSocketId,
              expectedUserId,
              expect.any(String), // zulipQueueId
              expectedUsername,
              'whale_port',
              { x: 400, y: 300 }
            );
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何无效的Token，登录应该失败
     * 验证需求 1.1: 游戏客户端连接时系统应验证游戏Token的有效性
     */
    it('对于任何无效的Token，登录应该失败', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成无效的Token（以'invalid'开头）
          fc.string({ minLength: 1, maxLength: 30 })
            .map(s => `invalid${s}`),
          // 生成有效的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0),
          async (invalidToken, socketId) => {
            const loginRequest: PlayerLoginRequest = {
              token: invalidToken,
              socketId: socketId.trim(),
            };

            const result = await service.handlePlayerLogin(loginRequest);

            // 验证登录失败
            expect(result.success).toBe(false);
            expect(result.error).toBe('Token验证失败');
            expect(result.userId).toBeUndefined();
            expect(result.sessionId).toBeUndefined();

            // 验证没有创建会话
            expect(mockSessionManager.createSession).not.toHaveBeenCalled();
          }
        ),
        { numRuns: 50 }
      );
    }, 30000);

    /**
     * 属性: 对于空或无效的参数，登录应该返回相应的错误信息
     * 验证需求 1.1: 系统应正确处理无效的登录请求
     */
    it('对于空或无效的参数，登录应该返回相应的错误信息', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成可能为空或以'invalid'开头的Token
          fc.oneof(
            fc.constant(''), // 空字符串
            fc.constant('   '), // 只有空格
            fc.string({ minLength: 1, maxLength: 50 }).map(s => 'invalid' + s), // 以invalid开头
          ),
          // 生成可能为空的socketId
          fc.option(fc.string({ minLength: 1, maxLength: 30 }), { nil: '' }),
          async (token, socketId) => {
            // 重置mock调用历史
            jest.clearAllMocks();
            
            const loginRequest: PlayerLoginRequest = {
              token: token || '',
              socketId: socketId || '',
            };

            const result = await service.handlePlayerLogin(loginRequest);

            // 验证登录失败
            expect(result.success).toBe(false);
            expect(result.error).toBeDefined();

            if (!token || token.trim().length === 0) {
              expect(result.error).toBe('Token不能为空');
            } else if (!socketId || socketId.trim().length === 0) {
              expect(result.error).toBe('socketId不能为空');
            } else if (token.startsWith('invalid')) {
              expect(result.error).toBe('Token验证失败');
            }

            // 验证没有创建会话
            expect(mockSessionManager.createSession).not.toHaveBeenCalled();
          }
        ),
        { numRuns: 50 }
      );
    }, 30000);

    /**
     * 属性: 对于有Zulip API Key的用户，应该尝试创建Zulip客户端
     * 验证需求 1.2: Token验证成功后系统应获取用户的Zulip API Key
     * 验证需求 1.3: 获取API Key后系统应创建用户专用的Zulip客户端实例
     */
    it('对于有Zulip API Key的用户，应该尝试创建Zulip客户端', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成包含特定标识的Token（表示有API Key）
          fc.constantFrom(
            'real_user_token_with_zulip_key_123',
            'token_with_lCPWCPf_key',
            'token_with_W2KhXaQx_key'
          ),
          // 生成有效的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0),
          async (tokenWithApiKey, socketId) => {
            const loginRequest: PlayerLoginRequest = {
              token: tokenWithApiKey,
              socketId: socketId.trim(),
            };

            const mockClientInstance = createMockClientInstance({
              userId: `user_${tokenWithApiKey.substring(0, 8)}`,
              queueId: 'test-queue-123',
            });

            const mockSession = createMockSession({
              socketId: socketId.trim(),
              zulipQueueId: 'test-queue-123',
            });

            mockConfigManager.getZulipConfig.mockReturnValue({
              zulipServerUrl: 'https://zulip.example.com',
            });

            mockZulipClientPool.createUserClient.mockResolvedValue(mockClientInstance);
            mockSessionManager.createSession.mockResolvedValue(mockSession);

            const result = await service.handlePlayerLogin(loginRequest);

            // 验证登录成功
            expect(result.success).toBe(true);

            // 验证尝试创建了Zulip客户端
            expect(mockZulipClientPool.createUserClient).toHaveBeenCalledWith(
              expect.any(String),
              expect.objectContaining({
                username: expect.any(String),
                apiKey: 'lCPWCPfGh7WUHxwN56GF8oYXOpqNfGF8',
                realm: 'https://zulip.example.com',
              })
            );
          }
        ),
        { numRuns: 30 }
      );
    }, 30000);
  });
  /**
   * 属性测试: 消息发送流程完整性
   * 
   * **Feature: zulip-integration, Property 3: 消息发送流程完整性**
   * **Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5**
   * 
   * 对于任何有效的聊天消息请求，系统应该进行内容过滤、权限验证、
   * 上下文注入，并成功发送到对应的Zulip Stream/Topic
   */
  describe('Property 3: 消息发送流程完整性', () => {
    /**
     * 属性: 对于任何有效会话的消息发送请求，应该成功处理并发送
     * 验证需求 3.1: 游戏客户端发送聊天消息时系统应获取玩家当前位置
     * 验证需求 3.2: 获取位置后系统应根据位置确定目标Stream和Topic
     * 验证需求 3.3: 确定目标后系统应进行消息内容过滤和频率检查
     */
    it('对于任何有效会话的消息发送请求，应该成功处理并发送', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0),
          // 生成有效的消息内容
          fc.string({ minLength: 1, maxLength: 200 })
            .filter(s => s.trim().length > 0 && !/[敏感词|违禁词]/.test(s)),
          // 生成地图和Stream映射
          fc.record({
            mapId: fc.constantFrom('tavern', 'novice_village', 'market'),
            streamName: fc.constantFrom('Tavern', 'Novice Village', 'Market'),
          }),
          async (socketId, content, mapping) => {
            const chatRequest: ChatMessageRequest = {
              socketId: socketId.trim(),
              content: content.trim(),
              scope: 'local',
            };

            const mockSession = createMockSession({
              socketId: socketId.trim(),
              userId: `user_${socketId.substring(0, 8)}`,
              currentMap: mapping.mapId,
            });

            mockSessionManager.getSession.mockResolvedValue(mockSession);
            mockSessionManager.injectContext.mockResolvedValue({
              stream: mapping.streamName,
              topic: 'General',
            });

            mockMessageFilter.validateMessage.mockResolvedValue({
              allowed: true,
              filteredContent: content.trim(),
            });

            mockZulipClientPool.sendMessage.mockResolvedValue({
              success: true,
              messageId: Math.floor(Math.random() * 1000000),
            });

            const result = await service.sendChatMessage(chatRequest);

            // 验证消息发送成功
            expect(result.success).toBe(true);
            expect(result.messageId).toBeDefined();

            // 验证调用了正确的方法
            expect(mockSessionManager.getSession).toHaveBeenCalledWith(socketId.trim());
            expect(mockSessionManager.injectContext).toHaveBeenCalledWith(socketId.trim());
            expect(mockMessageFilter.validateMessage).toHaveBeenCalledWith(
              mockSession.userId,
              content.trim(),
              mapping.streamName,
              mapping.mapId
            );
            expect(mockZulipClientPool.sendMessage).toHaveBeenCalledWith(
              mockSession.userId,
              mapping.streamName,
              'General',
              content.trim()
            );
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何不存在的会话，消息发送应该失败
     * 验证需求 3.1: 系统应验证会话的有效性
     */
    it('对于任何不存在的会话，消息发送应该失败', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成不存在的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0)
            .map(s => `nonexistent_${s}`),
          // 生成任意消息内容
          fc.string({ minLength: 1, maxLength: 200 })
            .filter(s => s.trim().length > 0),
          async (nonExistentSocketId, content) => {
            const chatRequest: ChatMessageRequest = {
              socketId: nonExistentSocketId,
              content: content.trim(),
              scope: 'local',
            };

            mockSessionManager.getSession.mockResolvedValue(null);

            const result = await service.sendChatMessage(chatRequest);

            // 验证消息发送失败
            expect(result.success).toBe(false);
            expect(result.error).toBe('会话不存在，请重新登录');

            // 验证没有进行后续处理
            expect(mockMessageFilter.validateMessage).not.toHaveBeenCalled();
            expect(mockZulipClientPool.sendMessage).not.toHaveBeenCalled();
          }
        ),
        { numRuns: 50 }
      );
    }, 30000);

    /**
     * 属性: 对于任何未通过验证的消息，发送应该失败
     * 验证需求 3.3: 确定目标后系统应进行消息内容过滤和频率检查
     */
    it('对于任何未通过验证的消息，发送应该失败', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0),
          // 生成可能包含敏感词的消息内容
          fc.string({ minLength: 1, maxLength: 200 })
            .filter(s => s.trim().length > 0),
          // 生成验证失败的原因
          fc.constantFrom(
            '消息包含敏感词',
            '发送频率过快',
            '权限不足',
            '消息长度超限'
          ),
          async (socketId, content, failureReason) => {
            const chatRequest: ChatMessageRequest = {
              socketId: socketId.trim(),
              content: content.trim(),
              scope: 'local',
            };

            const mockSession = createMockSession({
              socketId: socketId.trim(),
            });

            mockSessionManager.getSession.mockResolvedValue(mockSession);
            mockSessionManager.injectContext.mockResolvedValue({
              stream: 'Tavern',
              topic: 'General',
            });

            mockMessageFilter.validateMessage.mockResolvedValue({
              allowed: false,
              reason: failureReason,
            });

            const result = await service.sendChatMessage(chatRequest);

            // 验证消息发送失败
            expect(result.success).toBe(false);
            expect(result.error).toBe(failureReason);

            // 验证没有发送到Zulip
            expect(mockZulipClientPool.sendMessage).not.toHaveBeenCalled();
          }
        ),
        { numRuns: 50 }
      );
    }, 30000);

    /**
     * 属性: 即使Zulip发送失败，系统也应该返回成功（本地模式）
     * 验证需求 3.5: 发送消息到Zulip时系统应处理发送失败的情况
     */
    it('即使Zulip发送失败，系统也应该返回成功（本地模式）', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0),
          // 生成有效的消息内容
          fc.string({ minLength: 1, maxLength: 200 })
            .filter(s => s.trim().length > 0),
          // 生成Zulip错误信息
          fc.constantFrom(
            'Zulip服务不可用',
            '网络连接超时',
            'API Key无效',
            'Stream不存在'
          ),
          async (socketId, content, zulipError) => {
            const chatRequest: ChatMessageRequest = {
              socketId: socketId.trim(),
              content: content.trim(),
              scope: 'local',
            };

            const mockSession = createMockSession({
              socketId: socketId.trim(),
            });

            mockSessionManager.getSession.mockResolvedValue(mockSession);
            mockSessionManager.injectContext.mockResolvedValue({
              stream: 'Tavern',
              topic: 'General',
            });

            mockMessageFilter.validateMessage.mockResolvedValue({
              allowed: true,
              filteredContent: content.trim(),
            });

            mockZulipClientPool.sendMessage.mockResolvedValue({
              success: false,
              error: zulipError,
            });

            const result = await service.sendChatMessage(chatRequest);

            // 验证本地模式下仍返回成功
            expect(result.success).toBe(true);
            expect(result.messageId).toBeUndefined();
          }
        ),
        { numRuns: 50 }
      );
    }, 30000);
  });
  /**
   * 属性测试: 位置更新和上下文注入
   * 
   * **Feature: zulip-integration, Property 6: 位置更新和上下文注入**
   * **Validates: Requirements 4.1, 4.2, 4.3, 4.4**
   * 
   * 对于任何位置更新请求，系统应该正确更新玩家位置信息，
   * 并在消息发送时根据位置进行上下文注入
   */
  describe('Property 6: 位置更新和上下文注入', () => {
    /**
     * 属性: 对于任何有效的位置更新请求，应该成功更新位置
     * 验证需求 4.1: 玩家移动时系统应更新玩家在游戏世界中的位置信息
     * 验证需求 4.2: 更新位置时系统应验证位置的有效性
     */
    it('对于任何有效的位置更新请求，应该成功更新位置', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0),
          // 生成有效的坐标
          fc.record({
            x: fc.integer({ min: 0, max: 2000 }),
            y: fc.integer({ min: 0, max: 2000 }),
          }),
          // 生成有效的地图ID
          fc.constantFrom('tavern', 'novice_village', 'market', 'whale_port'),
          async (socketId, position, mapId) => {
            const positionRequest: PositionUpdateRequest = {
              socketId: socketId.trim(),
              x: position.x,
              y: position.y,
              mapId,
            };

            mockSessionManager.updatePlayerPosition.mockResolvedValue(true);

            const result = await service.updatePlayerPosition(positionRequest);

            // 验证位置更新成功
            expect(result).toBe(true);

            // 验证调用了正确的方法
            expect(mockSessionManager.updatePlayerPosition).toHaveBeenCalledWith(
              socketId.trim(),
              mapId,
              position.x,
              position.y
            );
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何无效的参数，位置更新应该失败
     * 验证需求 4.2: 更新位置时系统应验证位置的有效性
     */
    it('对于任何无效的参数，位置更新应该失败', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成可能为空的socketId
          fc.option(fc.string({ minLength: 1, maxLength: 30 }), { nil: '' }),
          // 生成可能为空的mapId
          fc.option(fc.constantFrom('tavern', 'market'), { nil: '' }),
          // 生成坐标
          fc.record({
            x: fc.integer({ min: 0, max: 2000 }),
            y: fc.integer({ min: 0, max: 2000 }),
          }),
          async (socketId, mapId, position) => {
            // 重置mock调用历史
            jest.clearAllMocks();
            
            const positionRequest: PositionUpdateRequest = {
              socketId: socketId || '',
              x: position.x,
              y: position.y,
              mapId: mapId || '',
            };

            const result = await service.updatePlayerPosition(positionRequest);

            if (!socketId || socketId.trim().length === 0 || 
                !mapId || mapId.trim().length === 0) {
              // 验证位置更新失败
              expect(result).toBe(false);
              
              // 验证没有调用SessionManager
              expect(mockSessionManager.updatePlayerPosition).not.toHaveBeenCalled();
            }
          }
        ),
        { numRuns: 50 }
      );
    }, 30000);

    /**
     * 属性: 位置更新失败时应该正确处理错误
     * 验证需求 4.1: 系统应正确处理位置更新过程中的错误
     */
    it('位置更新失败时应该正确处理错误', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的socketId
          fc.string({ minLength: 5, maxLength: 30 })
            .filter(s => s.trim().length > 0),
          // 生成有效的坐标
          fc.record({
            x: fc.integer({ min: 0, max: 2000 }),
            y: fc.integer({ min: 0, max: 2000 }),
          }),
          // 生成有效的地图ID
          fc.constantFrom('tavern', 'novice_village', 'market'),
          async (socketId, position, mapId) => {
            const positionRequest: PositionUpdateRequest = {
              socketId: socketId.trim(),
              x: position.x,
              y: position.y,
              mapId,
            };

            // 模拟SessionManager抛出错误
            mockSessionManager.updatePlayerPosition.mockRejectedValue(
              new Error('位置更新失败')
            );

            const result = await service.updatePlayerPosition(positionRequest);

            // 验证位置更新失败
            expect(result).toBe(false);
          }
        ),
        { numRuns: 50 }
      );
    }, 30000);
  });

  describe('辅助方法', () => {
    it('getSession - 应该返回会话信息', async () => {
      const socketId = 'socket-123';
      const mockSession = createMockSession({ socketId });

      mockSessionManager.getSession.mockResolvedValue(mockSession);

      const result = await service.getSession(socketId);

      expect(result).toBe(mockSession);
      expect(mockSessionManager.getSession).toHaveBeenCalledWith(socketId);
    });

    it('getSocketsInMap - 应该返回地图中的Socket列表', async () => {
      const mapId = 'tavern';
      const socketIds = ['socket-1', 'socket-2', 'socket-3'];

      mockSessionManager.getSocketsInMap.mockResolvedValue(socketIds);

      const result = await service.getSocketsInMap(mapId);

      expect(result).toBe(socketIds);
      expect(mockSessionManager.getSocketsInMap).toHaveBeenCalledWith(mapId);
    });
  });
});