package cn.cecep.talroad.service.impl.analysis;

import cn.cecep.talroad.common.core.utils.DateUtils;
import cn.cecep.talroad.domain.*;
import cn.cecep.talroad.enums.ProblemTypeEnum;
import cn.cecep.talroad.enums.ThresHoldConfigEnum;
import cn.cecep.talroad.service.*;
import cn.cecep.talroad.service.analysis.IDataLogicAnalysisService;
import cn.cecep.talroad.utils.ThrFunction;
import cn.cecep.talroad.vo.BasDeviceParamVo;
import cn.cecep.talroad.vo.FactoryDeviceVo;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.Builder;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class DataLogicAnalysisServiceImpl implements IDataLogicAnalysisService {

    @Autowired
    private IBasDeviceService basDeviceService;
    @Autowired
    private IBasThresholdConfigService basThresholdConfigService;
    @Autowired
    private IAmProblemFiService amProblemFiService;
    @Autowired
    private IBasDeviceParamService basDeviceParamService;
    @Autowired
    private IMonElecRealService monElecRealService;
    @Autowired
    private IBasDeviceSiteService basDeviceSiteService;
    @Autowired
    private IPcOutletService pcOutletService;
    @Autowired
    private IOdsMeshingShStationHourIService meshingShStationHourIService;
    @Autowired
    private IOdsMeshingWeiStationHourIService meshingWeiStationHourIService;

    @Override
    public void analysisElecRealDataLogic() {
        //dataType = 2 表示需要分析 分表计电
        String dataType = "2";
        Function paramFunc = (deviceId) -> {
            //查询所有需要分析 网格化 的设备对应的字段
            List<BasDeviceParamVo> basParamList = getBasDeviceParam(null,dataType,null);
            return basParamList;
        };
        ThrFunction<String,Date,Date,List<MonElecReal>> queryData = (deviceId,lastHourBegin,lastHourEnd) -> {
            //设备对应的点位 因为is_stat = 1 的只有一个
            Optional<BasDeviceSite> deviceSiteOpt = basDeviceSiteService.lambdaQuery()
                    .eq(BasDeviceSite::getDeviceId, deviceId)
                    .eq(BasDeviceSite::getIsStat, "1")
                    .oneOpt();
            if(deviceSiteOpt.isPresent()) {
                //设备点位信息
                BasDeviceSite deviceSite = deviceSiteOpt.get();
                //设备点位的对应当前时间段数据
                List<MonElecReal> elecReals = monElecRealService.lambdaQuery()
                        .eq(MonElecReal::getDeviceSiteId,deviceSite.getId())
                        .ge(MonElecReal::getDataTime, lastHourBegin)
                        .lt(MonElecReal::getDataTime, lastHourEnd)
                        .orderByAsc(MonElecReal::getDataTime)
                        .list();
                return elecReals;
            }
            return new ArrayList<>();
        };

        Function<MonElecReal,Date> dataTime1 = (t) -> t.getDataTime();

        analysisDataLogic(MonElecReal.class,queryData,paramFunc,dataTime1);
    }
    @Override
    public void analysisMeshingDataLogic(){
        //dataType = 5 表示需要分析 网格化
        String dataType = "5";
        Function paramFunc = (deviceId) -> {
            //查询所有需要分析 网格化 的设备对应的字段
            List<BasDeviceParamVo> basParamList = getBasDeviceParam(null,dataType,null);
            return basParamList;
        };
        ThrFunction<String,Date,Date,List<OdsMeshingShStationHourI>> queryData1 = (deviceId,lastHourBegin,lastHourEnd) -> {
            //设备对应的点位
            List<PcOutlet> pcOutletList = pcOutletService.selectByDevice(deviceId);
            //不存在启用的点位
            if(pcOutletList != null&&pcOutletList.size()>0) {
                List<OdsMeshingShStationHourI> elecReals = new ArrayList<>();
                pcOutletList.forEach(pcOutlet -> {
                    //设备点位的对应当前时间段数据
                    List<OdsMeshingShStationHourI> odsList = meshingShStationHourIService.lambdaQuery()
                        .eq(OdsMeshingShStationHourI::getStationId,pcOutlet.getStationId())
                        .ge(OdsMeshingShStationHourI::getReceiveTime, lastHourBegin)
                        .lt(OdsMeshingShStationHourI::getReceiveTime, lastHourEnd)
                        .orderByAsc(OdsMeshingShStationHourI::getReceiveTime)
                        .list();
                    elecReals.addAll(odsList);
                });
                return elecReals;
            }
            return new ArrayList<>();
        };
        ThrFunction<String,Date,Date,List<OdsMeshingWeiStationHourI>> queryData2 = (deviceId,lastHourBegin,lastHourEnd) -> {
            //设备对应的点位
            List<PcOutlet> pcOutletList = pcOutletService.selectByDevice(deviceId);
            //不存在启用的点位
            if(pcOutletList != null&&pcOutletList.size()>0) {
                List<OdsMeshingWeiStationHourI> elecReals = new ArrayList<>();
                pcOutletList.forEach(pcOutlet -> {
                    //设备点位的对应当前时间段数据
                    List<OdsMeshingWeiStationHourI> odsList = meshingWeiStationHourIService.lambdaQuery()
                        .eq(OdsMeshingWeiStationHourI::getStationId,pcOutlet.getStationId())
                        .ge(OdsMeshingWeiStationHourI::getReceiveTime, lastHourBegin)
                        .lt(OdsMeshingWeiStationHourI::getReceiveTime, lastHourEnd)
                        .orderByAsc(OdsMeshingWeiStationHourI::getReceiveTime)
                        .list();
                    elecReals.addAll(odsList);
                });

                return elecReals;
            }
            return new ArrayList<>();
        };

        Function<OdsMeshingShStationHourI,Date> dataTime1 = (t) -> {
            String receiveTime = t.getReceiveTime();
            if(receiveTime != null){
                LocalDateTime localDateTime = DateUtil.parseLocalDateTime(receiveTime);
                Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                return date;
            }
            return null;
        };
        Function<OdsMeshingWeiStationHourI,Date> dataTime2 = (t) -> {
            String receiveTime = t.getReceiveTime();
            if(receiveTime != null){
                LocalDateTime localDateTime = DateUtil.parseLocalDateTime(receiveTime);
                Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
                return date;
            }
            return null;
        };

        analysisDataLogic(OdsMeshingShStationHourI.class,queryData1,paramFunc,dataTime1);
        analysisDataLogic(OdsMeshingWeiStationHourI.class,queryData2,paramFunc,dataTime2);
    }

    /**
     * 查询所有需要分析的设备对应的字段
     * @param deviceId
     * @param dataType
     * @param codeList
     * @return
     */
    private List<BasDeviceParamVo> getBasDeviceParam(String deviceId,String dataType,List<String> codeList){
        //查询所有需要分析 分表计电 的设备对应的字段
        List<BasDeviceParamVo> basParamList = basDeviceParamService.selectBasDeviceParamByAnalyse(deviceId,dataType,codeList);
        return basParamList;
    }

    private <T> void analysisDataLogic(Class<T> clazz,
                                         ThrFunction<String,Date,Date,List<T>> deviceData,
                                         Function<String,List<BasDeviceParamVo>> paramFunc,
                                         Function<T,Date> dateTime
                                         ) {
        Date curr = new Date();
        Date lastHour = DateUtils.addDateHours(curr,-1);
        Date lastHourBegin = DateUtils.parseFormatDate("yyyy-MM-dd HH:00:00",lastHour);
        Date lastHourEnd = DateUtils.parseFormatDate("yyyy-MM-dd HH:59:59",lastHour);
        BasThresholdConfig config = basThresholdConfigService.selectByParamCode(ThresHoldConfigEnum.S01.getCode());
        //配置的数据缺失条数
        int threshold = config.getThresholdValue().intValue();
        Map<String,List<BasDeviceParamVo>> deviceParams = new HashMap<>();
        List<BasDeviceParamVo> basParamList = paramFunc.apply(null);
        SimpleCache<String, Method> getMethodCache = new SimpleCache<>();
        //按 设备id 分组查询结果, 便于按设备分析数据
        for (BasDeviceParamVo basDeviceParamVo : basParamList) {
            //缓存 反射的get方法
            String paramCodeSvc = basDeviceParamVo.getParamCodeSvc();
            Method getMethod = getMethodCache.get(paramCodeSvc,()->{
                String methodName = "get"+StrUtil.upperFirst(paramCodeSvc);
                Method method = ReflectUtil.getMethodByNameIgnoreCase(clazz, methodName);
                return method;
            });
            //如果类不存在 这个字段 ,则不分析,只打印报警信息
            if(getMethod == null){
                continue;
            }
            String deviceId = basDeviceParamVo.getDeviceId();
            List<BasDeviceParamVo> paramList = deviceParams.get(deviceId);
            if(paramList == null){
                paramList = new ArrayList<>();
                deviceParams.put(deviceId,paramList);
            }
            paramList.add(basDeviceParamVo);
        }
        List<Alarm> alarmList = new ArrayList<>();
        //按设备 分析数据
        for (String deviceId : deviceParams.keySet()) {
            //设备对应的参数列表
            List<BasDeviceParamVo> paramList = deviceParams.get(deviceId);
            //设备点位的对应当前时间段数据
            List<T> dataList = deviceData.apply(deviceId, lastHourBegin, lastHourEnd);
            Map<String,Alarm> alarmMap = new HashMap<>();
            for (T data : dataList) {
                for (BasDeviceParamVo basDeviceParamVo : paramList) {
                    if(StringUtils.isEmpty(basDeviceParamVo.getParamName())){
                        continue;
                    }
                    String paramId = basDeviceParamVo.getParamId();
                    String paramCodeSvc = basDeviceParamVo.getParamCodeSvc();
                    String paramName = basDeviceParamVo.getParamName();
                    Method getMethod = getMethodCache.get(paramCodeSvc);
                    if(getMethod == null){
                        continue;
                    }
                    Object fieldValue = ReflectUtil.invoke(data, getMethod);
                    BigDecimal fieldValueB;
                    if(fieldValue == null){
                        fieldValueB = new BigDecimal(0);
                    }else{
                        fieldValueB = new BigDecimal(String.valueOf(fieldValue));
                    }
                    //数据异常
                    Alarm alarm = alarmMap.get(paramId);
                    BigDecimal minValue = basDeviceParamVo.getMinValue();
                    BigDecimal maxValue = basDeviceParamVo.getMaxValue();
                    if(fieldValueB.compareTo(minValue) < 0 || fieldValueB.compareTo(maxValue) > 0 ){
                        if(alarm == null){
                            //TODO ?会否出现数据异常是前几条是超上限,后几条是超下限
                            alarm = Alarm.builder().threshold(threshold).deviceId(deviceId)
                                    .paramId(paramId)
                                    .paramName(paramName)
                                    .paramCodeSvc(paramCodeSvc)
                                    .minValue(String.valueOf(minValue))
                                    .maxValue(String.valueOf(maxValue))
                                    .build();
                            alarmMap.put(paramId,alarm);
                        }
                        alarm.increase().setTime(dateTime.apply(data));
                    }else{ //数据正常
                        //如果有异常信息且符合生成警报条件
                        if(alarm != null && alarm.isAlarm()){
                            alarmList.add(alarm);
                            //清除异常信息
                        }
                        alarmMap.remove(paramId);
                    }
                }
            }
            //剩余未处理的警报信息
            alarmList.addAll(alarmMap.values().stream().filter((item) ->  item != null && item.isAlarm()).collect(Collectors.toList()));
            alarmMap.clear();
        }
        List<AmProblemFi> amProblemFiList = new ArrayList<>();
        for (Alarm alarm : alarmList) {
            FactoryDeviceVo vo = basDeviceService.selectFactoryDeviceVoByDeviceId(alarm.getDeviceId());
            if(vo != null){
                //（设备名称）（参数名称）于“报警开始时间（年月日时分）至报警结束时间（年月日时分）”出现数据连续逻辑异常情况，超出数据估计值范围（下限-上限）。
                String startTimeStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, alarm.getStartTime());
                String endTimeStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, alarm.getEndTime());
                String msg = vo.getDeviceName() + "-" + alarm.paramName
                        + "-于报警开始时间(" + startTimeStr + ")至报警结束时间(" + endTimeStr
                        + ")出现数据连续逻辑异常情况，超出数据估计值范围(" +
                        alarm.minValue + "-" + alarm.maxValue + ")";
                AmProblemFi problemFi = doProcessAlaram(vo, curr, new Date(),
                        ProblemTypeEnum.DATA_LOGIC, msg, alarm.paramName,
                        alarm.paramCodeSvc);
                amProblemFiList.add(problemFi);
            }
        }
        // 批量保存报警
        this.doSaveBatchAlarm(amProblemFiList);
    }

    @Getter
    @Builder
    public static class Alarm{
        private String deviceId;
        private String paramId;
        private String paramName;
        private String paramCodeSvc;
        private int count = 0;
        private Date startTime;
        private Date endTime;
        private String minValue;
        private String maxValue;
        private int threshold;

        public boolean isAlarm(){
            return count >= threshold;
        }
        public Alarm increase(){
            this.count += 1;
            return this;
        }
        public Alarm setTime(Date time){
            if(this.count == 1){
                this.startTime = time;
            }else{
                this.endTime = time;
            }
            return this;
        }
    }

    /**
     * 生成报警数据
     * @param vo
     * @param start
     * @param end
     * @param problemType
     * @param msg
     * @param param
     * @param paramProperty
     * @return
     */
    private AmProblemFi doProcessAlaram(FactoryDeviceVo vo, Date start, Date end, ProblemTypeEnum problemType, String msg,String param ,String paramProperty) {
        AmProblemFi problemFi = new AmProblemFi();
        problemFi.setProblemSources("1");
        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;
    }

    /**
     * 批量保存报警
     * @param list
     */
    private void doSaveBatchAlarm(List<AmProblemFi> list){
        if(list!=null&&list.size()>0) {
            amProblemFiService.saveBatch(list);
        }
    }

}
