package com.zcxk.charge.xxl.mq;

import com.alibaba.fastjson.JSONObject;
import com.zcxk.charge.core.dao.AlarmDetailMapper;
import com.zcxk.charge.core.dao.MonitorDataMapper;
import com.zcxk.charge.core.entity.MonitorDataEntity;
import com.zcxk.charge.core.entity.MonitorDataValueEntity;
import com.zcxk.core.utils.ByteArrayUtils;
import com.zcxk.core.utils.DateUtil;
import com.zcxk.core.utils.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


/**
 * @ClassName ReceiveClearData
 * @Description: 监听rabbitmq队列消息，获取设备报警信息 (优化版）
 * @Author lihui
 * @Date 2021/4/7
 * @Version V1.0
 **/
@Component
@Slf4j
public class ReceiveClearData {

    /**
     * 时间
     */
    private static final String EVENT_TIME_KEY = "eventTime";

    /**
     * 数据
     */
    private static final String UNIT_IDENTIFIER_KEY = "unitIdentifier";

    /**
     * parsedData
     */
    private static final String PARSED_DATA_KEY = "parsedData";

    @Autowired
    private AlarmDataHandler alarmDataHandler;

    @Autowired
    private  ReportWaterPumpStateHandler reportWaterPumpStateHandler;

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private AlarmDetailMapper alarmDetailMapper;

    @Value("${receiveData.spring.rabbitmq.listener.queue}")
    private String rabbitmqQueue;

    @Resource
    private MonitorDataMapper monitorDataMapper;

    @Resource
    private RedisUtil redisUtil;

    private static final String ORIGINAL_DATA_STRING = "originalData";


    public MonitorDataEntity getDeviceMonitorInfoByDeviceCode(String deviceCode){
        // 先取缓存里的数据
        byte[] bytes = redisUtil.get(("sms_water_"+deviceCode).getBytes());
        if(bytes != null && bytes.length>0){
            return (MonitorDataEntity)ByteArrayUtils.bytesToObject(bytes).get();
        }
        return monitorDataMapper.getDeviceMonitorInfoByDeviceCode(deviceCode);
    }

    public MonitorDataEntity save(MonitorDataEntity monitorDataEntity) {
        // 缓存数据
        redisUtil.setExpire(("sms_water_"+monitorDataEntity.getDeviceCode()).getBytes(), ByteArrayUtils.objectToBytes(monitorDataEntity).get());
        // 把没有数据的属性去掉再保存
        monitorDataEntity.setDataValues(monitorDataEntity.getDataValues().stream().filter(m -> m.getDataValue()!=null).collect(Collectors.toList()));

        return mongoTemplate.save(monitorDataEntity);
    }

    /**
     * 先注入，然后再通过SPEL取值
     * @return
     */
    @Bean
    public String rabbitmqQueue(){
        return rabbitmqQueue;
    }

    @RabbitHandler
    @RabbitListener(queues = "#{rabbitmqQueue}", containerFactory = "receiveDataContainerFactory")
    public void received(byte[] receivedData) {
        try {
            log.debug("rabbitMq接收消息："+new String(receivedData));
            receivedDataHandle(receivedData);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(ExceptionUtils.getStackTrace(e));
            // 发送异常时消息返回队列
//            receiveDataRabbitTemplate.convertAndSend(rabbitmqQueue, receivedData);
        }

    }

    /***
     * 		manufacturer 厂家，mode型号，设备编号unitIdentifier，type设备类型
     *
     * @author lihui
     * @date 10:07 2021/5/28
     * @param receivedData :
     * @return void
     **/
    public void receivedDataHandle(byte[] receivedData){
        JSONObject originalData = new JSONObject();
        JSONObject jsonObject = JSONObject.parseObject(new String(receivedData));
        String eventTime      = jsonObject.getString("eventTime");
        String deviceCode     = jsonObject.getString("unitIdentifier");
        Date receiveDateTime  = parseDate(eventTime);
        log.info("rabbitMq接收消息处理，code:{}，上报时间:{}", deviceCode, eventTime);
        if (!checkParameter(jsonObject, deviceCode, receiveDateTime)){
            return;
        }
        // 查询不到设备或者设备属性为空
        MonitorDataEntity monitorDataEntity = getDeviceMonitorInfoByDeviceCode(deviceCode);
        if (monitorDataEntity == null || CollectionUtils.isEmpty(monitorDataEntity.getDataValues())){
            log.error("rabbitMq接收消息处理，code:{}，查询不到设备或者设备属性为空，退出",deviceCode);
            return;
        }
        JSONObject receiveData  = JSONObject.parseObject(jsonObject.getString("parsedData"));
        List<MonitorDataValueEntity> monitorDataValueEntities = monitorDataEntity.getDataValues();
        Integer number = 0;
        for (MonitorDataValueEntity monitorDataValueEntity : monitorDataValueEntities) {
            BigDecimal bigDecimal = null;
            try {
                Double doubleValue = receiveData.getDouble(monitorDataValueEntity.getIdentifier());
                if (doubleValue == null) {
                    continue;
                }
                bigDecimal = new BigDecimal(doubleValue);
            } catch (NumberFormatException e) {
                log.error("double 转换 error ->", e);
                continue;
            }
            // 整数超过10位直接变更为0
            if (String.valueOf(bigDecimal.intValue()).length() > 10 ){
                monitorDataValueEntity.setDataValue(0.0);
            } else {
                // 小数保留5位
                Double dataValue = bigDecimal.setScale(5, BigDecimal.ROUND_HALF_UP).doubleValue();
                monitorDataValueEntity.setDataValue(dataValue);
            }
            number++;
        }
        // 没有匹配到属性，视为垃圾数据忽略
        if (number == 0) {
            log.error("rabbitMq接收消息处理，code:{}，上报时间:{}，没有匹配到属性，视为垃圾数据忽略，退出",deviceCode, eventTime);
            return;
        }
        // 保存到缓存和mongodb数据库
        saveToCacheAndMongodb(monitorDataEntity, receiveDateTime);
        // 修改设备上报时间
        alarmDetailMapper.udpateLastUpdateTime(monitorDataEntity.getDeviceId(),receiveDateTime);
        // 水泵运行状态报表业务处理
        reportWaterPumpStateHandler.handler(monitorDataEntity, receiveData, receiveDateTime);
        // 异步处理报警信息
        alarmDataHandler.hanlder(monitorDataEntity, receiveData, receiveDateTime);
    }


    private Date parseDate(String eventTime){
        return StringUtils.isEmpty(eventTime) ? null : DateUtil.parseDate(eventTime, "yyyy-MM-dd HH:mm:ss");
    }


    /***
     * 对象有时间、有设备编码、有数据则解析，任何一个位空直接退出
     * @author lihui
     * @date 10:53 2021/5/28
     * @param jsonObject :
     * @param deviceCode :
     * @return boolean
     **/
    private boolean checkParameter(JSONObject jsonObject, String deviceCode, Date receiveDateTime){
        if (!jsonObject.containsKey(EVENT_TIME_KEY) || !jsonObject.containsKey(UNIT_IDENTIFIER_KEY) ||
                !jsonObject.containsKey(PARSED_DATA_KEY)) {
            log.error("rabbitMq接收消息处理，code:{}，上报时间:{}，退出",deviceCode);
            return false;
        }
        if (receiveDateTime == null) {
            log.error("rabbitMq接收消息处理，code:{}，报警信息查询时间转换错误，退出", deviceCode);
            return false;
        }
        return true;
    }


    private void saveToCacheAndMongodb(MonitorDataEntity monitorDataEntity, Date receiveDateTime){
        monitorDataEntity.setCollectDate(receiveDateTime);
        Calendar cal = Calendar.getInstance();
        cal.setTime(receiveDateTime);
        monitorDataEntity.setYear(cal.get(Calendar.YEAR));
        monitorDataEntity.setMonth(cal.get(Calendar.MONTH)+1);
        monitorDataEntity.setDay(cal.get(Calendar.DAY_OF_MONTH));
        monitorDataEntity.setHour(cal.get(Calendar.HOUR_OF_DAY));
        save(monitorDataEntity);
    }

    /**
     * 16进制转换成为string类型字符串
     * @param
     * @return
     */
    public static String hexStr2Str(String hexStr) {
           String str = "0123456789ABCDEF";
           char[] hexs = hexStr.toCharArray();
           byte[] bytes = new byte[hexStr.length() / 2];
            int n;
            for (int i = 0; i < bytes.length; i++) {
                 n = str.indexOf(hexs[2 * i]) * 16;
                 n += str.indexOf(hexs[2 * i + 1]);
                 bytes[i] = (byte) (n & 0xff);
             }
            return new String(bytes);
        }

    public static void main(String[] args) {
        System.out.println(ReceiveClearData.hexStr2Str("ABCD0031EA892108220200010501FE0F7710443131333B322E313B352E302031392020202020000000FFFF58521704013EAEEAF02B0D0A"));
    }
}
