package com.imdroid.sideslope.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.imdroid.common.util.NumberUtils;
import com.imdroid.common.util.WarningLogExecutor;
import com.imdroid.secapi.dto.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class WarningServiceImpl implements WarningService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    WarningCfgMapper warningCfgMapper;
    @Autowired
    WarningMsgMapper warningMsgMapper;
    @Autowired
    GnssStatusMapper gnssStatusMapper;
    @Autowired
    GnssDeviceMapper deviceMapper;
    @Value("${warning.log.directory}")
    private String logDirectory;

    // warning type <-> level & value
    Map<Integer, int[]> cfgMap = new ConcurrentHashMap<>();
    int warningLevel2Code = 0;
    @Value("${abnormal.check_list}")
    private String checkList;
    @PostConstruct
    @Override
    public void refreshCfg(){
        QueryWrapper<WarningCfg> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("devicetype", WarningCfg.TYPE_GNSS);
        List<WarningCfg> cfgList = warningCfgMapper.selectList(null);
        cfgMap.clear();
        for(WarningCfg cfg: cfgList){
            cfgMap.put(cfg.getType(),new int[]{cfg.getLevel(),cfg.getValue()});
            if(cfg.getLevel() == WarningCfg.LEVEL_2){
                warningLevel2Code |= cfg.getType();
            }
        }
    }
    /***
     * 检查一个周期内的b562数量是否足够
     */
    @Override
    public void checkB562Num(Device device){
        GnssStatus status = gnssStatusMapper.getByDeviceId(device.getDeviceId());
        if(status == null) return;

        boolean isUpdated = false;
        if ((status.getWarningcode() & WarningCfg.TYPE_LESS_D3XX) != 0) {
            clearWarning(status, WarningCfg.TYPE_LESS_D3XX);
            isUpdated = true;
        }

        // 无固定解少
        if(check(status, WarningCfg.TYPE_LESS_B562,
                WarningCfg.TYPE_NAME_LESS_B562,true,
                device.getFixedNum(),null,
                "固定解:"+device.getFixedNum()+",浮点解:"+device.getFloatNum()+",全部:"+device.getD341Count())){
            isUpdated=true;
        }

        //连续无固定解
        if ((status.getWarningcode() & WarningCfg.TYPE_NO_FIXED_RESULT) != 0) {
            if(0 != device.getFixedNum()) {
                clearWarning(status, WarningCfg.TYPE_NO_FIXED_RESULT);
                isUpdated = true;
            }
        }
        else{
            if(check(status, WarningCfg.TYPE_NO_FIXED_RESULT,
                    WarningCfg.TYPE_NAME_NO_FIXED_RESULT,false,
                    (int) device.getNoFixedAndFloatResult(),null,
                    "连续无固定解:"+device.getNoFixedAndFloatResult())){
                isUpdated=true;
            }
        }

        //连续无有效解
        if ((status.getWarningcode() & WarningCfg.TYPE_CONT_INVALID_RESULT) != 0) {
            int[] warningValues = cfgMap.get(WarningCfg.TYPE_CONT_INVALID_RESULT);
            if(warningValues!=null && device.getLastValidCalcDataTime()!=null){
                if(LocalDateTime.now().isBefore(device.getLastValidCalcDataTime().plusHours(warningValues[1]))){
                    clearWarning(status, WarningCfg.TYPE_CONT_INVALID_RESULT);
                    isUpdated = true;
                }
            }
        }
        else{
            if(device.getLastValidCalcDataTime()!=null &&
                    check(status, WarningCfg.TYPE_CONT_INVALID_RESULT,
                    WarningCfg.TYPE_NAME_CONT_INVALID_RESULT,false,
                    (int) Duration.between(device.getLastValidCalcDataTime(), LocalDateTime.now()).toHours(),null,
                    "长时间无有效解:"+device.getLastValidCalcDataTime())){
                isUpdated=true;
            }
        }

        if(isUpdated){
            // 根据告警码确定告警级别
            status.setWarning(getWarningLevel(status.getWarningcode()));
            gnssStatusMapper.updateById(status);
            device.setWarningcode(status.getWarningcode());
        }
    }
    /***
     * 检查电压、RSSI等
     */
    @Override
    public void checkDeviceStatus(GnssStatusMsg statusMsg, GnssStatus curStatus){
        // 清除离线告警
        if(curStatus.getWarningcode() == null) curStatus.setWarningcode(0);
        boolean isUpdated = false;

        if ((curStatus.getWarningcode() & WarningCfg.TYPE_DEVICE_OFF_LINE) != 0) {
            clearWarning(curStatus, WarningCfg.TYPE_DEVICE_OFF_LINE);
            isUpdated = true;
        }

        if(statusMsg!=null) {
            //低电压告警
            if (statusMsg.getVoltage() != null) {
                if (check(curStatus, WarningCfg.TYPE_LOW_VOLTAGE,
                        WarningCfg.TYPE_NAME_LOW_VOLTAGE, true,
                        statusMsg.getVoltage(), null, null)) isUpdated = true;
            }
            //低RSSI
            if (statusMsg.getRssi() != null) {
                if (check(curStatus, WarningCfg.TYPE_LOW_RSSI,
                        WarningCfg.TYPE_NAME_LOW_RSSI, true,
                        statusMsg.getRssi().intValue(), null, null)) isUpdated = true;
            }
            //倾角异常告警
            if (statusMsg.getPitch() != null) {
                if (check(curStatus, WarningCfg.TYPE_INCLINE,
                        WarningCfg.TYPE_NAME_INCLINE, false,
                        Math.abs(statusMsg.getPitch().intValue()),
                        Math.abs(statusMsg.getRoll().intValue()),
                        "pitch:" + NumberUtils.scaleTwo(statusMsg.getPitch().doubleValue())
                                + ",roll:" + NumberUtils.scaleTwo(statusMsg.getRoll().doubleValue()))) isUpdated = true;
            }
        }

        //卫星数
        if(curStatus.getSatelliteinuse()!=null){
            if(check(curStatus,WarningCfg.TYPE_LESS_INUSE_SAT,
                    WarningCfg.TYPE_NAME_LESS_INUSE_SAT,true,
                    curStatus.getSatelliteinuse(),null,null)) isUpdated=true;
        }

        // 根据告警码确定告警级别
        if(isUpdated){
            curStatus.setWarning(getWarningLevel(curStatus.getWarningcode()));
        }

    }

    boolean check(GnssStatus curStatus, int warningType, String warningName,
                  boolean isLessCmp, Integer value1, Integer value2, String auxInfo){
        boolean isUpdated=false;
        int newWarningCode = 0;
        int[] warningValues = cfgMap.get(warningType);
        if(warningValues!=null){
            if(isLessCmp) {
                if (value1 != null && value1 <= warningValues[1]) {
                    newWarningCode = warningType;
                }
                if (value2 != null && value2 <= warningValues[1]) {
                    newWarningCode = warningType;
                }
            }
            else{
                if (value1 != null && value1 >= warningValues[1]) {
                    newWarningCode = warningType;
                }
                if (value2 != null && value2 >= warningValues[1]) {
                    newWarningCode = warningType;
                }
            }
        }

        int oldWarningCode = curStatus.getWarningcode() & warningType;
        if(newWarningCode != oldWarningCode){
            if(newWarningCode == 0){
                clearWarning(curStatus, warningType);
            }
            else {
                WarningMsg warningMsg = new WarningMsg();
                warningMsg.setDeviceid(curStatus.getDeviceid());
                warningMsg.setTenantid(curStatus.getTenantid());
                warningMsg.setCreatetime(LocalDateTime.now());
                warningMsg.setDevicetype(WarningCfg.TYPE_GNSS);
                warningMsg.setCode(warningType);
                warningMsg.setLevel((short) warningValues[0]);
                if(auxInfo ==null) {
                    auxInfo = warningName + ":" + value1;
                    if (value2 != null) auxInfo = auxInfo + "," + value2;
                }
                else auxInfo = warningName + ":" + auxInfo;
                warningMsg.setInfo(auxInfo);
                warningMsgMapper.insert(warningMsg);
                //告警级别
                curStatus.setWarningcode(curStatus.getWarningcode() | warningType);

                // 新告警出现后，生成对应设备的 warning 日志文件
                //generate_warning_logs(curStatus.getDeviceid(),warningType,auxInfo);
            }
            isUpdated = true;
        }
        return isUpdated;
    }

    public short getWarningLevel(int warningCode){
        if(warningCode == 0) return WarningCfg.LEVEL_0;
        else if((warningCode & warningLevel2Code) !=0 ) return WarningCfg.LEVEL_2;
        else return WarningCfg.LEVEL_1;
    }

    @Override
    public void checkD3xxNum(Device device){
        GnssStatus status = gnssStatusMapper.getByDeviceId(device.getDeviceId());
        if(status != null) {
            boolean isUpdated = false;
            // 清除b562告警
            if((status.getWarningcode() & WarningCfg.TYPE_CONT_INVALID_RESULT) !=0 ) {
                clearWarning(status, WarningCfg.TYPE_CONT_INVALID_RESULT);
                isUpdated = true;
            }
            if((status.getWarningcode() & WarningCfg.TYPE_NO_FIXED_RESULT) !=0 ) {
                clearWarning(status, WarningCfg.TYPE_NO_FIXED_RESULT);
                isUpdated = true;
            }
            if((status.getWarningcode() & WarningCfg.TYPE_LESS_B562) !=0 ) {
                clearWarning(status, WarningCfg.TYPE_LESS_B562);
                isUpdated = true;
            }
            // 检查d331告警
            if(check(status, WarningCfg.TYPE_LESS_D3XX,
                    WarningCfg.TYPE_NAME_LESS_D3XX,true,
                    device.getD3xxCount(), null,null)) isUpdated=true;

            if(isUpdated) {
                status.setWarning(getWarningLevel(status.getWarningcode()));
                gnssStatusMapper.updateById(status);
                device.setWarningcode(status.getWarningcode());
            }
        }
    }

    public void generate_warning_logs(String device_id,int warning_type,String warning_type_name){
        // 连续无固定解 和 掉电 警告
        if (warning_type == WarningCfg.TYPE_NO_FIXED_RESULT || warning_type == WarningCfg.TYPE_LOW_VOLTAGE) {
            WarningLogExecutor warningLogExecutor = new WarningLogExecutor(logDirectory);
            warningLogExecutor.generateWarningLogs(device_id, warning_type_name);
        }

    }

    void clearWarning(GnssStatus status, int warningCode){
        status.setWarningcode(status.getWarningcode()&~warningCode);
        logger.info("{} clearWarning code {},{}",status.getDeviceid(),warningCode,status.getWarningcode());
        QueryWrapper<WarningMsg> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("deviceid",status.getDeviceid());
        queryWrapper.eq("code",warningCode);
        queryWrapper.orderByDesc("createtime");
        queryWrapper.last("limit 1");
        WarningMsg warningMsg = warningMsgMapper.selectOne(queryWrapper);
        if(warningMsg!=null){
            warningMsg.setCleartime(LocalDateTime.now());
            warningMsgMapper.updateById(warningMsg);
        }
    }

    @Override
    public void checkFilteredResultJump(double[] latestRpos, GnssCalcData locationRecord){
        if(StringUtils.hasText(checkList)){
            if(checkList.contains(locationRecord.getDeviceid())) return;
        }
        int[] warningValuesXY = cfgMap.get(WarningCfg.TYPE_XY_JUMP);
        int[] warningValuesZ = cfgMap.get(WarningCfg.TYPE_Z_JUMP);
        int warningCode = 0;
        if(warningValuesXY!=null){
            if(Math.abs(locationRecord.getRpose()-latestRpos[0])>warningValuesXY[1] ||
                    Math.abs(locationRecord.getRposn()-latestRpos[1])>warningValuesXY[1]){
                warningCode = WarningCfg.TYPE_XY_JUMP;
            }
        }
        if(warningCode==0 && warningValuesZ!=null){
            if(Math.abs(locationRecord.getRposd()-latestRpos[2])>warningValuesZ[1]){
                warningCode = WarningCfg.TYPE_Z_JUMP;
            }
        }

        if(warningCode!=0){
            // 停止推送
            QueryWrapper<GnssDevice> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("deviceid",locationRecord.getDeviceid());
            queryWrapper.last("limit 1");
            GnssDevice device = deviceMapper.selectOne(queryWrapper);
            String unFwdGroupName = "不推送";
            if(device != null){
                if(device.getTenantid() != Tenant.SAAS_PROVIDER_ID &&
                        !unFwdGroupName.equals(device.getFwd_group_id())){
                    device.setFwd_group_id(unFwdGroupName);
                    device.setFwd_group_id2(unFwdGroupName);
                    //deviceMapper.updateById(device);
                    // 产生告警
                    WarningMsg warningMsg = new WarningMsg();
                    warningMsg.setDeviceid(device.getDeviceid());
                    warningMsg.setTenantid(device.getTenantid());
                    warningMsg.setCreatetime(LocalDateTime.now());
                    warningMsg.setCode(warningCode);
                    warningMsg.setLevel(WarningCfg.LEVEL_2);
                    double deltaE = locationRecord.getRpose()-latestRpos[0];
                    double deltaN = locationRecord.getRposn()-latestRpos[1];
                    double deltaU = locationRecord.getRposd()-latestRpos[2];
                    warningMsg.setInfo("滤波结果跳变："+deltaE+", "+deltaN+", "+deltaU);
                    warningMsg.setDevicetype(WarningCfg.TYPE_GNSS);
                    warningMsgMapper.insert(warningMsg);
                }
            }
        }
    }
}
