<?php
/**
 * HuSNS - 敏感词过滤模型
 * 
 * @author  HYR
 * @QQ      281900864
 * @website https://huyourui.com
 * @license MIT
 */
class FilterModel extends Model
{
    protected $table = 'filter_words';
    
    private $words = [];
    private $tree = [];
    
    /**
     * 初始化敏感词树
     */
    public function initTree()
    {
        if (!empty($this->tree)) {
            return;
        }
        
        $words = $this->getAllWords();
        $this->tree = $this->buildTree($words);
    }
    
    /**
     * 获取所有敏感词
     * @return array
     */
    public function getAllWords()
    {
        if (!empty($this->words)) {
            return $this->words;
        }
        
        $result = $this->db->fetchAll("SELECT word, replacement FROM __PREFIX__filter_words WHERE status = 1");
        $this->words = [];
        
        foreach ($result as $row) {
            $this->words[] = [
                'word' => $row['word'],
                'replacement' => $row['replacement']
            ];
        }
        
        return $this->words;
    }
    
    /**
     * 构建敏感词树
     * @param array $words 敏感词列表
     * @return array
     */
    private function buildTree($words)
    {
        $tree = [];
        
        foreach ($words as $item) {
            $word = $item['word'];
            $node = &$tree;
            
            for ($i = 0; $i < mb_strlen($word); $i++) {
                $char = mb_substr($word, $i, 1);
                
                if (!isset($node[$char])) {
                    $node[$char] = ['children' => [], 'end' => false, 'replacement' => ''];
                }
                
                if ($i === mb_strlen($word) - 1) {
                    $node[$char]['end'] = true;
                    $node[$char]['replacement'] = $item['replacement'];
                }
                
                $node = &$node[$char]['children'];
            }
        }
        
        return $tree;
    }
    
    /**
     * 检测敏感词
     * @param string $text 待检测文本
     * @return bool
     */
    public function hasBadWord($text)
    {
        $this->initTree();
        return $this->checkText($text) !== false;
    }
    
    /**
     * 获取敏感词列表
     * @param string $text 待检测文本
     * @return array
     */
    public function getBadWords($text)
    {
        $this->initTree();
        
        $badWords = [];
        $textLen = mb_strlen($text);
        
        for ($i = 0; $i < $textLen; $i++) {
            $result = $this->checkText($text, $i);
            if ($result !== false) {
                $badWords[] = $result['word'];
                $i += mb_strlen($result['word']) - 1;
            }
        }
        
        return array_unique($badWords);
    }
    
    /**
     * 检测文本（从指定位置开始）
     * @param string $text 文本
     * @param int $startPos 起始位置
     * @return array|false
     */
    private function checkText($text, $startPos = 0)
    {
        $node = $this->tree;
        $textLen = mb_strlen($text);
        $matchedWord = '';
        $replacement = '';
        
        for ($i = $startPos; $i < $textLen; $i++) {
            $char = mb_substr($text, $i, 1);
            
            if (!isset($node[$char])) {
                break;
            }
            
            $matchedWord .= $char;
            
            if ($node[$char]['end']) {
                $replacement = $node[$char]['replacement'];
                
                // 继续检查是否有更长的匹配
                $tempNode = $node[$char]['children'];
                $tempWord = $matchedWord;
                
                for ($j = $i + 1; $j < $textLen; $j++) {
                    $nextChar = mb_substr($text, $j, 1);
                    if (!isset($tempNode[$nextChar])) {
                        break;
                    }
                    $tempWord .= $nextChar;
                    if ($tempNode[$nextChar]['end']) {
                        $matchedWord = $tempWord;
                        $replacement = $tempNode[$nextChar]['replacement'];
                    }
                    $tempNode = $tempNode[$nextChar]['children'];
                    $i = $j;
                }
                
                return ['word' => $matchedWord, 'replacement' => $replacement];
            }
            
            $node = $node[$char]['children'];
        }
        
        return false;
    }
    
    /**
     * 过滤敏感词（替换）
     * @param string $text 待过滤文本
     * @param string $replaceChar 替换字符，默认为*
     * @return string
     */
    public function filter($text, $replaceChar = '*')
    {
        $this->initTree();
        $textLen = mb_strlen($text);
        $result = '';
        
        $i = 0;
        while ($i < $textLen) {
            $checkResult = $this->checkText($text, $i);
            
            if ($checkResult !== false) {
                $wordLen = mb_strlen($checkResult['word']);
                
                // 如果有替换词使用替换词，否则使用*替换
                if (!empty($checkResult['replacement'])) {
                    $result .= $checkResult['replacement'];
                } else {
                    $result .= str_repeat($replaceChar, $wordLen);
                }
                
                $i += $wordLen;
            } else {
                $result .= mb_substr($text, $i, 1);
                $i++;
            }
        }
        
        return $result;
    }
    
    /**
     * 添加敏感词
     * @param string $word 敏感词
     * @param string $replacement 替换词（可选）
     * @return bool
     */
    public function addWord($word, $replacement = '')
    {
        // 检查是否已存在
        $exists = $this->db->count('filter_words', 'word = ?', [$word]);
        if ($exists > 0) {
            return false;
        }
        
        // 重建树
        $this->words = [];
        $this->tree = [];
        
        return $this->insert([
            'word' => $word,
            'replacement' => $replacement,
            'status' => 1,
            'created_at' => time(),
            'updated_at' => time()
        ]);
    }
    
    /**
     * 批量添加敏感词
     * @param array $words 敏感词数组
     * @return int
     */
    public function addWords($words)
    {
        $count = 0;
        
        foreach ($words as $word) {
            $replacement = '';
            if (is_array($word)) {
                $replacement = $word['replacement'] ?? '';
                $word = $word['word'];
            }
            
            if ($this->addWord($word, $replacement)) {
                $count++;
            }
        }
        
        return $count;
    }
    
    /**
     * 删除敏感词
     * @param int $id 敏感词ID
     * @return bool
     */
    public function deleteWord($id)
    {
        // 重建树
        $this->words = [];
        $this->tree = [];
        
        return $this->delete($id);
    }
    
    /**
     * 更新敏感词
     * @param int $id 敏感词ID
     * @param string $word 敏感词
     * @param string $replacement 替换词
     * @return bool
     */
    public function updateWord($id, $word, $replacement = '')
    {
        // 重建树
        $this->words = [];
        $this->tree = [];
        
        return $this->update($id, [
            'word' => $word,
            'replacement' => $replacement,
            'updated_at' => time()
        ]);
    }
    
    /**
     * 获取敏感词列表（分页）
     * @param int $page 页码
     * @param int $pageSize 每页数量
     * @param string $keyword 关键词
     * @return array
     */
    public function getWords($page = 1, $pageSize = 20, $keyword = '')
    {
        $offset = ($page - 1) * $pageSize;
        
        $where = [];
        $params = [];
        
        if (!empty($keyword)) {
            $where[] = 'word LIKE ?';
            $params[] = "%{$keyword}%";
        }
        
        $whereStr = $where ? 'WHERE ' . implode(' AND ', $where) : '';
        
        $sql = "SELECT * FROM __PREFIX__filter_words {$whereStr} ORDER BY created_at DESC LIMIT ?, ?";
        
        $params[] = $offset;
        $params[] = $pageSize;
        
        $words = $this->db->fetchAll($sql, $params);
        
        $total = $this->countWords($keyword);
        $totalPages = ceil($total / $pageSize);
        
        return [
            'items' => $words,
            'total' => $total,
            'page' => $page,
            'pageSize' => $pageSize,
            'totalPages' => $totalPages
        ];
    }
    
    /**
     * 统计敏感词数量
     * @param string $keyword 关键词
     * @return int
     */
    public function countWords($keyword = '')
    {
        $where = [];
        $params = [];
        
        if (!empty($keyword)) {
            $where[] = 'word LIKE ?';
            $params[] = "%{$keyword}%";
        }
        
        $whereStr = $where ? 'WHERE ' . implode(' AND ', $where) : '';
        
        $sql = "SELECT COUNT(*) as count FROM __PREFIX__filter_words {$whereStr}";
        $result = $this->db->fetch($sql, $params);
        
        return (int)($result['count'] ?? 0);
    }
    
    /**
     * 获取敏感词详情
     * @param int $id 敏感词ID
     * @return array|null
     */
    public function getWord($id)
    {
        return $this->find($id);
    }
    
    /**
     * 导入敏感词（从文件）
     * @param string $filePath 文件路径
     * @return int
     */
    public function importFromFile($filePath)
    {
        if (!file_exists($filePath)) {
            return 0;
        }
        
        $content = file_get_contents($filePath);
        $lines = explode("\n", $content);
        
        $count = 0;
        foreach ($lines as $line) {
            $line = trim($line);
            if (!empty($line) && !preg_match('/^#/', $line)) {
                // 支持格式：敏感词|替换词
                $parts = explode('|', $line, 2);
                $word = $parts[0];
                $replacement = isset($parts[1]) ? $parts[1] : '';
                
                if ($this->addWord($word, $replacement)) {
                    $count++;
                }
            }
        }
        
        return $count;
    }
}
?>