package com.cisdi.data.DHV.gateway.impl.dhv;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cisdi.data.DHV.gateway.config.YFHttpClient;
import com.cisdi.data.DHV.gateway.impl.HttpPullTaskBase;
import com.cisdi.data.DHV.gateway.vo.PullHttpTaskStartVo;
import com.cisdi.data.DHV.gateway.vo.dhv.*;
import com.cisdi.data.common.exception.BusinessException;

import com.cisdi.data.sdk.consts.ServiceName;
import com.cisdi.data.sdk.gateway.base.PulledHttpGatewayBase;
import com.cisdi.data.sdk.gateway.message.HttpMessage;
import com.cisdi.data.sdk.gateway.message.Message;
import com.cisdi.data.sdk.gateway.message.MessageBatch;
import com.cisdi.data.sdk.service.SendService;
import com.cisdi.data.sdk.service.ServiceProvider;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author jianghc
 * @function:
 * @date 2022年05月05日 10:32
 */
public class YFDHVPullTask extends HttpPullTaskBase implements Runnable {

    private Logger logger = LoggerFactory.getLogger(YFDHVPullTask.class);

    //请求测点状态的URL地址
    private String[] DHV_MPS_URL = null;

    //获取teachName的URL地址
    private String[] DHV_MET_URL = null;

    //请求测点总值的URL地址
    private String[] DHV_MPV_URL = null;

    //测点的ID
    private String[] measuringPointNumberOrTechIDs = null;

    //base64编码的user:password
    private String Authorization;

    //DHV请求必须携带的头部参数
    private String emptyanddefault;

    //超时时间
    private Integer timeOut;

    //其他参数中配置的数据库
    private List<String> dbNames;

    //判断当前是否获取了db-测点id-测点方法的一个标志，在网关启动的时候执行一次后变成true，并装入下面三个容器中
    private boolean metPara = false;
    private List<String> dbNameList;
    private List<String> itemIdList;
    private List<String> methodNameList;

    //连接管理中的拉取地址，以上所有的URL地址都是用这个地址为前缀拼接成的
    private String url;

    public YFDHVPullTask(PullHttpTaskStartVo pullHttpTaskStartVo, OtherParamVO otherParamVO) {
        checkInitParam(pullHttpTaskStartVo, otherParamVO);

        url = startVo.getHttpParam().getPullLocation();
        if (url == null) {
            logger.error("拉取地址不能为空!");
            throw new BusinessException("拉取地址不能为空!");
        }
        if (url.endsWith("/")) {
            url = url.substring(0, url.length() - 1);
        }

        dbNames = otherParamVO.getMpsvo().getDatabaseName();
        int dbNamesLen = dbNames.size();
        if (dbNamesLen > 0) {
            DHV_MPS_URL = new String[dbNamesLen];
            for (int i = 0; i < dbNamesLen; i++) {
                DHV_MPS_URL[i] = url + String.format(YFDHV.YF_DHV_MPS, dbNames.get(i));
            }
        }

        Authorization = otherParamVO.getAuthorization();

        emptyanddefault = otherParamVO.getEmptyanddefault() == null ? "1" : otherParamVO.getEmptyanddefault();

        timeOut = startVo.getHttpParam().getTimeout();

    }


    @Override
    public void run() {
        OkHttpClient httpClient = YFHttpClient.get(startVo.getHttpParam().getPullInternal());
        if (DHV_MPS_URL != null) {
            for (int i = 0; i < DHV_MPS_URL.length; i++) {
                logger.info("MPS采集地址：" + DHV_MPS_URL[i]);
            }
        } else {
            logger.info("您尚未配置采集地址");
        }

        //在这个驱动中，我们只请求如下时间范围的数据，从当前时间往前数半个小时（第一次请求这样）
        //以后每个半个小时请求一次，每次的请求时间范围是，上一次的请求时间到当前时间点，通常会比半个小时多一点点，
        //多出来的时间是上一次请求的耗时时间
        LocalDateTime endTime = null;
        LocalDateTime startTime = null;
        while (startVo.getShouldRun().get() == true) {
            logger.info("即将采集设备数据......");
            LocalDateTime end_time_temp = null;
            if (endTime != null) {
                end_time_temp = endTime;
            }
            endTime = LocalDateTime.now();
            if (startTime == null) {
                startTime = endTime.minusMinutes(30);
            } else {
                startTime = end_time_temp;
            }
            Map<String, String> mpsJsonStringMap = MPSRequest(httpClient);
            if (DHV_MET_URL == null) {
                Construct_DHV_MET_URL();
            }
            if (DHV_MPV_URL == null) {
                METRequest(httpClient);
            }
            Map<String, JSONObject> mpvJsonArrayMap = MPVRequest(httpClient, startTime, endTime);
            if (mpsJsonStringMap != null && mpsJsonStringMap.size() > 0) {
                for (int i = 0; i < mpsJsonStringMap.size(); i++) {
                    String msgKey = dbNames.get(i) + YFDHV.YF_Separator + YFDHV.YF_MPS;
                    sendToNextMQ(mpsJsonStringMap.get(dbNames.get(i)), msgKey);
                }
            }
            if (mpvJsonArrayMap != null && mpvJsonArrayMap.size() > 0) {
                List<String> dbNameList = new ArrayList<>(mpvJsonArrayMap.keySet());
                for (int i = 0; i < dbNameList.size(); i++) {
                    String dbName = dbNameList.get(i);
                    sendToNextMQ(mpvJsonArrayMap.get(dbName).toJSONString(), dbName);
                }
            }
            try {
                Thread.sleep(startVo.getHttpParam().getPullInternal());
            } catch (InterruptedException e) {
                logger.error("休眠异常");
                throw new BusinessException("休眠异常");
            }
        }
    }

    /**
     * @param httpClient
     * @return java.lang.String
     * @function: 终极奥义之通过DHV_MPS_URL地址获取测点状态信息
     * @author jianghc
     * @date 2022/5/24 16:16
     */
    private Map<String, String> MPSRequest(OkHttpClient httpClient) {
        if (!metPara) {
            dbNameList = new ArrayList<>();
            itemIdList = new ArrayList<>();
        }
        if (DHV_MPS_URL == null) {
            return null;
        }
        Map<String, String> mpsJsonMap = new HashMap<>();
        int DHV_MPS_URL_Len = DHV_MPS_URL.length;

        for (int i = 0; i < DHV_MPS_URL_Len; i++) {
            okhttp3.Request request = new okhttp3.Request.Builder()
                    .header("Authorization", Authorization)
                    .header("emptyanddefault", emptyanddefault)
                    .url(DHV_MPS_URL[i])
                    .get()
                    .build();
            Response response = null;
            try {
                Long currentTime_start = System.currentTimeMillis();
                Long currentTime_end = 0l;
                response = httpClient.newCall(request).execute();
                currentTime_end = System.currentTimeMillis();
                if (currentTime_end - currentTime_start > timeOut) {
                    logger.error("地址:" + DHV_MPS_URL[i] + "请求超时！");
                    throw new BusinessException("地址:" + DHV_MPS_URL[i] + "请求超时！");
                }
            } catch (Exception e) {
                logger.error("被请求的接口:" + DHV_MPS_URL[i] + "请求超时，" + e.getMessage());
                throw new BusinessException("被请求的接口:" + DHV_MPS_URL[i] + "请求超时，" + e.getMessage());
            }
            if (response.isSuccessful()) {
                String jsonString = null;
                try {
                    jsonString = new String(response.body().bytes(), StandardCharsets.UTF_8);
                } catch (Exception ex) {
                    logger.error("MPS响应数据解析异常");
                    throw new BusinessException("MPS响应数据解析异常");
                }
                Object jsonArrayObject = ((JSONObject) JSONObject.parse(jsonString)).get("values");
                JSONObject resJson = new JSONObject();
                if (jsonArrayObject instanceof JSONArray) {
                    JSONArray jsonArray = (JSONArray) jsonArrayObject;
                    for (int j = 0; j < jsonArray.size(); j++) {
                        JSONObject jsonObject = (JSONObject) jsonArray.get(j);
                        MPSItem[] mpsItems = MPSItem.values();
                        MPSItem primary = MPSItem.getPrimary();
                        Object primaryValue = jsonObject.get(primary.toString());

                        String mp = jsonObject.getString(MPSItem.getPrimary().getMpsItem());
                        if (!AllowMeasuringPoint.allowMeasuringPoint(mp)) {
                            continue;
                        }

                        for (MPSItem mpsItem : mpsItems) {
                            String key = dbNames.get(i) + YFDHV.YF_Separator + primaryValue + YFDHV.YF_Separator + mpsItem.getMpsItem();
                            Object value = jsonObject.get(mpsItem.toString());
                            resJson.fluentPut(key, value);
                        }
                        resJson.fluentPut(dbNames.get(i) + YFDHV.YF_Separator + primaryValue + YFDHV.YF_Separator + YFDHV.DateTime
                                , LocalDateTime.now());//注意测点状态时间是系统生成的，没有从接口中获取到
                        if (!metPara) {
                            String mpx = jsonObject.getString(MPSItem.getPrimary().getMpsItem());
                            if (!AllowMeasuringPoint.allowMeasuringPoint(mpx)) {
                                continue;
                            }
                            dbNameList.add(dbNames.get(i));
                            itemIdList.add(mpx);
                        }
                    }
                } else {
                    logger.warn("地址:" + DHV_MPS_URL[i] + "    未采集到数据!");
                }
                if (resJson.size() > 0) {
                    mpsJsonMap.put(dbNames.get(i), resJson.toJSONString());
                }
            }
        }
        if (!metPara) {
            StringBuilder stringBuffer = new StringBuilder("\n");
            stringBuffer.append("测点状态采集点信息:\n");
            stringBuffer.append("=======================================================================================\n");
            for (int i = 0; i < dbNameList.size(); i++) {
                stringBuffer.append("数据库:" + dbNameList.get(i) + "    测点状态id:" + itemIdList.get(i) + "\n");
            }
            stringBuffer.append("=======================================================================================\n");
            logger.info(stringBuffer.toString());
            metPara = true;
        }
        if (mpsJsonMap.size() > 0) {
            return mpsJsonMap;
        } else {
            return null;
        }
    }

    /**
     * @function: 构造DHV_MET_URL的请求地址
     * @author jianghc
     * @date 2022/5/24 16:12
     */
    private void Construct_DHV_MET_URL() {
        if (itemIdList == null || itemIdList.size() == 0) {
            return;
        }
        int itemIdList_len = itemIdList.size();
        DHV_MET_URL = new String[itemIdList_len];
        for (int i = 0; i < itemIdList.size(); i++) {
            String measuringPointNumberOrTechID = itemIdList.get(i);
            if (measuringPointNumberOrTechID.contains("#")) {
                measuringPointNumberOrTechID = measuringPointNumberOrTechID.replace("#", "%23");
            }
            DHV_MET_URL[i] = url + String.format(YFDHV.YF_DHV_METH, dbNameList.get(i), measuringPointNumberOrTechID);
        }
        StringBuilder stringBuffer = new StringBuilder("\n");
        stringBuffer.append("获取测点总值需要的方法请求路径包含:\n");
        stringBuffer.append("=======================================================================================\n");
        for (int i = 0; i < DHV_MET_URL.length; i++) {
            stringBuffer.append(DHV_MET_URL[i] + "\n");
        }
        stringBuffer.append("=======================================================================================\n");
        logger.info(stringBuffer.toString());
    }

    /**
     * @param httpClient
     * @function: 主要要来获取dbNameList，itemIdList，methodNameList
     * @author jianghc
     * @date 2022/5/24 16:12
     */
    private void METRequest(OkHttpClient httpClient) {
        List<String> dbNameList_temp = new ArrayList<>();
        List<String> itemIdList_temp = new ArrayList<>();
        if (DHV_MET_URL == null) {
            return;
        }
        methodNameList = new ArrayList<>();
        int DHV_MET_URL_len = DHV_MET_URL.length;
        for (int i = 0; i < DHV_MET_URL_len; i++) {
            okhttp3.Request request = new okhttp3.Request.Builder()
                    .header("Authorization", Authorization)
                    .header("emptyanddefault", emptyanddefault)
                    .url(DHV_MET_URL[i])
                    .get()
                    .build();
            Response response = null;
            try {
                response = httpClient.newCall(request).execute();
            } catch (IOException ex) {
                throw new BusinessException("接口:" + DHV_MET_URL[i] + "请求失败!");
            }
            if (response != null && response.isSuccessful()) {
                String jsonString = null;
                try {
                    jsonString = new String(response.body().bytes(), StandardCharsets.UTF_8);
                } catch (Exception ex) {
                    logger.error("MPS响应数据解析异常");
                    throw new BusinessException("MPS响应数据解析异常");
                }
                JSONObject jsonObject = (JSONObject) JSONObject.parse(jsonString);
                JSONArray jsonArray = (JSONArray) jsonObject.get("values");
                if (jsonArray != null && jsonArray.size() > 0) {
                    for (int j = 0; j < jsonArray.size(); j++) {
                        JSONObject jsonObject_val = (JSONObject) jsonArray.get(j);
                        String TechName = jsonObject_val.getString("TechName");
                        if (!AllowMethod.allowMethod(TechName)) {
                            logger.info(TechName + "方法不匹配");
                            continue;
                        }
                        dbNameList_temp.add(dbNameList.get(i));
                        itemIdList_temp.add(itemIdList.get(i));
                        methodNameList.add(TechName);
                    }
                }
            }
        }
        dbNameList = null;
        dbNameList = dbNameList_temp;
        itemIdList = null;
        itemIdList = itemIdList_temp;
        StringBuilder stringBuilder = new StringBuilder("\n");
        measuringPointNumberOrTechIDs = new String[dbNameList.size()];
        stringBuilder.append("测点总值需要的参数:\n");
        stringBuilder.append("=======================================================================================\n");
        for (int i = 0; i < dbNameList.size(); i++) {
            measuringPointNumberOrTechIDs[i] = dbNameList.get(i) + YFDHV.YF_Separator + itemIdList.get(i) + YFDHV.YF_Separator + methodNameList.get(i);
            stringBuilder.append("数据库:" + dbNameList.get(i)
                    + "    测点总值id:" + itemIdList.get(i)
                    + "    方法:" + methodNameList.get(i) + "\n");
        }
        stringBuilder.append("=======================================================================================\n");
        logger.info(stringBuilder.toString());
        Construct_DHV_MPV_URL();
    }

    /**
     * @function: 构造DHV_MPV_URL的请求地址
     * @author jianghc
     * @date 2022/5/24 16:14
     */
    private void Construct_DHV_MPV_URL() {
        if (methodNameList == null) {
            return;
        }
        int methodNameList_len = methodNameList.size();
        DHV_MPV_URL = new String[methodNameList_len];
        for (int i = 0; i < methodNameList_len; i++) {
            String measuringPointNumberOrTechID = itemIdList.get(i);
            if (measuringPointNumberOrTechID.contains("#")) {
                measuringPointNumberOrTechID = measuringPointNumberOrTechID.replace("#", "%23");
            }
            DHV_MPV_URL[i] = url + String.format(YFDHV.YF_DHV_MPV, dbNameList.get(i), measuringPointNumberOrTechID, methodNameList.get(i));
            DHV_MPV_URL[i] += "?ShortMem=true&LongMem=true&Expand=result";
        }
        StringBuilder stringBuilder = new StringBuilder("\n");
        stringBuilder.append("测点总值请求路径（该请求路径不含时间参数 &StartDateTime &StopDateTime ）:\n");
        stringBuilder.append("=======================================================================================\n");
        for (int i = 0; i < DHV_MPV_URL.length; i++) {
            stringBuilder.append(DHV_MPV_URL[i] + "\n");
        }
        stringBuilder.append("=======================================================================================\n");
        logger.info(stringBuilder.toString());
    }

    /**
     * @param httpClient
     * @param start
     * @param end
     * @return Map<String, JSONArray>
     * @function: 终极奥义之通过DHV_MPV_URL信息获取测点总值信息
     * @author jianghc
     * @date 2022/5/24 16:14
     */
    private Map<String, JSONObject> MPVRequest(OkHttpClient httpClient, LocalDateTime start, LocalDateTime end) {
        String startTime = start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String endTime = end.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        if (DHV_MPV_URL == null) {
            return null;
        }
        int DHV_MPV_URL_Len = DHV_MPV_URL.length;

        Map<String, JSONObject> jsonArrayResObj = new HashMap<>();
        JSONObject resJson = null;
        for (int i = 0; i < DHV_MPV_URL_Len; i++) {
            String msgKey = dbNameList.get(i) + YFDHV.YF_Separator + itemIdList.get(i) + YFDHV.YF_Separator + YFDHV.YF_MPV;
            resJson = jsonArrayResObj.get(msgKey);
            if (resJson == null) {
                resJson = new JSONObject();
            }
            okhttp3.Request request = new okhttp3.Request.Builder()
                    .header("Authorization", Authorization)
                    .header("emptyanddefault", emptyanddefault)
                    //.url(DHV_MPV_URL[i] + "&StartDateTime=2020-01-01 00:00:01" + "&StopDateTime=" + endTime)
                    .url(DHV_MPV_URL[i] + "&StartDateTime=" + startTime + "&StopDateTime=" + endTime)
                    .get()
                    .build();
            Response response = null;
            try {
                Long currentTime_start = System.currentTimeMillis();
                Long currentTime_end = 0l;
                response = httpClient.newCall(request).execute();
                currentTime_end = System.currentTimeMillis();
                if (currentTime_end - currentTime_start > timeOut) {
                    logger.error("地址:" + DHV_MPV_URL[i] + "请求超时！");
                    throw new BusinessException("地址:" + DHV_MPV_URL[i] + "请求超时！");
                }
            } catch (Exception e) {
                logger.error("地址:" + DHV_MPV_URL[i] + "请求超时！" + e.getMessage());
                throw new BusinessException("地址:" + DHV_MPV_URL[i] + "请求超时！");
            }
            MPVItem[] mpvItems = MPVItem.values();
            if (response.isSuccessful()) {
                MPVItem primary = MPVItem.getPrimary();
                String primary_value = measuringPointNumberOrTechIDs[i];
                String jsonString = null;
                try {
                    jsonString = new String(response.body().bytes(), StandardCharsets.UTF_8);
                } catch (Exception ex) {
                    logger.error("MPV响应数据解析异常");
                    throw new BusinessException("MPV响应数据解析异常");
                }
                Object jsonObject_temp = JSONObject.parse(jsonString);
                if (jsonObject_temp instanceof JSONObject) {
                    JSONObject jsonObject = (JSONObject) jsonObject_temp;
                    Object jsonArrayObject = jsonObject.get("values");
                    if (jsonArrayObject != null && jsonArrayObject instanceof JSONArray) {
                        JSONArray jsonArray = (JSONArray) jsonArrayObject;

                        for (int j = 0; j < jsonArray.size(); j++) {
                            Object obj = jsonArray.get(j);
                            if (j >= 2) {
                                logger.error("拉取数据过多，请缩短采集时间间隔!");
                                logger.error("多余数据将不被采集:" + ((JSONObject) obj).toJSONString());
                                continue;
                            }
                            if (obj != null && obj instanceof JSONObject) {
                                JSONObject jsonObject_value = (JSONObject) obj;
                                Object obj_Result = jsonObject_value.get("Result");
                                if (obj_Result != null && obj_Result instanceof JSONObject) {
                                    JSONObject jsonObject_Result = (JSONObject) obj_Result;

                                    //primary_value=数据库.测点.方法
                                    //key-value
                                    //key:数据库.测点.方法.属性
                                    //value:测点
                                    resJson.fluentPut(primary_value + YFDHV.YF_Separator + primary.getMpvItem(),
                                            primary_value.split(YFDHV.YF_EscapeCharacter + YFDHV.YF_Separator)[1]);


                                    Object RPM = jsonObject_Result.get("RPM");
                                    if (RPM != null && RPM instanceof JSONObject) {
                                        JSONObject rpmObj = (JSONObject) RPM;
                                        MPVRPMItem[] mpvrpmItems = MPVRPMItem.values();
                                        for (MPVRPMItem mpvrpmItem : mpvrpmItems) {
                                            String key = primary_value + YFDHV.YF_Separator + mpvrpmItem.getMpvRPMItem();
                                            String value = rpmObj.getString(mpvrpmItem.getMpvRPMItem());
                                            //key-value
                                            //key:数据库.测点.方法.属性
                                            //value:属性值
                                            resJson.fluentPut(key, value);
                                        }
                                    }

                                    //String DateTime_key = primary_value + YFDHV.YF_Separator + MPVItem.DateTime.getMpvItem();
                                    //String DateTime_value = jsonObject_Result.get(MPVItem.DateTime.toString()).toString();
                                    //key-value
                                    //key:数据库.测点.方法.属性
                                    //value:属性值
                                    //resJson.fluentPut(DateTime_key, DateTime_value);

                                    Object obj_Conditions = jsonObject_Result.get("Conditions");
                                    if (obj_Conditions != null && obj_Conditions instanceof JSONObject) {
                                        JSONObject jsonObject_Conditions = (JSONObject) obj_Conditions;

                                        for (MPVItem mpvItem : mpvItems) {
                                            String key = primary_value + YFDHV.YF_Separator + mpvItem.getMpvItem();
                                            Object value = null;
                                            try {
                                                value = jsonObject_Conditions.get(mpvItem.toString());
                                            } catch (Exception ex) {
                                                logger.error("永峰设备健康振动数据Http网关response解析异常");
                                                throw new BusinessException("永峰设备健康振动数据Http网关response解析异常");
                                            }
                                            if (value != null) {
                                                //key-value
                                                //key:数据库.测点.方法.属性
                                                //value:属性值
                                                resJson.fluentPut(key, value);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (resJson != null && resJson.size() > 0) {
                jsonArrayResObj.put(msgKey, resJson);
            }
        }
        if (jsonArrayResObj.size() > 0) {
            return jsonArrayResObj;
        } else {
            return null;
        }
    }

    /**
     * @param body
     * @param msgKey
     * @function: 将数据发送到平台
     * @author jianghc
     * @date 2022/5/24 16:15
     */
    private void sendToNextMQ(Object body, String msgKey) {
        ServiceProvider serviceProvider = startVo.getServiceProvider();
        PulledHttpGatewayBase gatewayBase = startVo.getGatewayBase();
        if (serviceProvider != null && gatewayBase != null && body instanceof String) {
            SendService service = (SendService) serviceProvider.getByName(ServiceName.Send);
            Message message = null;
            HttpMessage httpMessage = gatewayBase.buildHttpMessage();
            httpMessage.setDeviceId(startVo.getDeviceId());
            httpMessage.setProviderCode(null);
            httpMessage.setBody((String) body);
            httpMessage.setMsgKey(msgKey);
            message = httpMessage;
            service.sendMessage(message);
        } else if (serviceProvider != null && gatewayBase != null && body instanceof JSONArray) {
            SendService service = (SendService) serviceProvider.getByName(ServiceName.Send);
            Message message;
            JSONArray sendBody = (JSONArray) body;
            List<Message> httpMessages = new ArrayList(sendBody.size());
            for (Object obj : sendBody) {
                HttpMessage httpMessage = gatewayBase.buildHttpMessage();
                httpMessage.setDeviceId(startVo.getDeviceId());
                httpMessage.setProviderCode(null);
                httpMessage.setBody(obj.toString());
                httpMessage.setMsgKey(msgKey);
                httpMessages.add(httpMessage);
            }
            MessageBatch<Message> batch = new MessageBatch<>(httpMessages);
            message = batch;
            service.sendMessage(message);
        } else {
            logger.info("连接id-{} 拉取采集到值-{}", gatewayBase.getInstanceVo().getRunId(), body);
        }
    }
}
