package com.tmerclub.cloud.common.handler;

import cn.hutool.core.util.StrUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.tmerclub.cloud.common.bean.SensitiveWord;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.dubbo.CoreConfigFeignClient;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.util.Json;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;

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

/**
 * @author TRACK
 */
@SuppressWarnings("rawtypes")
@Component
public class SensitiveHandler {

    @DubboReference
    private CoreConfigFeignClient configFeignClient;


    /**
     * 缓存
     */
    private static final Cache<Object, Object> CONFIG_MAP = Caffeine.newBuilder()
            // 数量上限
            .maximumSize(1)
            // 过期机制
            .expireAfterWrite(5, TimeUnit.MINUTES).build();
    /**
     * 最小匹配
     */
    public static final int MIN_MATCH_TYPE = 1;
    private static final String IS_END = "isEnd";
    private static final String IS_ONE = "1";



    public String replaceSensitiveWord(String txt, int matchType, String replaceChar) {
        String resultTxt = txt;
        Set<String> sensitiveWordSet = this.getSensitiveWord(txt, matchType);
        Iterator<String> iterator = sensitiveWordSet.iterator();
        String word;
        String replaceString;
        while (iterator.hasNext()) {
            word = iterator.next();
            replaceString = getReplaceChars(replaceChar, word.length());
            resultTxt = resultTxt.replaceAll(word, replaceString);
        }
        return resultTxt;
    }


    public Set<String> getSensitiveWord(String txt, int matchType) {
        Set<String> sensitiveWordList = new HashSet<>(Constant.INITIAL_CAPACITY);
        for (int i = 0; i < txt.length(); i++) {
            int length = checkSensitiveWord(txt, i, matchType);
            if (length > 0) {
                sensitiveWordList.add(txt.substring(i, i + length));
                i = i + length - 1;
            }
        }
        return sensitiveWordList;
    }

    private String getReplaceChars(String replaceChar, int length) {
        StringBuilder resultReplace = new StringBuilder(Constant.INITIAL_CAPACITY);
        resultReplace.append(replaceChar);
        for (int i = 1; i < length; i++) {
            resultReplace.append(replaceChar);
        }
        return resultReplace.toString();
    }

    private int checkSensitiveWord(String txt, int beginIndex, int matchType) {
        Map nowMap = getSensitiveWordMap();
        boolean flag = false;
        char word;
        int matchFlag = 0;
        for (int i = beginIndex; i < txt.length(); i++) {
            word = txt.charAt(i);
            nowMap = (Map) nowMap.get(word);
            if (Objects.isNull(nowMap)) {
                break;
            }
            matchFlag++;
            if (isEnd(nowMap)) {
                flag = true;
                if (Objects.equals(MIN_MATCH_TYPE, matchType)) {
                    break;
                }
            }
        }
        if (matchFlag <= 1 || !flag) {
            matchFlag = 0;
        }
        return matchFlag;
    }


    private boolean isEnd(Map nowMap) {
        return Objects.equals(nowMap.get(IS_END), IS_ONE);
    }


    @SuppressWarnings("unchecked")
    private Map getSensitiveWordMap() {
        Map finalMap = (Map) CONFIG_MAP.get(Constant.SENSITIVE_WORDS, cacheKey -> {
            SensitiveWord sensitiveWord;
            try {
                ServerResponseEntity<String> configResponse = configFeignClient.getConfig(Constant.SENSITIVE_WORDS);
                if (!configResponse.isSuccess()) {
                    throw new LuckException(ResponseEnum.EXCEPTION);
                }
                String value = configResponse.getData();
                sensitiveWord = Json.parseObject(value, SensitiveWord.class);
            } catch (Exception e) {
                return Collections.emptyMap();
            }
            if (StrUtil.isBlank(sensitiveWord.getSensitiveWords())) {
                return Collections.emptyMap();
            }
            //根据中文逗号分隔
            List<String> sensitiveWordsList = Arrays.asList(sensitiveWord.getSensitiveWords().split(Constant.CN_COMMA));
            Set<String> sensitiveWordSet = new HashSet<>(sensitiveWordsList);
            Iterator<String> iterator = sensitiveWordSet.iterator();
            String key;
            Map nowMap;
            Map<String, String> newWordMap;
            Map<Object, Object> sensitiveWordMap = new HashMap<>(sensitiveWordSet.size());
            while (iterator.hasNext()) {
                //敏感词
                key = iterator.next();
                nowMap = sensitiveWordMap;
                for (int i = 0; i < key.length(); i++) {
                    //敏
                    char charAt = key.charAt(i);
                    Object wordMap = nowMap.get(charAt);
                    if (wordMap != null) {
                        //逐字加入map
                        nowMap = (Map) wordMap;
                    } else {
                        newWordMap = new HashMap<>(sensitiveWordSet.size());
                        //最后一个字，加标记
                        newWordMap.put("isEnd", "0");
                        nowMap.put(charAt, newWordMap);
                        nowMap = newWordMap;
                    }
                    if (i == key.length() - 1) {
                        //结束
                        nowMap.put("isEnd", "1");
                    }
                }
            }
            return sensitiveWordMap;
        });

        if (finalMap == null) {
            CONFIG_MAP.invalidate(Constant.SENSITIVE_WORDS);
        }

        return finalMap;
    }
}
