package com.yzt.airconditionersystem.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.*;
import com.yzt.airconditionersystem.bean.Device;
import com.yzt.airconditionersystem.bean.DeviceData;
import com.yzt.airconditionersystem.bean.FacilityAlarm;
import com.yzt.airconditionersystem.mapper.DeviceDataMapper;
import com.yzt.airconditionersystem.mapper.DeviceMapper;
import com.yzt.airconditionersystem.service.FacilityAlarmService;
import com.yzt.airconditionersystem.service.MqttService;
import com.yzt.airconditionersystem.util.AirconditionerStatus;
import com.yzt.airconditionersystem.util.MyUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.thymeleaf.util.StringUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author TzZach
 * @since 2022-05-10 22:54
 */
@Service
@Slf4j
public class MqttServiceImpl implements MqttService {


    @Autowired
    private DeviceDataMapper deviceDataMapper;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private FacilityAlarmService facilityAlarmService;

    @Override
    public void receiveFrom(String topic, Message message) throws IllegalAccessException {
//        System.out.println("接收的消息主题：" + topic);
//        System.out.println("接收消息QoS：" + message.getQos());
        //通过主题截取出公司名称和网关设备id，截取加1的目的是不取其中的“/”
        String companyNameAndGatewayId = topic.substring(topic.indexOf("/") + 1, topic.lastIndexOf("/"));
        String companyName = companyNameAndGatewayId.substring(0, companyNameAndGatewayId.indexOf("/"));
        String gatewayId = companyNameAndGatewayId.substring(companyNameAndGatewayId.indexOf("/") + 1);
        String payload = (String) message.getPayload();
        System.out.println(payload);

        //获取Json数组的第一个值并转换成为Json对象
        JsonObject deviceJson = JsonParser.parseString(payload).getAsJsonObject()
                .getAsJsonArray("devices").get(0).getAsJsonObject();
        Gson gson = new GsonBuilder().create();
        Device device = gson.fromJson(deviceJson.getAsJsonObject().toString(), Device.class);
        //设置一个deviceDataId
        String deviceDataId = MyUtils.getUUID();
        device.setCompanyName(companyName);
        device.setGatewayId(gatewayId);
        //要截取的前缀
        Set<String> deviceDataKeySet = deviceJson.get("deviceData").getAsJsonObject().keySet();
        String deviceDataKey = deviceDataKeySet.iterator().next();
        String prefix = deviceDataKey.substring(0, deviceDataKey.indexOf("_") + 1);
        device.setPrefix(prefix);
        //将deviceData转成Json对象再转成字符串后进行截取
        String deviceDataStr = deviceJson.get("deviceData").getAsJsonObject().toString().replace(prefix, "");
        DeviceData deviceData = gson.fromJson(deviceDataStr, DeviceData.class);
        //判断所接收的数据是否符合所需（即数据是否存在不为空的属性）
        if (!MyUtils.isFieldsAllNull(deviceData)) {
            deviceData.setId(deviceDataId);
            LocalDateTime now = LocalDateTime.now();
            deviceData.setTime(now);
            device.setTime(now);
            //如果该设备信息已存在，不插入
            String dId = insertDeviceIfNotExists(companyName, gatewayId, device);
            deviceData.setDId(dId);
            deviceDataMapper.insert(deviceData);
            setAlarm(deviceData, gatewayId);
        }
    }

    /**
     * 如果设备信息已存在，不插入；如果不存在，则插入
     * @param companyName 设备所属公司名称
     * @param gatewayId 设备所在网关id
     * @param device 设备对象
     * @return 返回插入的条目的id
     */
    private String insertDeviceIfNotExists(String companyName, String gatewayId, Device device) {
        String deviceId = device.getDeviceId();
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getCompanyName, companyName)
                .eq(Device::getGatewayId, gatewayId)
                .eq(Device::getDeviceId, deviceId)
                .last("limit 1");
        Device deviceInfo = deviceMapper.selectOne(queryWrapper);
        if (deviceInfo == null) {
            device.setId(MyUtils.getUUID());
            deviceMapper.insert(device);
            return device.getId();
        }
        return deviceInfo.getId();
    }


    /**
     * 根据传过来的数据设置报警
     * @param deviceData
     */
    private void setAlarm(DeviceData deviceData, String gatewayId) {
//        String data2 = map.get("data2");
//        String data3 = map.get("data3");
        Integer mBit2 = deviceData.getM_bit2();
        Integer qBit3 = deviceData.getQ_bit3();
        if (ObjectUtils.isEmpty(mBit2) || ObjectUtils.isEmpty(qBit3)) {
            return;
        }

        String m_bit2 = MyUtils.decToBin(mBit2);
        String q_bit3 = MyUtils.decToBin(qBit3);
        List<FacilityAlarm> facilityAlarms = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();


        if (q_bit3.charAt(AirconditionerStatus.UNIT_FAILURE) == AirconditionerStatus.ALARM) {
            addFacilityAlarms(AirconditionerStatus.UNIT_FAILURE_ALARM_CONTENT, AirconditionerStatus.STATUS_LASTING, facilityAlarms, now, gatewayId);
        }
        if (m_bit2.charAt(AirconditionerStatus.LACK_OF_LEEWAY) == AirconditionerStatus.ALARM) {
            addFacilityAlarms(AirconditionerStatus.LACK_OF_LEEWAY_ALARM_CONTENT, AirconditionerStatus.STATUS_LASTING, facilityAlarms, now, gatewayId);
        }
        if (m_bit2.charAt(AirconditionerStatus.HIGH_TEMPERATURE_ALARM) == AirconditionerStatus.ALARM) {
            addFacilityAlarms(AirconditionerStatus.HIGH_TEMPERATURE_ALARM_ALARM_CONTENT, AirconditionerStatus.STATUS_LASTING, facilityAlarms, now, gatewayId);
        }
        if (m_bit2.charAt(AirconditionerStatus.AIR_COOLED_MAINFRAME) == AirconditionerStatus.ALARM) {
            addFacilityAlarms(AirconditionerStatus.AIR_COOLED_MAINFRAME_ALARM_CONTENT, AirconditionerStatus.STATUS_LASTING, facilityAlarms, now, gatewayId);
        }
        if (m_bit2.charAt(AirconditionerStatus.EXHAUST_FAN) == AirconditionerStatus.ALARM) {
            addFacilityAlarms(AirconditionerStatus.EXHAUST_FAN_ALARM_CONTENT, AirconditionerStatus.STATUS_LASTING, facilityAlarms, now, gatewayId);
        }
        if (m_bit2.charAt(AirconditionerStatus.BLOWER) == AirconditionerStatus.ALARM) {
            addFacilityAlarms(AirconditionerStatus.BLOWER_ALARM_CONTENT, AirconditionerStatus.STATUS_LASTING, facilityAlarms, now, gatewayId);
        }
        if (m_bit2.charAt(AirconditionerStatus.W_CELL) == AirconditionerStatus.ALARM) {
            addFacilityAlarms(AirconditionerStatus.W_CELL_ALARM_CONTENT, AirconditionerStatus.STATUS_LASTING, facilityAlarms, now, gatewayId);
        }
        if (m_bit2.charAt(AirconditionerStatus.PREFILTER) == AirconditionerStatus.ALARM) {
            addFacilityAlarms(AirconditionerStatus.PREFILTER_ALARM_CONTENT, AirconditionerStatus.STATUS_LASTING, facilityAlarms, now, gatewayId);
        }

        facilityAlarmService.saveBatch(facilityAlarms);

    }


    /**
     * 将设备报警加入到list中
     * @param alarmContent
     * @param status
     * @param facilityAlarms
     * @param now
     */
    private void addFacilityAlarms(String alarmContent, String status, List<FacilityAlarm> facilityAlarms, LocalDateTime now, String gatewayId) {
        FacilityAlarm facilityAlarm =
                new FacilityAlarm(null, now, alarmContent, status, gatewayId, 1L);
        LambdaQueryWrapper<FacilityAlarm> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FacilityAlarm::getAlarmContent, alarmContent)
                            .eq(FacilityAlarm::getGatewayId, gatewayId)
                            .eq(FacilityAlarm::getStatus, status);
        FacilityAlarm alarm = facilityAlarmService.getOne(queryWrapper);

        if (!ObjectUtils.isEmpty(alarm)) {
            facilityAlarmService.removeById(alarm);
            LocalDateTime time = alarm.getTime();
            Duration between = Duration.between(time, now);
            facilityAlarm.setLastingTime(between.toMinutes());
        }

        facilityAlarms.add(facilityAlarm);
    }
}
