package com.fs.app.config;

import com.fs.live.service.ILiveSensitiveWordsService;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Component
public class ProductionWordFilter implements InitializingBean {

    @Autowired
    private ILiveSensitiveWordsService liveSensitiveWordsService;


    private static class TrieNode {
        @Getter
        private final Map<Character, TrieNode> children = new HashMap<>();
        private boolean isEndOfWord;

        public boolean isEndOfWord() {
            return isEndOfWord;
        }

        public void setEndOfWord(boolean endOfWord) {
            isEndOfWord = endOfWord;
        }

    }
    @Getter
    private static class MatchResult {
        private final String word;
        private final int endIndex;

        public MatchResult(String word, int endIndex) {
            this.word = word;
            this.endIndex = endIndex;
        }

        public int getLength() {
            return word.length();
        }
    }

    @Getter
    public static class FilterResult {
        private final String filteredText;
        private final Set<String> matchedWords;
        private final int replacedCount;

        public FilterResult(String filteredText, Set<String> matchedWords, int replacedCount) {
            this.filteredText = filteredText;
            this.matchedWords = matchedWords;
            this.replacedCount = replacedCount;
        }

    }

    private TrieNode root = new TrieNode();
    private List<String> wordSources;
    private final ScheduledExecutorService executor;

    public ProductionWordFilter(List<String> wordSources) {
        this.wordSources = wordSources;
        this.executor = Executors.newSingleThreadScheduledExecutor();
    }

    @Override
    public void afterPropertiesSet() {
        reload();
        executor.scheduleWithFixedDelay(this::reload, 1, 1, TimeUnit.HOURS);
    }

    public synchronized void reload() {
        wordSources = liveSensitiveWordsService.selectAllWords();
        TrieNode newRoot = new TrieNode();
        wordSources.stream()
                .flatMap(source -> loadWords(source).stream())
                .forEach(word -> addWord(newRoot, word));
        this.root = newRoot;
    }

    public FilterResult filter(String text) {
        StringBuilder result = new StringBuilder();
        Set<String> foundWords = new HashSet<>();
        int replacedCount = 0;

        for (int i = 0; i < text.length(); ) {
            MatchResult match = findNextMatch(text, i);
            if (match != null) {
                foundWords.add(match.getWord());
                result.append(StringUtils.repeat("", match.getLength()));
                replacedCount++;
                i = match.getEndIndex();
            } else {
                result.append(text.charAt(i));
                i++;
            }
        }

        return new FilterResult(result.toString(), foundWords, replacedCount);
    }


    private MatchResult findNextMatch(String text, int start) {
        TrieNode current = root;
        for (int i = start; i < text.length(); i++) {
            char c = text.charAt(i);
            if (!current.getChildren().containsKey(c)) {
                return null;
            }
            current = current.getChildren().get(c);
            if (current.isEndOfWord()) {
                return new MatchResult(text.substring(start, i + 1), i + 1);
            }
        }
        return null;
    }

    private void addWord(TrieNode root, String word) {
        TrieNode node = root;
        for (char c : word.toCharArray()) {
            node = node.getChildren().computeIfAbsent(c, k -> new TrieNode());
        }
        node.setEndOfWord(true);
    }

    private List<String> loadWords(String source) {
        // 示例：实际应从 source（如 URL 或文件路径）读取
        return Collections.singletonList(source); // 替换为真实逻辑
    }


}
