package com.jsc.dsp.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.protobuf.Descriptors;
import com.jsc.dsp.binding.ProtobufBinding;
import com.jsc.dsp.proto.EsOuterClass;
import com.jsc.dsp.utils.FileUtils;
import com.jsc.dsp.utils.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.json.JacksonJsonParser;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Base64.Decoder;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
@EnableBinding(ProtobufBinding.class)
public class ProtobufService extends StreamService {

    @Autowired
    Decoder base64;

    @Autowired
    JacksonJsonParser jsonParser;

    @Value("${custom.proto_save_path}")
    String protoSavePath;

    @Value("${custom.transfer_backup_path}")
    String transferBackupPath;

    @Value("${custom.keep_backup_file}")
    String keepBackupFile;

    @Value("${custom.filter-words-query-api}")
    String filterWordsQueryAPI;

    @Value("${custom.filter-words-update-interval-ms}")
    Long filterWordsUpdateIntervalMs;

    private final Logger logger = LogManager.getLogger(ProtobufService.class.getName());

    @Autowired
    private ProtobufBinding source;

    @Override
    public void sendMessage(byte[] msg) {
        source.protobufOutPut().send(MessageBuilder.withPayload(msg).build());
    }

    private final FileUtils fileUtils = new FileUtils();

    private static Map<String, List<String>> filterWordsMap = null;

    private long filterMapUpdateTimestamp = 0L;

    @Override
    @StreamListener(ProtobufBinding.PROTOBUF_PIPELINE_IN)
    public void receiveMessage(Object payload) {
        try {
            logger.debug("protobuf in");
            String tempString = new String(base64.decode(payload.toString()), StandardCharsets.UTF_8);
            Map<String, Object> remoteData = jsonParser.parseMap(tempString);
            Map<String, Object> localData = new HashMap<>();
            String protoName = remoteData.get("protoName").toString();
            // 处理带HTML标签的内容文本
            if ("public_info_data_".equals(protoName)) {
                EsOuterClass.EsSets.Builder remoteEsSetsBuilder = EsOuterClass.EsSets.newBuilder();
                EsOuterClass.EsSets.Builder localEsSetsBuilder = EsOuterClass.EsSets.newBuilder();
                String contentWithTags = "";
                EsOuterClass.EsSets esSets = EsOuterClass.EsSets.parseFrom(remoteData.get("content").toString().getBytes(StandardCharsets.ISO_8859_1));
                for (int i = 0; i < esSets.getEsCount(); i++) {
                    EsOuterClass.Es es = esSets.getEs(i);
                    Map<Descriptors.FieldDescriptor, Object> fieldsMap = es.getAllFields();
                    // 带有 HTML 标签的内容初始存储在 es_urlcontent 中，需要转移到 es_links 字段中，同时 es_urlcontent 用来存储纯文本
                    for (Descriptors.FieldDescriptor key : fieldsMap.keySet()) {
                        if (key.getName().equals("es_urlcontent")) {
                            Object value = fieldsMap.get(key);
                            contentWithTags = value.toString();
                        }
                    }
                    String contentText = StringUtils.Html2Text(contentWithTags);
                    if (contentText != null) {
                        EsOuterClass.Es.Builder builder = EsOuterClass.Es.newBuilder(es);
                        builder.setEsUrlcontent(contentText);
                        builder.setEsDoclength(String.valueOf(contentText.length()));
                        builder.setEsLinks(contentWithTags);
                        EsOuterClass.Es temp = builder.build();
                        if (filterWordsMap == null || System.currentTimeMillis() - filterMapUpdateTimestamp > filterWordsUpdateIntervalMs) {
                            Map<String, List<String>> queryFilterWordsMap = StringUtils.initFilterMap(filterWordsQueryAPI);
                            if (queryFilterWordsMap.size() > 0) {
                                filterWordsMap = queryFilterWordsMap;
                                filterMapUpdateTimestamp = System.currentTimeMillis();
                                logger.info("过滤词列表更新成功！" + StringUtils.TimestampToStringDate(filterMapUpdateTimestamp));
                            } else {
                                logger.error("过滤词列表更新失败！");
                            }
                        }
                        String filterString = builder.getEsWarning();
                        if (filterString != null && filterString.length() > 0) {
                            JSONObject filterConfig = JSON.parseObject(filterString);
                            JSONArray globalInCate = filterConfig.getJSONArray("global_in");
                            JSONArray globalExCate = filterConfig.getJSONArray("global_ex");
                            String contentIn = filterConfig.getString("content_in");
                            String contentEx = filterConfig.getString("content_ex");
                            List<String> inWords = StringUtils.getFilterWordList(contentIn, filterWordsMap, globalInCate);
                            List<String> exWords = StringUtils.getFilterWordList(contentEx, filterWordsMap, globalExCate);
                            if (!StringUtils.checkContentWithFilterWords(contentText, exWords, inWords)) {
                                logger.warn("Filter word check no pass!");
                                builder.setEsWarningWord("Filtered");
                                temp = builder.build();
                                localEsSetsBuilder.addEs(temp);
                                continue;
                            }
                        }
                        remoteEsSetsBuilder.addEs(temp);
                        localEsSetsBuilder.addEs(temp);
                    }
                }
                remoteData.put("content", new String(remoteEsSetsBuilder.build().toByteArray(), StandardCharsets.ISO_8859_1));
                localData.put("content", new String(localEsSetsBuilder.build().toByteArray(), StandardCharsets.ISO_8859_1));
                logger.info("Filter HTML tag done");
            }

            StringBuilder fullPath = new StringBuilder();
            if (remoteData.containsKey("savePath")) {
                fullPath.append(remoteData.get("savePath")).append(new Date().getTime()).append(".todist");
            } else {
                fullPath.append(protoSavePath).append(protoName).append(new Date().getTime()).append(".todist");
            }
            //判断路径是否存在，不存在则创建
            int lastIndex = fullPath.lastIndexOf("/");
            if (lastIndex <= 0) {
                lastIndex = fullPath.lastIndexOf("\\");
            }
            logger.debug("protobuf get filepath");
            File file = new File(fullPath.substring(0, lastIndex));
            if (!file.exists()) {
                file.mkdirs();
            }
            if (remoteData.get("content") == null || remoteData.get("content").toString().length() == 0) {
                return;
            }
            logger.debug("protobuf get content");
            OutputStream oStream = new FileOutputStream(fullPath.toString());
            oStream.write(remoteData.get("content").toString().getBytes(StandardCharsets.ISO_8859_1));
            oStream.flush();
            oStream.close();
            logger.info(String.format("protobuf write file: %s", fullPath.substring(lastIndex + 1)));
            if (!localData.isEmpty()) {
                localData.put("processQueue", remoteData.get("processQueue"));
                localData.put("protoName", "public_info_data_");
                sendToNext(localData);
            }
            logger.debug("protobuf done");
            // 转移备份目录的todist文件
            File transferPath = new File(transferBackupPath);
            File[] files = transferPath.listFiles();
            if (files != null && files.length > 0) {
                for (File transferFile : files) {
                    if (transferFile.getName().endsWith(".todist")) {
                        fileUtils.moveFileToBackupFolder(transferFile, keepBackupFile);
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }
}
