/**
 * 聊天消息过滤服务
 *
 * 功能描述：
 * - 实施内容审核和频率控制
 * - 敏感词过滤和权限验证
 * - 防止恶意操作和滥用
 *
 * 架构层级：Business Layer（业务层）
 *
 * 最近修改：
 * - 2026-01-14: 代码规范优化 - 补充类级别JSDoc注释 (修改者: moyin)
 * - 2026-01-14: 代码规范优化 - 完善文件头注释和方法注释规范 (修改者: moyin)
 *
 * @author moyin
 * @version 1.0.2
 * @since 2026-01-14
 * @lastModified 2026-01-14
 */

import { Injectable, Logger, Inject } from '@nestjs/common';
import { IRedisService } from '../../../core/redis/redis.interface';
import { IZulipConfigService } from '../../../core/zulip_core/zulip_core.interfaces';

/**
 * 内容过滤结果接口
 */
export interface ContentFilterResult {
  allowed: boolean;
  filtered?: string;
  reason?: string;
}

/**
 * 敏感词配置接口
 */
interface SensitiveWordConfig {
  word: string;
  level: 'block' | 'replace';
  category?: string;
}

/**
 * 聊天消息过滤服务类
 *
 * 职责：
 * - 实施消息内容审核和敏感词过滤
 * - 控制用户发送消息的频率
 * - 验证用户发送消息的权限
 *
 * 主要方法：
 * - validateMessage() - 综合验证消息（频率+内容+权限）
 * - filterContent() - 过滤消息内容中的敏感词
 * - checkRateLimit() - 检查用户发送频率
 * - validatePermission() - 验证用户发送权限
 *
 * 使用场景：
 * - 用户发送聊天消息前的预处理
 * - 防止恶意刷屏和不当内容传播
 */
@Injectable()
export class ChatFilterService {
  private readonly RATE_LIMIT_PREFIX = 'chat:rate_limit:';
  private readonly DEFAULT_RATE_LIMIT = 10;
  private readonly RATE_LIMIT_WINDOW = 60;
  private readonly MAX_MESSAGE_LENGTH = 1000;
  private readonly logger = new Logger(ChatFilterService.name);

  private sensitiveWords: SensitiveWordConfig[] = [
    { word: '垃圾', level: 'replace', category: 'offensive' },
    { word: '广告', level: 'replace', category: 'spam' },
    { word: '刷屏', level: 'replace', category: 'spam' },
  ];

  private readonly BLACKLISTED_DOMAINS = ['malware.com', 'phishing.net'];

  constructor(
    @Inject('REDIS_SERVICE')
    private readonly redisService: IRedisService,
    @Inject('ZULIP_CONFIG_SERVICE')
    private readonly configManager: IZulipConfigService,
  ) {
    this.logger.log('ChatFilterService初始化完成');
  }

  /**
   * 综合消息验证
   * @param userId 用户ID
   * @param content 消息内容
   * @param targetStream 目标Stream
   * @param currentMap 当前地图ID
   * @returns 验证结果，包含是否允许、原因和过滤后的内容
   */
  async validateMessage(
    userId: string,
    content: string,
    targetStream: string,
    currentMap: string
  ): Promise<{ allowed: boolean; reason?: string; filteredContent?: string }> {
    // 1. 频率限制检查
    const rateLimitOk = await this.checkRateLimit(userId);
    if (!rateLimitOk) {
      return { allowed: false, reason: '发送频率过高，请稍后重试' };
    }

    // 2. 内容过滤
    const contentResult = await this.filterContent(content);
    if (!contentResult.allowed) {
      return { allowed: false, reason: contentResult.reason };
    }

    // 3. 权限验证
    const permissionOk = await this.validatePermission(userId, targetStream, currentMap);
    if (!permissionOk) {
      return { allowed: false, reason: '您当前位置无法向该频道发送消息' };
    }

    return { allowed: true, filteredContent: contentResult.filtered };
  }

  /**
   * 内容过滤
   * @param content 待过滤的消息内容
   * @returns 过滤结果，包含是否允许、过滤后内容和原因
   */
  async filterContent(content: string): Promise<ContentFilterResult> {
    // 空内容检查
    if (!content?.trim()) {
      return { allowed: false, reason: '消息内容不能为空' };
    }

    // 长度检查
    if (content.length > this.MAX_MESSAGE_LENGTH) {
      return { allowed: false, reason: `消息内容过长，最多${this.MAX_MESSAGE_LENGTH}字符` };
    }

    // 空白字符检查
    if (/^\s+$/.test(content)) {
      return { allowed: false, reason: '消息不能只包含空白字符' };
    }

    // 敏感词检查
    let filteredContent = content;
    let hasBlockedWord = false;

    for (const wordConfig of this.sensitiveWords) {
      if (content.toLowerCase().includes(wordConfig.word.toLowerCase())) {
        if (wordConfig.level === 'block') {
          hasBlockedWord = true;
          break;
        } else {
          const replacement = '*'.repeat(wordConfig.word.length);
          filteredContent = filteredContent.replace(
            new RegExp(this.escapeRegExp(wordConfig.word), 'gi'),
            replacement
          );
        }
      }
    }

    if (hasBlockedWord) {
      return { allowed: false, reason: '消息包含不允许的内容' };
    }

    // 重复字符检查
    if (this.hasExcessiveRepetition(content)) {
      return { allowed: false, reason: '消息包含过多重复字符' };
    }

    // 恶意链接检查
    if (!this.checkLinks(content)) {
      return { allowed: false, reason: '消息包含不允许的链接' };
    }

    return {
      allowed: true,
      filtered: filteredContent !== content ? filteredContent : undefined,
    };
  }

  /**
   * 频率限制检查
   * @param userId 用户ID
   * @returns 是否通过频率限制检查
   */
  async checkRateLimit(userId: string): Promise<boolean> {
    try {
      const rateLimitKey = `${this.RATE_LIMIT_PREFIX}${userId}`;
      const currentCount = await this.redisService.get(rateLimitKey);
      const count = currentCount ? parseInt(currentCount, 10) : 0;

      if (count >= this.DEFAULT_RATE_LIMIT) {
        return false;
      }

      if (count === 0) {
        await this.redisService.setex(rateLimitKey, this.RATE_LIMIT_WINDOW, '1');
      } else {
        await this.redisService.incr(rateLimitKey);
      }

      return true;
    } catch (error) {
      this.logger.error('频率检查失败', { error: (error as Error).message });
      return true; // 失败时允许，避免影响正常用户
    }
  }

  /**
   * 权限验证
   * @param userId 用户ID
   * @param targetStream 目标Stream
   * @param currentMap 当前地图ID
   * @returns 是否有权限发送消息
   */
  async validatePermission(userId: string, targetStream: string, currentMap: string): Promise<boolean> {
    if (!userId?.trim() || !targetStream?.trim() || !currentMap?.trim()) {
      return false;
    }

    const allowedStream = this.configManager.getStreamByMap(currentMap);
    if (!allowedStream) return false;

    return targetStream.toLowerCase() === allowedStream.toLowerCase();
  }

  // ========== 私有方法 ==========

  private hasExcessiveRepetition(content: string): boolean {
    // 连续重复字符检查
    if (/(.)\1{4,}/.test(content)) return true;

    // 重复短语检查
    if (/(.{2,})\1{2,}/.test(content)) return true;

    return false;
  }

  private checkLinks(content: string): boolean {
    const urlPattern = /(https?:\/\/[^\s]+)/gi;
    const urls = content.match(urlPattern);

    if (!urls) return true;

    for (const url of urls) {
      try {
        const urlObj = new URL(url);
        const domain = urlObj.hostname.toLowerCase();

        for (const blacklisted of this.BLACKLISTED_DOMAINS) {
          if (domain.includes(blacklisted)) return false;
        }
      } catch {
        // URL解析失败，允许通过
      }
    }

    return true;
  }

  private escapeRegExp(string: string): string {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  }
}
