package com.sckw.slope.detection.service.task;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sckw.core.model.constant.NumberConstant;
import com.sckw.core.model.enums.AlarmLevelEnum;
import com.sckw.core.model.enums.AlarmTitleEnum;
import com.sckw.core.model.enums.AlarmTypeEnum;
import com.sckw.core.model.enums.DeviceEnum;
import com.sckw.core.utils.IdWorker;
import com.sckw.excel.utils.DateUtil;
import com.sckw.log.TraceLog.TraceLog;
import com.sckw.slope.detection.common.config.SmsTemplateConfig;
import com.sckw.slope.detection.dao.mysql.KwsAlarmDetailMapper;
import com.sckw.slope.detection.dao.mysql.KwsAlarmMapper;
import com.sckw.slope.detection.dao.mysql.KwsDeviceMapper;
import com.sckw.slope.detection.dao.mysql.KwsProjectDeviceMapper;
import com.sckw.slope.detection.dao.tdengine.DevicesMapper;
import com.sckw.slope.detection.model.dos.mysql.*;
import com.sckw.slope.detection.model.dos.tdengine.Devices;
import com.sckw.slope.detection.model.vo.ProjectVo;
import com.sckw.slope.detection.service.CommonService;
import com.sckw.slope.detection.service.repository.KwsAlarmInfoRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lfdc
 * @description
 * @date 2023-12-01 15:12:59
 */
@Slf4j
@Component
@EnableScheduling
public class DeviceAlarmTaskService {

    @Value("${scheduled.device.enable}")
    private boolean deviceEnable;

    @Autowired
    KwsAlarmDetailMapper alarmDetailMapper;

    @Autowired
    KwsAlarmMapper alarmMapper;

    @Autowired
    KwsDeviceMapper deviceMapper;

    @Autowired
    DevicesMapper devicesMapper;

    @Autowired
    KwsProjectDeviceMapper projectDeviceMapper;

    @Autowired
    CommonService commonService;

    @Autowired
    SmsTemplateConfig smsTemplateConfig;

    /**
     * 设备告警数据
     */
    @TraceLog(description = "设备告警数据")
    @Scheduled(cron = "0 0 */4 * * ? ")
//    @Scheduled(cron = "* * * * * *")
    public void deviceAlarm() throws IOException {
//        if ("false".equals(deviceEnable)) {
//            return;
//        }
        if (!deviceEnable) {
            return;
        }
        List<KwsDevice> deviceList = deviceMapper.selectList(
                new LambdaQueryWrapper<KwsDevice>()
                        .eq(KwsDevice::getDelFlag, 0)
                        .eq(KwsDevice::getStatus, DeviceEnum.ALREADY_REFERRED_TO.getCode())
        );
        if (!CollectionUtils.isEmpty(deviceList) && deviceList.size() > 0) {
            for (KwsDevice device : deviceList) {
                String snCode = device.getSnCode();
                if (StringUtils.isNotBlank(snCode)) {
                    Devices devices = null;
                    try {
                        devices = devicesMapper.selectLastData(snCode);
                    } catch (Exception e) {
                        log.error("select deviceAlarm tdengine device error :{}", e.getMessage(), e);
//                        continue;
                        long id = insertAlarmAndDetailByDevice(1L, device, device.getId(), AlarmTitleEnum.ALARM_TITLE_ONE.getStatus(), AlarmTypeEnum.ALARM_TWO.getCode());
//                        // todo 暂未设备告警电话，设备告警不推送短信
                        device.setOnline(1);
                        deviceMapper.updateById(device);
                        pushDeviceAlarmSmsMessage(device);
                        pushDeviceAlarmEmailMessage(device.getMountainId(), 1, device, new Date().getTime());
                    }
                    if (devices == null) {
//                        continue;
                        long id = insertAlarmAndDetailByDevice(1L, device, device.getId(), AlarmTitleEnum.ALARM_TITLE_ONE.getStatus(), AlarmTypeEnum.ALARM_TWO.getCode());
//                        // todo 暂未设备告警电话，设备告警不推送短信
                        device.setOnline(1);
                        deviceMapper.updateById(device);
                        pushDeviceAlarmSmsMessage(device);
                        pushDeviceAlarmEmailMessage(device.getMountainId(), 1, device, new Date().getTime());
                    }
                    //判断是否满足设备超时离线报警
                    try {
                        checkDeviceAlarm(snCode, device, devices);
                    } catch (Exception e) {
                        log.error("判断是否满足设备超时离线报警 :{}", e.getMessage(), e);
                    }
                }
            }
        }
    }

    @Autowired
    KwsAlarmInfoRepository alarmInfoRepository;

    /**
     * 设备告警邮件
     *
     * @param mountainId
     * @param level
     * @param device
     * @param ts
     */
    private void pushDeviceAlarmEmailMessage(String mountainId, Integer level, KwsDevice device, Long ts) throws IOException {
        /**邮件推送*/
        List<KwsAlarmInfo> emailList = alarmInfoRepository.list(new LambdaQueryWrapper<KwsAlarmInfo>()
                .eq(KwsAlarmInfo::getMountainId, mountainId)
                .eq(KwsAlarmInfo::getLevel, level.intValue())
                .eq(KwsAlarmInfo::getType, NumberConstant.TWO)
                .eq(KwsAlarmInfo::getDelFlag, NumberConstant.ZERO)
        );
        List<String> emailAddress = new ArrayList<>();
        if (!com.sckw.core.utils.CollectionUtils.isEmpty(emailList)) {
            emailAddress = emailList.stream().map(KwsAlarmInfo::getValueDesc).distinct().collect(Collectors.toList());
        }
        if (!com.sckw.core.utils.CollectionUtils.isEmpty(emailAddress)) {
            Map<String, String> templateParam = new HashMap<>();
            LocalDateTime localDateTime = Instant.ofEpochSecond(ts).atZone(ZoneId.systemDefault()).toLocalDateTime();
            templateParam.put("company", smsTemplateConfig.getDataAlarm().getSignName());
            templateParam.put("level", AlarmLevelEnum.getName(level));
            templateParam.put("name", device.getName());
            templateParam.put("time", cn.hutool.core.date.DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
            ProjectVo vo = projectDeviceMapper.selectProjectByDeviceId(device.getId());
            templateParam.put("project", vo == null ? null : vo.getProjectName());
            templateParam.put("value", cn.hutool.core.date.DateUtil.format(localDateTime, "yyyy-MM-dd HH:mm:ss"));
            templateParam.put("type", "设备离线告警");
            templateParam.put("element", AlarmTitleEnum.ALARM_TITLE_ONE.getName());
//            commonService.pushEmailMessage(emailAddress
//                    , templateParam, "设备告警通知邮件");
            commonService.pushHtmlEmailMessage(emailAddress
                    , templateParam, "设备告警通知邮件");
        }
    }

    private void pushDeviceAlarmSmsMessage(KwsDevice device) {
        //设备离线
        String servicePhones = device.getServicePhones();
        List<String> pushToPhone = new ArrayList<>();
        if (StringUtils.isNotBlank(servicePhones)) {
            pushToPhone = com.sckw.core.utils.StringUtils.splitStrToList(servicePhones, String.class);
        }
        if (!CollectionUtils.isEmpty(pushToPhone) && pushToPhone.size() > 0) {
            Map<String, String> templateParam = new HashMap<>();
            String dateString = DateUtil.getDateString(new Date(), "yyyy-MM-dd HH:mm:ss");
            templateParam.put("time", dateString);
            templateParam.put("level", "一");
            templateParam.put("name", device.getName());
            templateParam.put("value", "");
            commonService.pushSmsMessage(pushToPhone
                    , smsTemplateConfig.getDataAlarm().getSmsCode()
                    , smsTemplateConfig.getDataAlarm().getSignName()
                    , templateParam);
        }
    }

    /**
     * 去除告警历史数据
     */
    @TraceLog(description = "去除告警历史数据")
    @Scheduled(cron = "0 0 0/8 * * ? ")
//    @Scheduled(cron = "* * * * * *")
    public void deleteDeviceAlarm() {
        List<KwsAlarmDetail> details = alarmDetailMapper.selectList(new LambdaQueryWrapper<KwsAlarmDetail>()
                .eq(KwsAlarmDetail::getStatus, NumberConstant.ONE)
                .eq(KwsAlarmDetail::getIsHistory, NumberConstant.ZERO)
                .eq(KwsAlarmDetail::getDelFlag, NumberConstant.ONE)
        );
        if (!CollectionUtils.isEmpty(details)) {
            details.forEach(alarmDetail -> {
                alarmDetailMapper.deleteById(alarmDetail);
            });
        }
    }

    private void checkDeviceAlarm(String deviceCode, KwsDevice device, Devices devices) throws IOException {
        Date deviceTime = devices.getTs();
        Date date = new Date();
        long diff = Math.abs(date.getTime() - deviceTime.getTime());
        long diffHours = diff / (60 * 60 * 1000) % 24;
        if (diffHours > 24) {
            long id = insertAlarmAndDetailByDevice(1L, device, device.getId(), AlarmTitleEnum.ALARM_TITLE_ONE.getStatus(), AlarmTypeEnum.ALARM_TWO.getCode());
            // todo 暂未设备告警电话，设备告警不推送短信
            //设备离线
            device.setOnline(1);
            deviceMapper.updateById(device);
            pushDeviceAlarmSmsMessage(device);
            pushDeviceAlarmEmailMessage(device.getMountainId(), 1, device, deviceTime.getTime());
        } else {
            device.setOnline(0);
            deviceMapper.updateById(device);
        }
    }

    /**
     * 保存阈值主表以及明细表
     *
     * @param level       阈值等级
     * @param device      设备
     * @param thresholdId 阈值id/设备id
     * @param alarmTitle  设备类型明细
     * @param type        设备类型
     */
    @Transactional
    public long insertAlarmAndDetailByDevice(Long level, KwsDevice device, Long thresholdId,
                                             String alarmTitle, Integer type
    ) {
        Long deviceId = device.getId();
        KwsProjectDevice projectDevice = projectDeviceMapper.selectOne(new LambdaQueryWrapper<KwsProjectDevice>()
                .eq(KwsProjectDevice::getDeviceId, deviceId)
                .eq(KwsProjectDevice::getDelFlag, 0)
                .orderByDesc(KwsProjectDevice::getCreateTime)
                .last(" limit 1")
        );
        //新增未读数据
        long alarmId = insertUnreadByDevice(level, device, thresholdId, alarmTitle, type, deviceId, projectDevice);
        //新增历史数据
        insertHistoryReadByDevice(level, alarmTitle, type, deviceId, projectDevice);
        return alarmId;
    }

    private void insertHistoryReadByDevice(Long level, String alarmTitle, Integer type, Long deviceId, KwsProjectDevice projectDevice) {
        LambdaQueryWrapper<KwsAlarm> wrapper = new LambdaQueryWrapper<KwsAlarm>()
                .eq(KwsAlarm::getStatus, NumberConstant.ONE)
                .eq(KwsAlarm::getType, type)
                .eq(KwsAlarm::getLevel, level.intValue())
                .eq(KwsAlarm::getDeviceId, deviceId)
                .eq(KwsAlarm::getIsHistory, 1)
                .eq(KwsAlarm::getTitle, alarmTitle)
                .eq(KwsAlarm::getDelFlag, 0)
                .orderByDesc(KwsAlarm::getCreateTime);
        if (projectDevice != null) {
            wrapper.eq(KwsAlarm::getProjectId, projectDevice.getProjectId())
                    .eq(KwsAlarm::getMountainId, projectDevice.getMountainId());
            if (projectDevice.getProjectId() != null) {
                wrapper.eq(KwsAlarm::getCompanyId, projectDevice.getCompanyId());
            }
        }
        long alarmId = new IdWorker(NumberConstant.ONE).nextId();
        KwsAlarm alarms = alarmMapper.selectOne(wrapper);
        if (alarms == null) {
            KwsAlarm alarm = new KwsAlarm();
            String mountainId = projectDevice == null ? null : projectDevice.getMountainId();
            String companyId = projectDevice == null ? null : projectDevice.getCompanyId();
            String projectId = projectDevice == null ? null :
                    (projectDevice.getProjectId() == null ? null : projectDevice.getProjectId().toString());
            alarm.setId(alarmId);
            alarm.setCompanyId(companyId);
            alarm.setMountainId(mountainId);
            alarm.setProjectId(projectId);
            alarm.setDeviceId(deviceId);
            alarm.setTitle(alarmTitle);
            alarm.setLevel(level.intValue());
            alarm.setType(type);
            LocalDateTime localDateTime = LocalDateTime.now();
            alarm.setCreateTime(localDateTime);
            alarm.setUpdateTime(localDateTime);
            alarm.setStatus(1);
            alarm.setTriggerTimes(0);
            alarm.setIsHistory(1);
            alarmMapper.insert(alarm);
        }
    }

    private long insertUnreadByDevice(Long level, KwsDevice device, Long thresholdId, String alarmTitle, Integer type, Long deviceId, KwsProjectDevice projectDevice) {
        long alarmId = new IdWorker(NumberConstant.ONE).nextId();
        KwsAlarm alarms = selectAlarmData(level, deviceId, type, alarmTitle, projectDevice);
        if (alarms == null) {
            KwsAlarm alarm = new KwsAlarm();
            String mountainId = projectDevice == null ? null : projectDevice.getMountainId();
            String companyId = projectDevice == null ? null : projectDevice.getCompanyId();
            String projectId = projectDevice == null ? null :
                    (projectDevice.getProjectId() == null ? null : projectDevice.getProjectId().toString());
            alarm.setId(alarmId);
            alarm.setCompanyId(companyId);
            alarm.setMountainId(mountainId);
            alarm.setProjectId(projectId);
            alarm.setDeviceId(deviceId);
            alarm.setTitle(alarmTitle);
            alarm.setLevel(level.intValue());
            alarm.setType(type);
            LocalDateTime localDateTime = LocalDateTime.now();
            alarm.setCreateTime(localDateTime);
            alarm.setUpdateTime(localDateTime);
            alarm.setStatus(0);
            alarm.setTriggerTimes(1);
            alarmMapper.insert(alarm);
            KwsAlarmDetail alarmDetail = new KwsAlarmDetail();
            alarmDetail.setId(new IdWorker(NumberConstant.ONE).nextId());
            alarmDetail.setMountainId(mountainId);
            alarmDetail.setCompanyId(companyId);
            alarmDetail.setAlarmId(alarmId);
            alarmDetail.setPid(thresholdId);
//            alarmDetail.setVal(itemValue);
            alarmDetail.setLat(device.getLogicLat());
            alarmDetail.setLng(device.getLogicLng());
            alarmDetail.setAlt(device.getLogicAlt());
            alarmDetail.setCreateTime(localDateTime);
            alarmDetail.setUpdateTime(localDateTime);
            alarmDetail.setStatus(0);
            alarmDetail.setDelFlag(NumberConstant.ZERO);
            alarmDetail.setIsHistory(NumberConstant.ZERO);
            alarmDetailMapper.insert(alarmDetail);
        } else {
            alarmId = alarms.getId();
            KwsAlarm alarm = alarms;
            LocalDateTime localDateTime = LocalDateTime.now();
//            LocalDateTime localDateTime = new Date(ts).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
            alarm.setUpdateTime(localDateTime);
            alarm.setStatus(0);
            Long aLong = alarmDetailMapper.selectCount(new LambdaQueryWrapper<KwsAlarmDetail>()
                    .eq(KwsAlarmDetail::getAlarmId, alarmId)
                    .eq(KwsAlarmDetail::getStatus, 0)
                    .eq(KwsAlarmDetail::getIsHistory, 0)
                    .eq(KwsAlarmDetail::getDelFlag, 0)
            );
            alarm.setTriggerTimes(aLong.intValue() + 1);
            alarmMapper.updateById(alarm);
            String mountainId = projectDevice == null ? null : projectDevice.getMountainId();
            String companyId = projectDevice == null ? null : projectDevice.getCompanyId();
            String projectId = projectDevice == null ? null :
                    (projectDevice.getProjectId() == null ? null : projectDevice.getProjectId().toString());
            KwsAlarmDetail alarmDetail = new KwsAlarmDetail();
            alarmDetail.setId(new IdWorker(NumberConstant.TWO).nextId());
            alarmDetail.setMountainId(mountainId);
            alarmDetail.setCompanyId(companyId);
            alarmDetail.setAlarmId(alarm.getId());
            alarmDetail.setPid(thresholdId);
//            alarmDetail.setVal(itemValue);
            alarmDetail.setLat(device.getLogicLat());
            alarmDetail.setLng(device.getLogicLng());
            alarmDetail.setAlt(device.getLogicAlt());
            alarmDetail.setCreateTime(localDateTime);
            alarmDetail.setUpdateTime(localDateTime);
            alarmDetail.setStatus(0);
            alarmDetail.setDelFlag(NumberConstant.ZERO);
            alarmDetail.setIsHistory(NumberConstant.ZERO);
            alarmDetailMapper.insert(alarmDetail);
        }
        return alarmId;
    }


    private KwsAlarm selectAlarmData(Long level, Long deviceId, Integer type, String alarmTitle, KwsProjectDevice projectDevice) {
        LambdaQueryWrapper<KwsAlarm> wrapper = new LambdaQueryWrapper<KwsAlarm>()
                .eq(KwsAlarm::getLevel, level.intValue())
                .eq(KwsAlarm::getDeviceId, deviceId)
                .eq(KwsAlarm::getStatus, NumberConstant.ZERO)
                .eq(KwsAlarm::getType, type)
                .eq(KwsAlarm::getTitle, alarmTitle)
                .eq(KwsAlarm::getIsHistory, NumberConstant.ZERO)
                .eq(KwsAlarm::getDelFlag, NumberConstant.ZERO)
                .orderByDesc(KwsAlarm::getCreateTime);
        if (projectDevice != null) {
            wrapper.eq(KwsAlarm::getProjectId, projectDevice.getProjectId())
                    .eq(KwsAlarm::getMountainId, projectDevice.getMountainId());
            if (projectDevice.getProjectId() != null) {
                wrapper.eq(KwsAlarm::getCompanyId, projectDevice.getCompanyId());
            }
        }
        KwsAlarm alarms = alarmMapper.selectOne(wrapper);
        return alarms;
    }
}
