package com.zcxk.charge.xxl.mq;

import com.alibaba.fastjson.JSONObject;
import com.zcxk.charge.api.dto.WorkOrderManageByAlarmDto;
import com.zcxk.charge.api.vo.AlarmDetailsEntityVo;
import com.zcxk.charge.api.vo.DeviceCheckAlarmVo;
import com.zcxk.charge.core.dao.AlarmDetailMapper;
import com.zcxk.charge.core.entity.AlarmDetailsEntity;
import com.zcxk.charge.core.entity.MonitorDataEntity;
import com.zcxk.charge.xxl.client.OperationManagerClient;
import com.zcxk.charge.xxl.client.UserCenterClient;
import com.zcxk.core.common.pojo.Message;
import com.zcxk.core.common.util.BeanCopyUtils;
import com.zcxk.core.utils.CalcUtil;
import com.zcxk.core.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName AlarmDataHandler
 * @Description: 报警数据处理
 * @Author lihui
 * @Date 2021/4/8
 * @Version V1.0
 **/
@Component
@Slf4j
public class AlarmDataHandler {

    @Resource(name = "messageRabbitTemplate")
    private AmqpTemplate messageRabbitTemplate;

    @Resource
    private AlarmDetailMapper alarmDetailMapper;

    @Resource
    private OperationManagerClient operationManagerClient;

    @Resource
    private UserCenterClient userCenterClient;

    @Value("${receive.exchange.name}")
    private String receiveExchangeName;

    @Value("${dispath.routing.key}")
    private String dispathRoutingKey;

    @Async
    public void hanlder(MonitorDataEntity monitorDataEntity, JSONObject receiveData, Date receiveDateTime){
        log.info("rabbitMq处理报警，code:{}，上报时间:{}",monitorDataEntity.getDeviceCode(), receiveDateTime);
        // 校验各参数异常情况
        Double receivedValue            = null;
        List<AlarmDetailsEntity> insert = null;
        List<AlarmDetailsEntity> update = null;
        // 获取需要验证的报警条件monitorDataEntity.getDeviceId()
        List<DeviceCheckAlarmVo> deviceCheckAlarmDtos = alarmDetailMapper.selectDeviceForCheckAlarm(monitorDataEntity.getDeviceId(),"参数报警");
        // 有设置报警参数
        if (deviceCheckAlarmDtos.size() == 0 || StringUtils.isBlank(deviceCheckAlarmDtos.get(0).getIdentifiter())){
            return;
        }
        insert = new ArrayList<>();
        update = new ArrayList<>();
        // 系统中已存在的报警信息
        List<AlarmDetailsEntityVo> alarmDetailsDtos = alarmDetailMapper.selectStateAlarm(deviceCheckAlarmDtos.get(0).getDeviceId(),"参数报警",null);
        // 已存在的报警信息转为map方便匹配alarmSettingId
        Map<Integer,AlarmDetailsEntityVo> alarmDetailsDtoMap = alarmDetailsDtos.stream().collect(Collectors.toMap(AlarmDetailsEntityVo::getAlarmSettingId, a -> a,(k1, k2)->k1));
        for (DeviceCheckAlarmVo deviceCheckAlarmDto : deviceCheckAlarmDtos){
            // 获取参数接收值
            receivedValue = receiveData.getDouble(deviceCheckAlarmDto.getIdentifiter());
            if (!deviceCheckAlarmDto.checkdeviceAttributeAlarm(receivedValue)) {
                continue;
            }
            // 如果设置了报警时间，需要检查当前系统时间是否在设置的时间范围内
            if (!isInTime(deviceCheckAlarmDto.getStartTime(), deviceCheckAlarmDto.getEndTime(), new Date())){
                continue;
            }
            // 判断报警是否已存在
            if (alarmDetailsDtoMap.containsKey(deviceCheckAlarmDto.getAlarmSettingId())) {
                AlarmDetailsEntityVo alarmDetailsDto = alarmDetailsDtoMap.get(deviceCheckAlarmDto.getAlarmSettingId());
                alarmDetailsDto.setAlarmValue(receivedValue);
                alarmDetailsDto.setMinValue(CalcUtil.compareBySign(alarmDetailsDto.getMinValue(),receivedValue, "<")?alarmDetailsDto.getMinValue():receivedValue);
                alarmDetailsDto.setMaxValue(CalcUtil.compareBySign(alarmDetailsDto.getMaxValue(),receivedValue,">")?alarmDetailsDto.getMaxValue():receivedValue);
                alarmDetailsDto.setDateUpdate(new Date());

                AlarmDetailsEntity entity = new AlarmDetailsEntity();
                BeanCopyUtils.copyProperties(alarmDetailsDto, entity, AlarmDetailsEntityVo.class);
                alarmDetailMapper.update(entity);
                update.add(entity);
                // 已存在的修改后从集合移除
                alarmDetailsDtoMap.remove(deviceCheckAlarmDto.getAlarmSettingId());
            } else {
                AlarmDetailsEntity alarmDetailsEntity = getAlarmDetailsEntity(deviceCheckAlarmDto, receivedValue, receiveDateTime);
                sendMessage(deviceCheckAlarmDto);
                insert.add(alarmDetailsEntity);
            }
        }
        // 批量插入新增报警
        if (insert.size() > 0) {
            alarmDetailMapper.batchInsert(insert);
        }
        if (alarmDetailsDtoMap.values().size() == 0) {
            return;
        }
        // 处理完成后，剩下的标记为历史数据
        List<Long> deviceIds = new ArrayList<>();
        deviceIds.add(monitorDataEntity.getDeviceId().longValue());
        List<WorkOrderManageByAlarmDto> workOrders = operationManagerClient.findWorkOrderByDeviceIds(deviceIds);
        if (CollectionUtils.isEmpty(workOrders)) {
            return;
        }
        WorkOrderManageByAlarmDto orderManageByAlarmDto = workOrders.get(0);
        String taskDesc = orderManageByAlarmDto.getTaskDesc();
        // 标记为历史数据 并 修改工单描述
        for (AlarmDetailsEntityVo alarmDetailsDto : alarmDetailsDtoMap.values()){
            alarmDetailsDto.setState(0);
            alarmDetailsDto.setAlarmEndTime(receiveDateTime);
            alarmDetailsDto.setDateUpdate(new Date());

            AlarmDetailsEntity entity = new AlarmDetailsEntity();
            BeanCopyUtils.copyProperties(alarmDetailsDto, entity, AlarmDetailsEntityVo.class);
            alarmDetailMapper.update(entity);
            taskDesc = taskDesc.replace(";"+alarmDetailsDto.getAlarmContent(),"")
                    .replace(alarmDetailsDto.getAlarmContent(),"");
        }
        orderManageByAlarmDto.setTaskDesc(taskDesc);
        // 工单未派单且无报警信息，删除工单
        if (orderManageByAlarmDto != null && isEmpty(orderManageByAlarmDto.getOrderStatus(), taskDesc)) {
            operationManagerClient.batchDeleteByAlarms(new ArrayList<>(deviceIds));
            return;
        }
        // 其他情况修改描述
        operationManagerClient.updateByAlarms(workOrders);
    }

    private boolean isEmpty(Integer orderStatus, String taskDesc){
        return (orderStatus == null || orderStatus == 0) && StringUtils.isBlank(taskDesc);
    }


    /**
    * @Author lihui
    * @Description //TODO
    * @Date 14:27 2021/4/16
    * @Param [deviceCheckAlarmDto]
    * @return boolean
    **/
    private boolean isInTime(String startTime, String endTime, Date date){
        if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime)) {
            return true;
        }
        return DateUtil.isInTime(startTime, endTime, date,"HH:mm:ss");
    }

    /**
     * @Author lihui
     * @Description 设置报警信息
     * @Date 17:29 2021/4/7
     * @Param [deviceCheckAlarmDto, receivedValue, receiveDateTime]
     * @return com.zcxk.entity.AlarmDetailsEntity
     **/
    private AlarmDetailsEntity getAlarmDetailsEntity(DeviceCheckAlarmVo deviceCheckAlarmDto, Double receivedValue, Date receiveDateTime){
        AlarmDetailsEntity alarmDetailsEntity = new AlarmDetailsEntity();
        alarmDetailsEntity.setTenantId(deviceCheckAlarmDto.getTenantId());
        alarmDetailsEntity.setParentSceneId(deviceCheckAlarmDto.getParentSceneId());
        alarmDetailsEntity.setParentSceneName(deviceCheckAlarmDto.getParentSceneName());
        alarmDetailsEntity.setSceneId(deviceCheckAlarmDto.getSceneId());
        alarmDetailsEntity.setSceneName(deviceCheckAlarmDto.getSceneName());
        alarmDetailsEntity.setDeviceId(deviceCheckAlarmDto.getDeviceId());
        alarmDetailsEntity.setCompanyOrgId(deviceCheckAlarmDto.getCompanyOrgId());
        alarmDetailsEntity.setDeptOrgId(deviceCheckAlarmDto.getDeptOrgId());
        alarmDetailsEntity.setAlarmType(deviceCheckAlarmDto.getAlarmType());
        alarmDetailsEntity.setAttributeId(deviceCheckAlarmDto.getAttributeId());
        alarmDetailsEntity.setAlarmValue(receivedValue);
        alarmDetailsEntity.setAlarmStartTime(receiveDateTime);
        alarmDetailsEntity.setAlarmContent(deviceCheckAlarmDto.getAlarminfo(receivedValue));
        alarmDetailsEntity.setAlarmSettingId(deviceCheckAlarmDto.getAlarmSettingId());
        alarmDetailsEntity.setState(1);
        alarmDetailsEntity.setOpState(1);
        alarmDetailsEntity.setMinValue(receivedValue);
        alarmDetailsEntity.setMaxValue(receivedValue);
        alarmDetailsEntity.setStatus(1);
        alarmDetailsEntity.setDateCreate(new Date());
        alarmDetailsEntity.setDateUpdate(new Date());
        alarmDetailsEntity.setCreateBy("system");
        alarmDetailsEntity.setUpdateBy("system");
        return alarmDetailsEntity;
    }

    private void sendMessage(DeviceCheckAlarmVo deviceCheckAlarmDto){
        Message message = new Message();
        message.setStatus(1);
        message.setTenantId(deviceCheckAlarmDto.getTenantId());
        message.setMessageId(UUID.randomUUID().toString());
        JSONObject jsonContent = new JSONObject();
        // ${场景名称}${设备名称}【${报警字段}】异常报警
        jsonContent.put("场景名称",deviceCheckAlarmDto.getSceneName());
        jsonContent.put("设备名称",deviceCheckAlarmDto.getDeviceName());
        jsonContent.put("报警字段",deviceCheckAlarmDto.getAttributeName());
        // 消息内容，如果需要动态使用，配合模板使用{key:value}
        message.setMessageContent(jsonContent.toJSONString());
        // 消息类型、模板id、渠道
        message.setMessageType(1);
        message.setMessageTemplateId(1);
        message.setChannel(0);
        Integer companyOrgId = deviceCheckAlarmDto.getCompanyOrgId();
        Integer departmentOrgId = deviceCheckAlarmDto.getDeptOrgId();
        try{
            List<Integer> taskUsers = userCenterClient.findUserIdsByPermissonOrg(deviceCheckAlarmDto.getTenantId(),companyOrgId,departmentOrgId);
            if (taskUsers == null) {
                return;
            }
            taskUsers.forEach(id -> {
                message.setUserId(id);
                this.send(message);
            });
        } catch (Exception e) {
            log.error ("推送报警消息失败:{}",e);
        }
    }

    private void send(Message message){
        log.debug("消息发送 exchange={}  routingkey={} 用户id={}",receiveExchangeName,dispathRoutingKey,message.getUserId());
        messageRabbitTemplate.convertAndSend(receiveExchangeName, dispathRoutingKey, JSONObject.toJSONString(message));
    }

}
