/**
 * 聊天业务服务测试
 * 
 * 测试范围：
 * - 玩家登录/登出流程
 * - 聊天消息发送和广播
 * - 位置更新和会话管理
 * - Token验证和错误处理
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2026-01-14
 * @lastModified 2026-01-19
 * 
 * 修改记录：
 * - 2026-01-19 moyin: 修复handlePlayerLogout测试，删除不再调用的deleteApiKey断言和过时测试用例
 */

import { Test, TestingModule } from '@nestjs/testing';
import { Logger } from '@nestjs/common';
import { ChatService } from './chat.service';
import { ChatSessionService } from './services/chat_session.service';
import { ChatFilterService } from './services/chat_filter.service';
import { LoginCoreService } from '../../core/login_core/login_core.service';

describe('ChatService', () => {
  let service: ChatService;
  let sessionService: jest.Mocked<ChatSessionService>;
  let filterService: jest.Mocked<ChatFilterService>;
  let zulipClientPool: any;
  let apiKeySecurityService: any;
  let loginCoreService: jest.Mocked<LoginCoreService>;
  let mockWebSocketGateway: any;

  beforeEach(async () => {
    // Mock依赖
    const mockSessionService = {
      createSession: jest.fn(),
      getSession: jest.fn(),
      destroySession: jest.fn(),
      updatePlayerPosition: jest.fn(),
      injectContext: jest.fn(),
      getSocketsInMap: jest.fn(),
    };

    const mockFilterService = {
      validateMessage: jest.fn(),
      filterContent: jest.fn(),
      checkRateLimit: jest.fn(),
      validatePermission: jest.fn(),
    };

    const mockZulipClientPool = {
      createUserClient: jest.fn(),
      destroyUserClient: jest.fn(),
      sendMessage: jest.fn(),
      getUserClient: jest.fn(),
    };

    const mockApiKeySecurityService = {
      getApiKey: jest.fn(),
      deleteApiKey: jest.fn(),
    };

    const mockLoginCoreService = {
      verifyToken: jest.fn(),
    };

    const mockZulipAccountsService = {
      findByGameUserId: jest.fn(),
    };

    mockWebSocketGateway = {
      broadcastToMap: jest.fn(),
      sendToPlayer: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ChatService,
        {
          provide: ChatSessionService,
          useValue: mockSessionService,
        },
        {
          provide: ChatFilterService,
          useValue: mockFilterService,
        },
        {
          provide: 'ZULIP_CLIENT_POOL_SERVICE',
          useValue: mockZulipClientPool,
        },
        {
          provide: 'API_KEY_SECURITY_SERVICE',
          useValue: mockApiKeySecurityService,
        },
        {
          provide: LoginCoreService,
          useValue: mockLoginCoreService,
        },
        {
          provide: 'ZulipAccountsService',
          useValue: mockZulipAccountsService,
        },
      ],
    }).compile();

    service = module.get<ChatService>(ChatService);
    sessionService = module.get(ChatSessionService);
    filterService = module.get(ChatFilterService);
    zulipClientPool = module.get('ZULIP_CLIENT_POOL_SERVICE');
    apiKeySecurityService = module.get('API_KEY_SECURITY_SERVICE');
    loginCoreService = module.get(LoginCoreService);

    // 设置默认的mock行为
    // ZulipAccountsService默认返回null（用户没有Zulip账号）
    const zulipAccountsService = module.get('ZulipAccountsService');
    zulipAccountsService.findByGameUserId.mockResolvedValue(null);
    
    // ZulipClientPool的getUserClient默认返回null
    zulipClientPool.getUserClient.mockResolvedValue(null);

    // 设置WebSocket网关
    service.setWebSocketGateway(mockWebSocketGateway);

    // 禁用日志输出
    jest.spyOn(Logger.prototype, 'log').mockImplementation();
    jest.spyOn(Logger.prototype, 'error').mockImplementation();
    jest.spyOn(Logger.prototype, 'warn').mockImplementation();
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  describe('初始化', () => {
    it('应该成功创建服务实例', () => {
      expect(service).toBeDefined();
    });

    it('应该成功设置WebSocket网关', () => {
      const newGateway = { broadcastToMap: jest.fn(), sendToPlayer: jest.fn() };
      service.setWebSocketGateway(newGateway);
      expect(service['websocketGateway']).toBe(newGateway);
    });
  });

  describe('handlePlayerLogin', () => {
    const validToken = 'valid.jwt.token';
    const socketId = 'socket_123';

    it('应该成功处理玩家登录', async () => {
      const userInfo = {
        sub: 'user_123',
        username: 'testuser',
        email: 'test@example.com',
        role: 1,
        type: 'access' as 'access' | 'refresh',
      };

      loginCoreService.verifyToken.mockResolvedValue(userInfo);
      sessionService.createSession.mockResolvedValue({
        socketId,
        userId: userInfo.sub,
        username: userInfo.username,
        zulipQueueId: 'queue_123',
        currentMap: 'whale_port',
        position: { x: 400, y: 300 },
        lastActivity: new Date(),
        createdAt: new Date(),
      });

      const result = await service.handlePlayerLogin({ token: validToken, socketId });

      expect(result.success).toBe(true);
      expect(result.userId).toBe(userInfo.sub);
      expect(result.username).toBe(userInfo.username);
      expect(loginCoreService.verifyToken).toHaveBeenCalledWith(validToken, 'access');
      expect(sessionService.createSession).toHaveBeenCalled();
    });

    it('应该拒绝空Token', async () => {
      const result = await service.handlePlayerLogin({ token: '', socketId });

      expect(result.success).toBe(false);
      expect(result.error).toBe('Token或socketId不能为空');
      expect(loginCoreService.verifyToken).not.toHaveBeenCalled();
    });

    it('应该拒绝空socketId', async () => {
      const result = await service.handlePlayerLogin({ token: validToken, socketId: '' });

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

    it('应该处理Token验证失败', async () => {
      loginCoreService.verifyToken.mockResolvedValue(null);

      const result = await service.handlePlayerLogin({ token: validToken, socketId });

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

    it('应该处理Token验证异常', async () => {
      loginCoreService.verifyToken.mockRejectedValue(new Error('Token expired'));

      const result = await service.handlePlayerLogin({ token: validToken, socketId });

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

    it('应该处理会话创建失败', async () => {
      const userInfo = { sub: 'user_123', username: 'testuser', email: 'test@example.com', role: 1, type: 'access' as 'access' | 'refresh' };
      loginCoreService.verifyToken.mockResolvedValue(userInfo);
      sessionService.createSession.mockRejectedValue(new Error('Redis error'));

      const result = await service.handlePlayerLogin({ token: validToken, socketId });

      expect(result.success).toBe(false);
      expect(result.error).toBe('登录失败，请稍后重试');
    });
  });

  describe('handlePlayerLogout', () => {
    const socketId = 'socket_123';
    const userId = 'user_123';

    it('应该成功处理玩家登出', async () => {
      sessionService.getSession.mockResolvedValue({
        socketId,
        userId,
        username: 'testuser',
        zulipQueueId: 'queue_123',
        currentMap: 'whale_port',
        position: { x: 400, y: 300 },
        lastActivity: new Date(),
        createdAt: new Date(),
      });
      zulipClientPool.destroyUserClient.mockResolvedValue(undefined);
      sessionService.destroySession.mockResolvedValue(true);

      await service.handlePlayerLogout(socketId, 'manual');

      expect(sessionService.getSession).toHaveBeenCalledWith(socketId);
      expect(zulipClientPool.destroyUserClient).toHaveBeenCalledWith(userId);
      expect(sessionService.destroySession).toHaveBeenCalledWith(socketId);
    });

    it('应该处理会话不存在的情况', async () => {
      sessionService.getSession.mockResolvedValue(null);

      await service.handlePlayerLogout(socketId);

      expect(sessionService.destroySession).not.toHaveBeenCalled();
    });

    it('应该处理Zulip客户端清理失败', async () => {
      sessionService.getSession.mockResolvedValue({
        socketId,
        userId,
        username: 'testuser',
        zulipQueueId: 'queue_123',
        currentMap: 'whale_port',
        position: { x: 400, y: 300 },
        lastActivity: new Date(),
        createdAt: new Date(),
      });
      zulipClientPool.destroyUserClient.mockRejectedValue(new Error('Zulip error'));
      sessionService.destroySession.mockResolvedValue(true);

      await service.handlePlayerLogout(socketId);

      expect(sessionService.destroySession).toHaveBeenCalled();
    });
  });

  describe('sendChatMessage', () => {
    const socketId = 'socket_123';
    const userId = 'user_123';
    const content = 'Hello, world!';

    beforeEach(() => {
      sessionService.getSession.mockResolvedValue({
        socketId,
        userId,
        username: 'testuser',
        zulipQueueId: 'queue_123',
        currentMap: 'whale_port',
        position: { x: 400, y: 300 },
        lastActivity: new Date(),
        createdAt: new Date(),
      });
      sessionService.injectContext.mockResolvedValue({
        stream: 'Whale Port',
        topic: 'General',
      });
      filterService.validateMessage.mockResolvedValue({
        allowed: true,
        filteredContent: content,
      });
      sessionService.getSocketsInMap.mockResolvedValue([socketId, 'socket_456']);
      apiKeySecurityService.getApiKey.mockResolvedValue({
        success: true,
        apiKey: 'test_api_key',
      });
    });

    it('应该成功发送聊天消息', async () => {
      const result = await service.sendChatMessage({ socketId, content, scope: 'local' });

      expect(result.success).toBe(true);
      expect(result.messageId).toBeDefined();
      expect(sessionService.getSession).toHaveBeenCalledWith(socketId);
      expect(filterService.validateMessage).toHaveBeenCalled();
    });

    it('应该拒绝不存在的会话', async () => {
      sessionService.getSession.mockResolvedValue(null);

      const result = await service.sendChatMessage({ socketId, content, scope: 'local' });

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

    it('应该拒绝被过滤的消息', async () => {
      filterService.validateMessage.mockResolvedValue({
        allowed: false,
        reason: '消息包含敏感词',
      });

      const result = await service.sendChatMessage({ socketId, content, scope: 'local' });

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

    it('应该处理消息发送异常', async () => {
      sessionService.getSession.mockRejectedValue(new Error('Redis error'));

      const result = await service.sendChatMessage({ socketId, content, scope: 'local' });

      expect(result.success).toBe(false);
      expect(result.error).toBe('消息发送失败，请稍后重试');
    });
  });

  describe('updatePlayerPosition', () => {
    const socketId = 'socket_123';
    const mapId = 'whale_port';
    const x = 500;
    const y = 400;

    it('应该成功更新玩家位置', async () => {
      sessionService.updatePlayerPosition.mockResolvedValue(true);

      const result = await service.updatePlayerPosition({ socketId, mapId, x, y });

      expect(result).toBe(true);
      expect(sessionService.updatePlayerPosition).toHaveBeenCalledWith(socketId, mapId, x, y);
    });

    it('应该拒绝空socketId', async () => {
      const result = await service.updatePlayerPosition({ socketId: '', mapId, x, y });

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

    it('应该拒绝空mapId', async () => {
      const result = await service.updatePlayerPosition({ socketId, mapId: '', x, y });

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

    it('应该处理更新失败', async () => {
      sessionService.updatePlayerPosition.mockRejectedValue(new Error('Redis error'));

      const result = await service.updatePlayerPosition({ socketId, mapId, x, y });

      expect(result).toBe(false);
    });
  });

  describe('getChatHistory', () => {
    it('应该返回聊天历史', async () => {
      const result = await service.getChatHistory({ mapId: 'whale_port' });

      expect(result.success).toBe(true);
      expect(result.messages).toBeDefined();
      expect(Array.isArray(result.messages)).toBe(true);
    });

    it('应该支持分页查询', async () => {
      const result = await service.getChatHistory({ mapId: 'whale_port', limit: 10, offset: 0 });

      expect(result.success).toBe(true);
      expect(result.count).toBeLessThanOrEqual(10);
    });
  });

  describe('getSession', () => {
    const socketId = 'socket_123';

    it('应该返回会话信息', async () => {
      const mockSession = {
        socketId,
        userId: 'user_123',
        username: 'testuser',
        zulipQueueId: 'queue_123',
        currentMap: 'whale_port',
        position: { x: 400, y: 300 },
        lastActivity: new Date(),
        createdAt: new Date(),
      };
      sessionService.getSession.mockResolvedValue(mockSession);

      const result = await service.getSession(socketId);

      expect(result).toEqual(mockSession);
      expect(sessionService.getSession).toHaveBeenCalledWith(socketId);
    });

    it('应该处理会话不存在', async () => {
      sessionService.getSession.mockResolvedValue(null);

      const result = await service.getSession(socketId);

      expect(result).toBeNull();
    });
  });
});
