package com.imdroid.beidou_ehm.task;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imdroid.secapi.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;

@Component
@Configuration
@EnableScheduling
public class EhmTask{
    @Autowired
    GnssStatusMapper statusMapper;
    @Autowired
    EhmMapper ehmMapper;
    @Autowired
    EhmConfigMapper ehmConfigMapper;
    @Autowired
    WarningMsgMapper warningMsgMapper;
    @Autowired
    GnssCalcDataMapper gnssCalcDataMapper;
    @Autowired
    GnssTrxMsgMapper gnssTrxMsgMapper;
    /**
     * 每半小时转发GNSS解算结果
     */
    @Scheduled(cron = "0 15 7 * * ?") // 每天7点15分钟执行一次
    private void check() {
        // 获取健康检查配置
        List<EhmConfig> ehmConfigList = ehmConfigMapper.selectList(null);
        EhmConfig ehmConfig;
        if(ehmConfigList==null || ehmConfigList.size()==0){
            ehmConfig = new EhmConfig();
            ehmConfig.setUpdatetime(LocalDateTime.now());
            ehmConfig.setCalcstathours(24);
            ehmConfig.setOfflinestatdays((short) 7);
            ehmConfigMapper.insert(ehmConfig);
        }
        else{
            ehmConfig = ehmConfigList.get(0);
        }
        // 获取所有已部署的设备
        List<GnssStatusJoin> deviceList= statusMapper.queryDeployed();
        Iterator<GnssStatusJoin> iterator = deviceList.iterator();
        while(iterator.hasNext()) {
            GnssStatusJoin status=iterator.next();
            Ehm ehm = new Ehm();
            ehm.setDeviceid(status.getDeviceid());
            ehm.setTenantid(status.getTenantid());
            ehm.setCreatetime(LocalDateTime.now());
            ehm.setOfflinestatdays(ehmConfig.getOfflinestatdays());
            ehm.setCalcstathours(ehmConfig.getCalcstathours());

            // 检查过去N天离线次数和时长
            checkOffline(ehm, status.getState()==GnssStatus.STATE_OFFLINE);
            // 检查过去N天无效解总时长
            checkInvalidGnssDataTime(ehm);
            // 计算过去N小时标准差
            // 计算过去N小时有效解比例
            // 计算过去N小时固定解比例
            checkCalcResults(ehm);
            ehmMapper.insert(ehm);
        }
    }

    private void checkOffline(Ehm ehm, boolean isOffline){
        LocalDateTime now = ehm.getCreatetime();
        // 从告警消息里查询离线记录
        QueryWrapper<WarningMsg> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deviceid",ehm.getDeviceid());
        queryWrapper.ge("createtime",now.minusDays(ehm.getOfflinestatdays()));
        queryWrapper.eq("code",WarningCfg.TYPE_DEVICE_OFF_LINE);
        List<WarningMsg> warningMsgList = warningMsgMapper.selectList(queryWrapper);
        if(warningMsgList==null || warningMsgList.size()==0){
            if(isOffline){
                ehm.setOfflinecount(1);
                ehm.setOfflineminutes(ehm.getOfflinestatdays()*24*60);
            }
            else{
                ehm.setOfflinecount(0);
                ehm.setOfflineminutes(0);
            }
        }
        else{
            ehm.setOfflinecount(warningMsgList.size());
            int offlineMinutes = 0;
            LocalDateTime offlineEnd = null;
            LocalDateTime offlineBegin = null;
            for(WarningMsg warningMsg:warningMsgList){
                // 读下一条纪录时才计算上一条的时间差，因为cleartime有可能没有
                if(offlineEnd==null) offlineEnd=warningMsg.getCreatetime();
                if(offlineBegin!=null){
                    Duration duration = Duration.between(offlineBegin,offlineEnd);
                    offlineMinutes+=duration.toMinutes();
                }
                offlineBegin = warningMsg.getCreatetime();
                offlineEnd = warningMsg.getCleartime();
            }
            if(offlineEnd==null) offlineEnd=now;
            if(offlineBegin!=null){
                Duration duration = Duration.between(offlineBegin,offlineEnd);
                offlineMinutes+=duration.toMinutes();
            }
            ehm.setOfflineminutes(offlineMinutes);
        }
    }

    private void checkInvalidGnssDataTime(Ehm ehm){
        LocalDateTime now = ehm.getCreatetime();
        // 从告警消息里查询离线记录
        QueryWrapper<WarningMsg> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deviceid",ehm.getDeviceid());
        queryWrapper.ge("createtime",now.minusDays(ehm.getOfflinestatdays()));
        queryWrapper.eq("code",WarningCfg.TYPE_CONT_INVALID_RESULT);
        List<WarningMsg> warningMsgList = warningMsgMapper.selectList(queryWrapper);
        if(warningMsgList!=null){
            int invalidGnssDataMinutes = 0;
            LocalDateTime offlineEnd = null;
            LocalDateTime offlineBegin = null;
            for(WarningMsg warningMsg:warningMsgList){
                // 读下一条纪录时才计算上一条的时间差，因为cleartime有可能没有
                if(offlineEnd==null) offlineEnd=warningMsg.getCreatetime();
                if(offlineBegin!=null){
                    Duration duration = Duration.between(offlineBegin,offlineEnd);
                    invalidGnssDataMinutes+=duration.toMinutes();
                }
                offlineBegin = warningMsg.getCreatetime();
                offlineEnd = warningMsg.getCleartime();
            }
            if(offlineEnd==null) offlineEnd=now;
            if(offlineBegin!=null){
                Duration duration = Duration.between(offlineBegin,offlineEnd);
                invalidGnssDataMinutes+=duration.toMinutes();
            }
            ehm.setInvalidresultminutes(invalidGnssDataMinutes);
        }
    }

    private void checkCalcResults(Ehm ehm){
        // 获取过去N小时的解算数据
        QueryWrapper<GnssCalcData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deviceid", ehm.getDeviceid());
        queryWrapper.ge("createtime", ehm.getCreatetime().minusHours(ehm.getCalcstathours()));
        List<GnssCalcData> gnssCalcDataList = gnssCalcDataMapper.selectList(queryWrapper);
        // 计算过去N小时标准差，不含有效值
        // 计算过去N小时有效解比例
        double avgE = 0;
        double avgN = 0;
        double avgU = 0;
        double sedevE = 0;
        double sedevN = 0;
        double sedevU = 0;
        int validCount = 0;
        for(GnssCalcData gnssCalcData:gnssCalcDataList){
            if(gnssCalcData.getEnabled()){
                avgE += gnssCalcData.getB562e();
                avgN += gnssCalcData.getB562n();
                avgU += gnssCalcData.getB562d();
                validCount++;
            }
        }
        if(validCount>0){
            avgE = avgE/validCount;
            avgN = avgN/validCount;
            avgU = avgU/validCount;
            for(GnssCalcData gnssCalcData:gnssCalcDataList){
                if(gnssCalcData.getEnabled()){
                    sedevE += Math.pow(gnssCalcData.getB562e()-avgE,2);
                    sedevN += Math.pow(gnssCalcData.getB562n()-avgN,2);
                    sedevU += Math.pow(gnssCalcData.getB562d()-avgU,2);
                }
            }
            ehm.setStdeve((float) Math.sqrt(sedevE/validCount));
            ehm.setStdevn((float) Math.sqrt(sedevN/validCount));
            ehm.setStdevu((float) Math.sqrt(sedevU/validCount));
            ehm.setValidrate((float) (validCount*100/gnssCalcDataList.size()));
        }


        // 计算过去N小时固定解比例
        QueryWrapper<GnssTrxMsg> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("deviceid",ehm.getDeviceid());
        queryWrapper1.ge("createtime", ehm.getCreatetime().minusHours(ehm.getCalcstathours()));
        List<GnssTrxMsg> gnssTrxMsgList = gnssTrxMsgMapper.selectList(queryWrapper1);
        int fixCount = 0;
        int d341Count = 0;
        int jumpCount = 0;
        for(GnssTrxMsg gnssTrxMsg:gnssTrxMsgList){
            if(gnssTrxMsg.getFixnum()!=null) fixCount += gnssTrxMsg.getFixnum();
            if(gnssTrxMsg.getB562count()!=null) d341Count += gnssTrxMsg.getB562count();
            if(gnssTrxMsg.getJumpcount()!=null) jumpCount += gnssTrxMsg.getJumpcount();
        }
        if(d341Count>0) ehm.setFixrate((float) (fixCount*100/d341Count));
        ehm.setJumpcount(jumpCount);
    }
}
