package com.jsc.dsp.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.jsc.dsp.binding.StorageBinding;
import com.jsc.dsp.model.Indeximos;
import com.jsc.dsp.proto.EsOuterClass.Es;
import com.jsc.dsp.proto.EsOuterClass.EsSets;
import com.jsc.dsp.utils.DBUtils;
import com.jsc.dsp.utils.EsUtils;
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.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Base64.Decoder;

@Component
@EnableBinding(StorageBinding.class)
@ConditionalOnProperty(name = "switch.enable-storage-service", havingValue = "true", matchIfMissing = true)
public class StorageService extends StreamService {

    @Autowired
    Decoder base64;

    @Autowired
    StorageBinding source;

    @Autowired
    JacksonJsonParser jsonParser;

    @Value("${custom.dev-mode}")
    boolean devMode;

    @Value("${custom.local-file-storage-path}")
    String localFileStoragePath;

    @Value("${custom.websiteWhiteList}")
    String websiteWhiteListString;

    @Value("${db.driver}")
    String dbDriver;

    @Value("${db.url}")
    String dbUrl;

    @Value("${db.user}")
    String dbUser;

    @Value("${db.password}")
    String dbPassword;


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


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

    public final List<String> dateFields = Arrays.asList("es_attentiontime", "es_lasttime", "es_loadtime",
            "es_similaritytime", "es_urltime", "es_warningtime");

    @Override
    @StreamListener(StorageBinding.STORAGE_PIPELINE_IN)
    public void receiveMessage(Object payload) {
        List<String> websiteWhiteList = Arrays.asList(websiteWhiteListString.split(";"));
        String tempString;
        try {
            tempString = new String(base64.decode(payload.toString()), StandardCharsets.UTF_8);
            Map<String, Object> data = jsonParser.parseMap(tempString);
            String protoName = data.get("protoName").toString();
            //匹配对应的proto，目前只有EsSets
            if ("public_info_data_".equals(protoName)) {
                EsSets.Builder esSetsBuilder = EsSets.newBuilder();
                EsSets esSets = EsSets.parseFrom(data.get("content").toString().getBytes(StandardCharsets.ISO_8859_1));
                List<Indeximos> dbStorageItems = new ArrayList<>();
                BulkRequest bulkRequest = new BulkRequest();
                bulkRequest.timeout("5s");
                for (int i = 0; i < esSets.getEsCount(); i++) {
                    Es es = esSets.getEs(i);
                    Es.Builder builder = Es.newBuilder(es);
                    Map<FieldDescriptor, Object> fieldsMap = es.getAllFields();
                    Indeximos indeximos = new Indeximos();
                    for (FieldDescriptor key : fieldsMap.keySet()) {
                        boolean hasField = DBUtils.hasField(Indeximos.class, key.getName());
                        if (!hasField) {
                            continue;
                        }
                        Object temp = fieldsMap.get(key);
                        String value = temp.toString();
                        if (dateFields.contains(key.getName())) {
                            long _value = 0L;
                            try {
                                _value = Long.parseLong(value);
                            } catch (Exception e) {
                                logger.error("Invalid Long type data: " + value);
                            }
                            Field field = indeximos.getClass().getDeclaredField(key.getName());
                            field.setAccessible(true);
                            field.set(indeximos, StringUtils.TimestampToStringDate(_value));
                        } else {
                            Field field = indeximos.getClass().getDeclaredField(key.getName());
                            field.setAccessible(true);
                            String fieldType = DBUtils.getFieldType(Indeximos.class, key.getName());
                            if (fieldType.contains("Float")) {
                                field.set(indeximos, Float.valueOf(value));
                            } else {
                                field.set(indeximos, value);
                            }
                        }
                    }
                    // 只导出目标站点的数据
                    if (websiteWhiteList.contains(indeximos.getEs_sitename())) {
                        logger.info("开始处理站点【" + indeximos.getEs_sitename() + "】的数据入库流程");
                        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                        String es_urlname = indeximos.getEs_urlname();
                        if (!es_urlname.isEmpty()) {
                            // 根据urlname生成固定的UUID，避免重复入库相同的文章
                            UUID _uuid = UUID.nameUUIDFromBytes(es_urlname.getBytes());
                            uuid = _uuid.toString().replaceAll("-", "");
                        }
                        indeximos.setEs_urltitle(indeximos.getEs_urltitle().trim());
                        indeximos.setEs_sid(uuid);
                        indeximos.setEs_links(indeximos.getEs_links());
                        indeximos.setEs_loadtime(StringUtils.TimestampToStringDate(System.currentTimeMillis()));
                        builder.setEsSid(uuid);
                        for (Field f : indeximos.getClass().getDeclaredFields()) {
                            f.setAccessible(true);
                            //判断字段是否为空，并且对象属性中的基本都会转为对象类型来判断
                            if (f.get(indeximos) == null) {
                                String fieldType = DBUtils.getFieldType(Indeximos.class, f.getName());
                                if (fieldType.contains("Float")) {
                                    f.set(indeximos, 0.0f);
                                } else {
                                    if (!dateFields.contains(f.getName())) {
                                        f.set(indeximos, "");
                                    }
                                }
                            }
                        }
                        dbStorageItems.add(indeximos);
                    }
                }
                if (dbStorageItems.size() > 0) {
                    if (DBUtils.insertIntoDB(dbDriver, dbUrl, dbUser, dbPassword, dbStorageItems)) {
                        logger.info("Store to MySQL Database");
                    } else {
                        logger.error("MySQL Database Storage error!");
                    }
                }
                data.put("content", new String(esSetsBuilder.build().toByteArray(), StandardCharsets.ISO_8859_1));
            }
            sendToNext(data);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

}
