/**
 * 聊天消息过滤服务测试
 * 
 * 测试范围：
 * - 消息内容过滤和敏感词检测
 * - 频率限制检查
 * - 权限验证
 * - 综合消息验证流程
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-14
 * @lastModified 2026-01-14
 */

import { Test, TestingModule } from '@nestjs/testing';
import { Logger } from '@nestjs/common';
import { ChatFilterService } from './chat_filter.service';

describe('ChatFilterService', () => {
  let service: ChatFilterService;
  let redisService: any;
  let configManager: any;

  beforeEach(async () => {
    const mockRedisService = {
      get: jest.fn(),
      setex: jest.fn(),
      incr: jest.fn(),
    };

    const mockConfigManager = {
      getStreamByMap: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ChatFilterService,
        {
          provide: 'REDIS_SERVICE',
          useValue: mockRedisService,
        },
        {
          provide: 'ZULIP_CONFIG_SERVICE',
          useValue: mockConfigManager,
        },
      ],
    }).compile();

    service = module.get<ChatFilterService>(ChatFilterService);
    redisService = module.get('REDIS_SERVICE');
    configManager = module.get('ZULIP_CONFIG_SERVICE');

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

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

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

  describe('filterContent', () => {
    it('应该通过正常消息', async () => {
      const result = await service.filterContent('Hello, world!');

      expect(result.allowed).toBe(true);
      expect(result.filtered).toBeUndefined();
    });

    it('应该拒绝空消息', async () => {
      const result = await service.filterContent('');

      expect(result.allowed).toBe(false);
      expect(result.reason).toBe('消息内容不能为空');
    });

    it('应该拒绝只包含空白字符的消息', async () => {
      const result = await service.filterContent('   \n\t  ');

      expect(result.allowed).toBe(false);
      expect(result.reason).toBe('消息内容不能为空');
    });

    it('应该拒绝超长消息', async () => {
      const longMessage = 'a'.repeat(1001);
      const result = await service.filterContent(longMessage);

      expect(result.allowed).toBe(false);
      expect(result.reason).toContain('消息内容过长');
    });

    it('应该替换敏感词', async () => {
      const result = await service.filterContent('这是垃圾消息');

      expect(result.allowed).toBe(true);
      expect(result.filtered).toContain('**');
    });

    it('应该拒绝包含过多重复字符的消息', async () => {
      const result = await service.filterContent('aaaaa');

      expect(result.allowed).toBe(false);
      expect(result.reason).toBe('消息包含过多重复字符');
    });

    it('应该拒绝包含重复短语的消息', async () => {
      const result = await service.filterContent('哈哈哈哈哈');

      expect(result.allowed).toBe(false);
      expect(result.reason).toBe('消息包含过多重复字符');
    });

    it('应该拒绝包含黑名单链接的消息', async () => {
      const result = await service.filterContent('访问 https://malware.com 获取更多信息');

      expect(result.allowed).toBe(false);
      expect(result.reason).toBe('消息包含不允许的链接');
    });

    it('应该允许包含正常链接的消息', async () => {
      const result = await service.filterContent('访问 https://example.com 获取更多信息');

      expect(result.allowed).toBe(true);
    });

    it('应该处理多个敏感词', async () => {
      const result = await service.filterContent('这是垃圾广告');

      expect(result.allowed).toBe(true);
      expect(result.filtered).toContain('**');
    });

    it('应该处理大小写不敏感的敏感词', async () => {
      const result = await service.filterContent('这是GARBAGE消息');

      expect(result.allowed).toBe(true);
    });
  });

  describe('checkRateLimit', () => {
    const userId = 'user_123';

    it('应该允许首次发送消息', async () => {
      redisService.get.mockResolvedValue(null);
      redisService.setex.mockResolvedValue('OK');

      const result = await service.checkRateLimit(userId);

      expect(result).toBe(true);
      expect(redisService.setex).toHaveBeenCalled();
    });

    it('应该允许在限制内发送消息', async () => {
      redisService.get.mockResolvedValue('5');
      redisService.incr.mockResolvedValue(6);

      const result = await service.checkRateLimit(userId);

      expect(result).toBe(true);
      expect(redisService.incr).toHaveBeenCalled();
    });

    it('应该拒绝超过频率限制的消息', async () => {
      redisService.get.mockResolvedValue('10');

      const result = await service.checkRateLimit(userId);

      expect(result).toBe(false);
      expect(redisService.incr).not.toHaveBeenCalled();
    });

    it('应该处理Redis错误', async () => {
      redisService.get.mockRejectedValue(new Error('Redis error'));

      const result = await service.checkRateLimit(userId);

      // 失败时应该允许，避免影响正常用户
      expect(result).toBe(true);
    });

    it('应该正确递增计数器', async () => {
      redisService.get.mockResolvedValue('1');
      redisService.incr.mockResolvedValue(2);

      await service.checkRateLimit(userId);

      expect(redisService.incr).toHaveBeenCalledWith(`chat:rate_limit:${userId}`);
    });
  });

  describe('validatePermission', () => {
    const userId = 'user_123';
    const targetStream = 'Whale Port';
    const currentMap = 'whale_port';

    it('应该允许有权限的用户发送消息', async () => {
      configManager.getStreamByMap.mockReturnValue('Whale Port');

      const result = await service.validatePermission(userId, targetStream, currentMap);

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

    it('应该拒绝无权限的用户发送消息', async () => {
      configManager.getStreamByMap.mockReturnValue('Other Stream');

      const result = await service.validatePermission(userId, targetStream, currentMap);

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

    it('应该拒绝空userId', async () => {
      const result = await service.validatePermission('', targetStream, currentMap);

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

    it('应该拒绝空targetStream', async () => {
      const result = await service.validatePermission(userId, '', currentMap);

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

    it('应该拒绝空currentMap', async () => {
      const result = await service.validatePermission(userId, targetStream, '');

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

    it('应该处理地图没有对应Stream的情况', async () => {
      configManager.getStreamByMap.mockReturnValue(null);

      const result = await service.validatePermission(userId, targetStream, currentMap);

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

    it('应该忽略大小写进行匹配', async () => {
      configManager.getStreamByMap.mockReturnValue('whale port');

      const result = await service.validatePermission(userId, 'WHALE PORT', currentMap);

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

  describe('validateMessage', () => {
    const userId = 'user_123';
    const content = 'Hello, world!';
    const targetStream = 'Whale Port';
    const currentMap = 'whale_port';

    beforeEach(() => {
      redisService.get.mockResolvedValue(null);
      redisService.setex.mockResolvedValue('OK');
      configManager.getStreamByMap.mockReturnValue('Whale Port');
    });

    it('应该通过所有验证的消息', async () => {
      const result = await service.validateMessage(userId, content, targetStream, currentMap);

      expect(result.allowed).toBe(true);
      // filteredContent可能是undefined（如果没有过滤）或者是过滤后的内容
      if (result.filteredContent) {
        expect(result.filteredContent).toBeDefined();
      }
    });

    it('应该拒绝超过频率限制的消息', async () => {
      redisService.get.mockResolvedValue('10');

      const result = await service.validateMessage(userId, content, targetStream, currentMap);

      expect(result.allowed).toBe(false);
      expect(result.reason).toContain('发送频率过高');
    });

    it('应该拒绝包含敏感词的消息', async () => {
      const result = await service.validateMessage(userId, 'aaaaa', targetStream, currentMap);

      expect(result.allowed).toBe(false);
      expect(result.reason).toBeDefined();
    });

    it('应该拒绝无权限发送的消息', async () => {
      configManager.getStreamByMap.mockReturnValue('Other Stream');

      const result = await service.validateMessage(userId, content, targetStream, currentMap);

      expect(result.allowed).toBe(false);
      expect(result.reason).toContain('无法向该频道发送消息');
    });

    it('应该返回过滤后的内容', async () => {
      const result = await service.validateMessage(userId, '这是垃圾消息', targetStream, currentMap);

      expect(result.allowed).toBe(true);
      expect(result.filteredContent).toContain('**');
    });
  });

  describe('边界情况', () => {
    it('应该处理null内容', async () => {
      const result = await service.filterContent(null as any);

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

    it('应该处理undefined内容', async () => {
      const result = await service.filterContent(undefined as any);

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

    it('应该处理特殊字符', async () => {
      const result = await service.filterContent('!@#$%^&*()');

      expect(result.allowed).toBe(true);
    });

    it('应该处理Unicode字符', async () => {
      const result = await service.filterContent('你好世界 🌍');

      expect(result.allowed).toBe(true);
    });

    it('应该处理混合语言内容', async () => {
      const result = await service.filterContent('Hello 世界 مرحبا');

      expect(result.allowed).toBe(true);
    });

    it('应该处理恰好1000字符的消息', async () => {
      const message = 'a'.repeat(1000);
      const result = await service.filterContent(message);

      expect(result.allowed).toBe(false);
      expect(result.reason).toBe('消息包含过多重复字符');
    });
  });
});
