#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
AC自动机敏感词检测核心模块
提供线程安全的敏感词检测功能
"""

import os
import threading
from pathlib import Path
from collections import deque
from typing import List, Dict, Any, Optional
from foundation.observability.logger.loggering import review_logger as logger


class ACNode:
    """AC自动机节点"""
    
    def __init__(self):
        self.children = {}
        self.fail = None
        self.is_end = False
        self.word = None
        self.source = None


class ACAutomaton:
    """AC自动机实现"""
    
    def __init__(self):
        self.root = ACNode()
        self._lock = threading.RLock()
    
    def add_word(self, word: str, source: Optional[str] = None):
        """添加敏感词到字典树
        
        Args:
            word: 敏感词
            source: 词源（来自哪个文件）
        """
        if not word:
            return
            
        with self._lock:
            node = self.root
            for char in word:
                if char not in node.children:
                    node.children[char] = ACNode()
                node = node.children[char]
            node.is_end = True
            node.word = word
            node.source = source
    
    def build_fail_pointer(self):
        """构建失败指针（KMP的fail数组思想）"""
        with self._lock:
            queue = deque()
            
            for child in self.root.children.values():
                child.fail = self.root
                queue.append(child)
            
            while queue:
                current = queue.popleft()
                
                for char, child in current.children.items():
                    queue.append(child)
                    
                    fail_node = current.fail
                    while fail_node is not None:
                        if char in fail_node.children:
                            child.fail = fail_node.children[char]
                            break
                        fail_node = fail_node.fail
                    
                    if child.fail is None:
                        child.fail = self.root
    
    def search(self, text: str) -> List[Dict[str, Any]]:
        """搜索文本中的敏感词
        
        Args:
            text: 待检测文本
            
        Returns:
            List[Dict]: 检测结果列表，每项包含：
                - word: 敏感词
                - position: 起始位置（原文本中的位置）
                - end_position: 结束位置（原文本中的位置）
                - source: 词源
                - original_text: 原文本中的实际文本（包含空格和符号）
        """
        if not text:
            return []
        
        # 去除空格和符号，只保留中文、英文、数字
        # 同时建立清洗后文本到原文本的位置映射
        cleaned_text = []
        position_map = []  # 记录清洗后每个字符在原文本中的位置
        
        for i, char in enumerate(text):
            # 保留中文字符、英文字母、数字
            if char.isalnum() or '\u4e00' <= char <= '\u9fff':
                cleaned_text.append(char)
                position_map.append(i)
        
        cleaned_text_str = ''.join(cleaned_text)
        
        if not cleaned_text_str:
            return []
        
        results = []
        node = self.root
        
        for i, char in enumerate(cleaned_text_str):
            while node is not None and char not in node.children:
                node = node.fail
            
            if node is None:
                node = self.root
                continue
            
            node = node.children[char]
            
            temp = node
            while temp is not None:
                if temp.is_end:
                    # 计算在清洗后文本中的位置
                    cleaned_start = i - len(temp.word) + 1
                    cleaned_end = i + 1
                    
                    # 映射回原文本的位置
                    original_start = position_map[cleaned_start]
                    original_end = position_map[cleaned_end - 1] + 1
                    
                    results.append({
                        'word': temp.word,
                        'position': original_start,
                        'end_position': original_end,
                        'source': temp.source,
                        'original_text': text[original_start:original_end]
                    })
                temp = temp.fail
        
        return results


class SensitiveWordDetector:
    """敏感词检测器（单例模式）"""
    
    _instance = None
    _lock = threading.Lock()
    _initialized = False
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
            
        self.ac_automaton = ACAutomaton()
        self._initialized = True
        self._word_count = 0
    
    def load_words_from_directory(self, words_dir: str) -> Dict[str, int]:
        """从目录加载敏感词
        
        Args:
            words_dir: 敏感词文件目录
            
        Returns:
            Dict: 加载统计信息
        """
        words_path = Path(words_dir)
        if not words_path.exists():
            logger.error(f"敏感词目录不存在: {words_dir}")
            return {'total': 0, 'files': 0, 'error': '目录不存在'}
        
        total_words = 0
        file_count = 0
        file_stats = {}
        
        logger.info(f"开始加载敏感词库: {words_dir}")
        
        for file_path in words_path.glob('*.txt'):
            try:
                source_name = file_path.stem
                file_word_count = 0
                
                with open(file_path, 'r', encoding='utf-8') as f:
                    for line in f:
                        word = line.strip()
                        if word:
                            self.ac_automaton.add_word(word, source_name)
                            total_words += 1
                            file_word_count += 1
                
                file_stats[source_name] = file_word_count
                file_count += 1
                logger.info(f"已加载: {file_path.name} ({file_word_count} 个词)")
                
            except Exception as e:
                logger.error(f"加载 {file_path.name} 时出错: {e}")
        
        logger.info(f"构建AC自动机指针...")
        self.ac_automaton.build_fail_pointer()
        logger.info(f"AC自动机构建完成！总共加载 {total_words} 个敏感词，来自 {file_count} 个文件")
        
        self._word_count = total_words
        
        return {
            'total': total_words,
            'files': file_count,
            'file_stats': file_stats
        }

