package cn.cecep.talroad.data.analyse.task.missing;


import cn.cecep.talroad.common.core.utils.DateUtils;
import cn.cecep.talroad.common.core.utils.StringUtils;
import cn.cecep.talroad.data.analyse.task.anomaly.analysis.DataAnomalyAbstractService;
import cn.cecep.talroad.data.analyse.task.entity.PreAmProblemFi;
import cn.cecep.talroad.domain.AmProblemFi;
import cn.cecep.talroad.domain.BasDevice;
import cn.cecep.talroad.domain.BasThresholdConfig;
import cn.cecep.talroad.domain.analysis.BasDeviceParamAnalyse;
import cn.cecep.talroad.enums.ProblemTypeEnum;
import cn.cecep.talroad.mapper.BasThresholdConfigMapper;
import cn.cecep.talroad.vo.BasDeviceParamListVo;
import cn.cecep.talroad.vo.FactoryDeviceVo;
import cn.hutool.core.collection.CollectionUtil;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 数据缺失抽象处理类
 */
@Slf4j
public abstract class DataMissingAbstractService<T> extends DataAnomalyAbstractService<T> {


    /**
     * 数据异常判断配置 code 正常来说 如果不变的情况下 走默认 异常来不及修改情况可从配置获取
     */
    @Value("${data.logic.code:S01}")
    private String earlyWarningParamCode;

    /**
     * 数据异常判断阈值 默认30 正常来说 如果不变的情况下 走默认 异常来不及修改情况可从配置获取
     */
    @Value("${data.logic.default.threshold:30}")
    private Integer defaultThreshold;

    @Autowired
    private BasThresholdConfigMapper basThresholdConfigMapper;

    @Override
    protected List<AmProblemFi> verify(String deviceId, List<T> deviceRealData, List<BasDeviceParamListVo> deviceBindOnlineParams) {
        Map<String, BasDeviceParamListVo> paramsMap = deviceBindOnlineParams.stream().collect(Collectors.toMap(BasDeviceParamListVo::getParamId, Function.identity()));
        Map<String, List<Object>> paramAllValue = getColumnParamValue(deviceRealData, new ArrayList<>(paramsMap.keySet()));
        Map<String,Integer> thresholdMap = new HashMap<>(paramsMap.size());
        List<PreAmProblemFi> preAmProblemFis = new ArrayList<>();
        Map<String,String> pMap = new HashMap<>();
        int configThreshold = getConfigThreshold();
        for (Map.Entry<String, List<Object>> entry : paramAllValue.entrySet()) {
            BasDeviceParamListVo deviceParam = paramsMap.get(entry.getKey());
            if (deviceParam == null){
                continue;
            }
            List<Object> values = entry.getValue();
            String startTime = null;
            String endTime = null;
            //values 一定是有序的 那么如果其中某一个数据异常 则可以根据下标
            for (int i = 0; i < values.size(); i++) {
                //数据缺失
                if (values.get(i) == null || StringUtils.isEmpty(values.get(i).toString())){
                    //记录预警日志 如果是第一次发生异常 应该记录到该参数异常开始时间
                    Integer threshold = thresholdMap.get(entry.getKey());
                    if (threshold == null){
                        //初始化 开始时间 和 异常次数
                        startTime = getLogicExceptionsDate(deviceRealData.get(i));
                        threshold = 0;
                    }
                    //此处判断 连续异常次数 是否 >= 阈值所配置
                    if (++threshold >= configThreshold){
                        //预先存储 告警日志 信息
                        endTime = getLogicExceptionsDate(deviceRealData.get(i));
                    }
                    thresholdMap.put(entry.getKey(),threshold);
                }else{
                    int count = thresholdMap.get(entry.getKey()) == null ? 0 : thresholdMap.get(entry.getKey());
                    if (count < configThreshold){
                        //如果未达到阈值 且 不是异常数据 那么需要清除之前的记录 保证连续判断
                        cleanThreshold(thresholdMap,entry.getKey());
                        startTime = null;
                        endTime = null;
                        //判断 剩余数据 是否满足 连续的阈值判断 不满足 跳出 执行下个参数
                        if (values.size() - configThreshold > values.size() - i){
                            break;
                        }
                    }
                }
            }
            //如果有异常开始时间 和 结束时间 就应该预先添加对应日志信息了
            if (startTime != null && endTime != null){
                preAmProblemFis.add(new PreAmProblemFi(deviceId,entry.getKey(),startTime,endTime));
            }
        }
        //无异常情况 直接退出
        if (CollectionUtil.isEmpty(preAmProblemFis)){
            return new ArrayList<>();
        }
        //获取 预警设备 和参数的名称 用来组装预警信息
        List<String> deviceIds = preAmProblemFis.stream().map(PreAmProblemFi::getDeviceId).distinct().collect(Collectors.toList());
        List<String> paramIds = preAmProblemFis.stream().map(PreAmProblemFi::getParamId).distinct().collect(Collectors.toList());
        Map<String, BasDevice> deviceMap = queryDeviceByIds(deviceIds);
        Map<String, String> paramsNameByIds = queryParamsNameByIds(paramIds);
        Map<String, BasDeviceParamAnalyse> paramAnalyseMap = queryParamsAnalyseByIds(paramIds);
        return convertPreAmProblemFiToAmProblemFi(preAmProblemFis,deviceMap,paramsNameByIds,paramAnalyseMap,pMap,deviceId);
    }

    public List<AmProblemFi> convertPreAmProblemFiToAmProblemFi(List<PreAmProblemFi> preAmProblemFis,
                                                                Map<String, BasDevice> deviceMap,
                                                                Map<String, String> paramsNameByIds,
                                                                Map<String, BasDeviceParamAnalyse> paramAnalyseMap,
                                                                Map<String,String> pMap,
                                                                String deviceId){
        List<AmProblemFi> result = new ArrayList<>();
        for (PreAmProblemFi preAmProblemFi : preAmProblemFis) {
            BasDeviceParamAnalyse basDeviceParamAnalyse = paramAnalyseMap.get(preAmProblemFi.getParamId());
            String dataType = "1";
            if (basDeviceParamAnalyse != null){
                dataType = basDeviceParamAnalyse.getDataType();
            }
            //生成告警信息
            FactoryDeviceVo factoryDeviceVo = new FactoryDeviceVo();
            BasDevice basDevice = deviceMap.get(deviceId);
            if (basDevice == null){
                log.error("设备信息不存在！deviceId:{}",deviceId);
                continue;
            }
            factoryDeviceVo.setDeviceName(basDevice.getName());
            factoryDeviceVo.setDeviceId(deviceId);
            factoryDeviceVo.setFactoryId(basDevice.getFactoryId());
            String stDate = "";
            String endDate = "";
            if(DateUtils.parseDate(preAmProblemFi.getStartTime()).compareTo(DateUtils.parseDate(preAmProblemFi.getEndTime()))<0){
                stDate = preAmProblemFi.getEndTime();
                endDate = preAmProblemFi.getStartTime();
            }else{
                stDate = preAmProblemFi.getStartTime();
                endDate = preAmProblemFi.getEndTime();
            }

            //构建报警信息
            String msg = buildMsg(basDevice.getName(),paramsNameByIds.get(preAmProblemFi.getParamId()),
                stDate,endDate);

            //构建报警实体
            AmProblemFi amProblemFi = doProcessAlaram(factoryDeviceVo,dataType,
                DateUtils.parseDate(stDate),
                DateUtils.parseDate(endDate),
                    ProblemTypeEnum.SJQS, msg,
                    paramsNameByIds.get(preAmProblemFi.getParamId()), preAmProblemFi.getParamId());

            if(amProblemFi!=null){
                String key = deviceId+preAmProblemFi.getParamId();
                String value = pMap.get(key);
                if(StringUtils.isEmpty(value)){
                    pMap.put(key,deviceId);
                    result.add(amProblemFi);
                }
            }
        }
        return result;
    }

    /**
     * 构建报警信息
     */
    private String buildMsg(String deviceName,String paramName,String start,String end){
        return  deviceName + "-" +
                paramName + "-于报警开始时间(" +
                start + ")至报警结束时间(" +
                end + "出现数据连续缺失情况";
    }

    /**
     * 构建报警信息实体
     */
    private AmProblemFi doProcessAlaram(FactoryDeviceVo vo, String problemSources,
                                        Date start, Date end, ProblemTypeEnum problemType, String msg, String param , String paramProperty) {
        AmProblemFi problemFi = new AmProblemFi();
        problemFi.setId(IdUtil.simpleUUID());
        problemFi.setProblemSources(problemSources);
        problemFi.setFactoryId(vo.getFactoryId());
        problemFi.setProblemDetails(msg);
        problemFi.setType(1);
        problemFi.setProblemType(problemType.getCode());
        problemFi.setProblemTreatmentState("1");
        problemFi.setAnalyseTime(start);
        problemFi.setAnalyseTimeEnd(end);
        problemFi.setStationId(vo.getDeviceId());
        problemFi.setStationName(vo.getDeviceName());
        problemFi.setCreateTime(new Date());
        problemFi.setParamName(param);
        problemFi.setParamProperty(paramProperty);
        Map<String,Object> map = new HashMap<>();
        List<Map<String,Object>> list = new ArrayList<>();
        Map m = new HashMap();
        m.put("deviceId",vo.getDeviceId());
        m.put("params", Arrays.asList(paramProperty.split(",")));
        list.add(m);
        map.put("dcsDeviceList",list);
        problemFi.setExtra(JSON.toJSONString(map));
        return problemFi;
    }

    /**
     * 获取 数据逻辑异常分析 阈值
     * @return 阈值 默认30
     */
    public int getConfigThreshold(){
        BasThresholdConfig thresholdConfig = basThresholdConfigMapper.selectByParamCode(earlyWarningParamCode);
        if (thresholdConfig == null || thresholdConfig.getThresholdValue() == null){
            return defaultThreshold;
        }
        return thresholdConfig.getThresholdValue().intValue();
    }

    /**
     * 清除参数异常次数
     * @param thresholdMap 参数异常map
     * @param key 参数名称
     */
    private void cleanThreshold(Map<String, Integer> thresholdMap, String key) {
        Integer threshold = thresholdMap.get(key);
        if (threshold == null) {
            threshold = 0;
        } else {
            ++threshold;
        }
        thresholdMap.put(key, threshold);
    }

    /**
     * 获取逻辑异常时间
     */
    protected abstract String getLogicExceptionsDate(T entity);

    /**
     * 获取查询实时表开始时间
     */
    protected Date getQueryDataStartDateTime(){
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, (calendar.get(Calendar.HOUR_OF_DAY) - 1));
        return calendar.getTime();
    }

    /**
     * 获取查询实时表结束时间
     */
    protected Date getQueryDataEndDateTime(){
        return Calendar.getInstance().getTime();
    }
    @Override
    protected boolean filterDeviceParamTaskSwitch(BasDeviceParamAnalyse analyse){
        return "1".equals(analyse.getDataLogicDeficiencyEw());
    }

    /**
     * 获取所有设备ids
     * 需要自行实现
     */
    protected abstract List<String> queryDeviceIds();

    /**
     * 获取实时数据
     *
     * @return
     */
    protected abstract List<T> queryReal(List<String> deviceIds, Date startTime, Date endTime);

}
