package cn.iocoder.yudao.module.iot.controller.admin.mqttdatarecord;

import cn.iocoder.yudao.module.iot.controller.admin.alertrecord.vo.AlertRecordPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DevicePageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.alert.AlertDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.alertrecord.AlertRecordDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO;
import cn.iocoder.yudao.module.iot.dal.mysql.alert.AlertMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.alertrecord.AlertRecordMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceAttributeMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.formula.FormulaRecordMapper;
import cn.iocoder.yudao.module.iot.framework.constant.Constants;
import cn.iocoder.yudao.module.iot.framework.constant.DeviceStatusEnum;
import cn.iocoder.yudao.module.iot.framework.util.FormulaCalculator;
import cn.iocoder.yudao.module.iot.service.device.DeviceService;
import cn.iocoder.yudao.module.iot.service.mqttrecord.MqttRecordService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Service
@EnableAsync
@EnableScheduling
public class MqttDataService {
    @Resource
    private FormulaRecordMapper formulaRecordMapper;
    @Resource
    private DeviceService deviceService;
    @Resource
    private DeviceMapper deviceMapper;
    @Resource
    private MqttRecordService mqttRecordService;
    @Resource
    private DeviceAttributeMapper attributeMapper;
    @Resource
    private AlertMapper alertMapper;
    @Resource
    private AlertRecordMapper alertRecordMapper;
    /**定时检查设备状态*/
    //@Scheduled(fixedDelay = 2000)
    public void checkDeviceStatus() {
        DevicePageReqVO respVO = new DevicePageReqVO().setStatus(DeviceStatusEnum.在线.getValue()).setIsEnable(true);
        List<DeviceDO> deviceList = deviceService.selectList(respVO);
        for (DeviceDO device : deviceList) {
            if(device.getLastOnlineTime()==null){
                device.setStatus(DeviceStatusEnum.离线.getValue());
                deviceMapper.updateById(device);
            }else {
                Duration duration = Duration.between(device.getLastOnlineTime(), LocalDateTime.now());
                //long seconds = duration.getSeconds();
                long millis = duration.toMillis();
                long second = device.getOffLineDuration()==null? Constants.DEVICE_OFFLINE_SECOND : device.getOffLineDuration();
                if(millis > second*1000){//认为设备已经离线
                    device.setStatus(DeviceStatusEnum.离线.getValue());
                    deviceMapper.updateById(device);
                }
            }
        }
    }
    @Async
    public void updateDeviceStatus(DeviceDO deviceDO){
        deviceDO.setStatus(DeviceStatusEnum.在线.getValue());
        deviceDO.setLastOnlineTime(LocalDateTime.now());
        deviceMapper.updateById(deviceDO);
    }
    @Async
    public void checkDeviceAlert(Map<String,String> value, DeviceDO deviceDO){
        DeviceAttributeDO reqVO = new DeviceAttributeDO().setDeviceId(deviceDO.getId()).setAlertId(1L).setIsEnable(true);
        List<DeviceAttributeDO> attributeList = attributeMapper.selectAlertList(reqVO);
        if(attributeList!=null){
            //拿到该设备关联有告警的属性，进行逻辑计算
            for (DeviceAttributeDO attr : attributeList) {
                if(StringUtils.isBlank(value.get(attr.getAttributeName())))continue;

                AlertDO alertDO = alertMapper.selectById(attr.getAlertId());
                //告警设置有效
                if (alertDO!=null && alertDO.getIsEnable() && !alertDO.getConditionFormula().isEmpty()){
                    //允许重复报警
                    if(alertDO.getIsRepeat() || alertDO.getNoRepeatDuration()==null || alertDO.getNoRepeatDuration() < 1){
                        insertRecord(value,alertDO,attr);
                    }else{//不允许重复报警，检查下次报警间隔，以 告警id和属性id查找上次报警记录
                        AlertRecordPageReqVO pageReqVO = new AlertRecordPageReqVO().setAlertId(alertDO.getId()).setDataId(attr.getId());
                        AlertRecordDO last = alertRecordMapper.selectLastOne(pageReqVO);
                        if(last==null){
                            insertRecord(value,alertDO,attr);
                        }else{
                            Duration duration = Duration.between(last.getCreateTime(), LocalDateTime.now());
                            //long seconds = duration.getSeconds();
                            long millis = duration.toMillis();
                            //可以继续报警了
                            if(millis > alertDO.getNoRepeatDuration()*1000){
                                insertRecord(value,alertDO,attr);
                            }
                        }
                    }
                }
            }
        }
    }

    private void insertRecord(Map<String,String> value, AlertDO alertDO,DeviceAttributeDO attr){
        boolean result = FormulaCalculator.calFormulaBoolean(alertDO.getConditionFormula(),
                Constants.DefaultFormulaVar,value.get(attr.getAttributeName()));
        //触发告警
        if(result){
            AlertRecordDO recordDO = new AlertRecordDO(alertDO);
            recordDO.setData(attr);
            recordDO.setDataValue(value.get(attr.getAttributeName()));
            alertRecordMapper.insert(recordDO);
        }
    }
}
