package com.yzt.airconditionersystem.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import com.yzt.airconditionersystem.bean.*;
import com.yzt.airconditionersystem.config.MqttOutboundConfig;
import com.yzt.airconditionersystem.result.Result;
import com.yzt.airconditionersystem.service.*;
import com.yzt.airconditionersystem.util.AirconditionerStatus;
import com.yzt.airconditionersystem.util.BasePageVoUtil;
import com.yzt.airconditionersystem.util.MyUtils;
import com.yzt.airconditionersystem.vo.BasePageVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.messaging.MessageHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalUnit;
import java.util.*;

/**
 * @author TzZach
 * @since 2022-04-18 11:10
 */
@Slf4j
@Controller
public class FacilityController {

    private static final String PREFIX = "facility/";

    @Autowired
    private MqttOutboundConfig.MyGateway mqttGateway;
    @Autowired
    private MessageHandler mqttOutbound;
    @Autowired
    private FacilityAlarmService facilityAlarmService;
    @Autowired
    private DeviceService deviceService;
    @Autowired
    private DeviceDataService deviceDataService;
//    @Autowired
//    private MqttProducer mqttProducer;
    @Autowired
    private LogService logService;

    @GetMapping("/facility-list")
    public String facilityList(@RequestParam("gatewayId") String gatewayId,
//                               @RequestParam("deviceId") String deviceId,
                               HttpSession session) {
//        Device sessionAttribute = (Device) session.getAttribute("device");
        LambdaQueryWrapper<Device> deviceLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceLambdaQueryWrapper.eq(Device::getGatewayId, gatewayId)
//                        .eq(Device::getDeviceId, sessionAttribute.getDeviceId())
                .orderByDesc(Device::getTime).last("limit 1");
        Device device = deviceService.getOne(deviceLambdaQueryWrapper);
        session.setAttribute("device", device);
        return PREFIX + "facility-list";
    }

    @GetMapping("/status")
    public String status(Model model, HttpSession session) {
        Device deviceSession = (Device) session.getAttribute("device");
//        String gatewayId = deviceSession.getGatewayId();

//        LambdaQueryWrapper<Device> deviceLambdaQueryWrapper = new LambdaQueryWrapper<>();
//        deviceLambdaQueryWrapper.eq(Device::getGatewayId, gatewayId)
//                .orderByDesc(Device::getTime);
        //获取最近的一个作为状态显示
//        Device device = deviceService.list(deviceLambdaQueryWrapper).get(0);
        Device device = deviceService.getById(deviceSession.getId());
        LambdaQueryWrapper<DeviceData> dataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        dataLambdaQueryWrapper.eq(DeviceData::getDId, deviceSession.getId())
                .orderByDesc(DeviceData::getTime).last("limit 1");
        DeviceData deviceData = deviceDataService.getOne(dataLambdaQueryWrapper);

        String m_bit1 = MyUtils.decToBin(deviceData.getM_bit1());
        String m_bit2 = MyUtils.decToBin(deviceData.getM_bit2());
        String q_bit3 = MyUtils.decToBin(deviceData.getQ_bit3());
        deviceData.setM_bit1BinStr(m_bit1);
        deviceData.setM_bit2BinStr(m_bit2);
        deviceData.setQ_bit3BinStr(q_bit3);

        device.setDeviceData(deviceData);

        model.addAttribute("device", device);

        return PREFIX + "status";
    }

    @GetMapping("/system-parameter")
    public String systemParameter(Model model, HttpSession session) {

        Device deviceSession = (Device) session.getAttribute("device");
        String gatewayId = deviceSession.getGatewayId();
        String deviceId = deviceSession.getDeviceId();
        //得到最新的device信息，并根据device中的deviceDataId获取对应deviceData
        LambdaQueryWrapper<Device> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Device::getGatewayId, gatewayId)
                .eq(Device::getDeviceId, deviceId);
        Device device = deviceService.getOne(lambdaQueryWrapper);
        LambdaQueryWrapper<DeviceData> deviceDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceDataLambdaQueryWrapper.eq(DeviceData::getDId, device.getId())
                .orderByDesc(DeviceData::getTime).last("limit 1");
        DeviceData deviceData = deviceDataService.getOne(deviceDataLambdaQueryWrapper);
//        LambdaQueryWrapper<DeviceData> deviceDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
//        deviceDataLambdaQueryWrapper.eq(DeviceData::getDId, device)
//        DeviceData deviceData = deviceDataService.getById(device.getDeviceDataId());
        model.addAttribute("deviceData", deviceData);
        return PREFIX + "system-parameter";
    }

    @GetMapping("/parameter-settings")
    public String parameterSettings(Model model, HttpSession session) {
        Device deviceSession = (Device) session.getAttribute("device");
        String gatewayId = deviceSession.getGatewayId();
        String deviceId = deviceSession.getDeviceId();
        //得到最新的device信息，并根据device中的deviceDataId获取对应deviceData
        LambdaQueryWrapper<Device> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Device::getGatewayId, gatewayId)
                .eq(Device::getDeviceId, deviceId);
        Device device = deviceService.getOne(lambdaQueryWrapper);
        LambdaQueryWrapper<DeviceData> deviceDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceDataLambdaQueryWrapper.eq(DeviceData::getDId, device.getId())
                .orderByDesc(DeviceData::getTime).last("limit 1");
        DeviceData deviceData = deviceDataService.getOne(deviceDataLambdaQueryWrapper);
        deviceData.setM_bit1BinStr(MyUtils.decToBin(deviceData.getM_bit1()));
        deviceData.setM_bit2BinStr(MyUtils.decToBin(deviceData.getM_bit2()));
        deviceData.setQ_bit3BinStr(MyUtils.decToBin(deviceData.getQ_bit3()));
        model.addAttribute("deviceData", deviceData);
        return PREFIX + "parameter-settings";
    }


    @GetMapping("/data-curves")
    public String dataCurves(Model model, HttpSession session) {
        Device deviceSession = (Device) session.getAttribute("device");
        String gatewayId = deviceSession.getGatewayId();
        String deviceId = deviceSession.getDeviceId();
        LambdaQueryWrapper<Device> deviceLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceLambdaQueryWrapper.eq(Device::getGatewayId, gatewayId)
                .eq(Device::getDeviceId, deviceId);
        Device device = deviceService.getOne(deviceLambdaQueryWrapper);
        List<Double> tempReadList = new ArrayList<>();
        List<Double> humiReadList = new ArrayList<>();
        List<String> timeList = new ArrayList<>();
        LambdaQueryWrapper<DeviceData> deviceDataLambdaQueryWrapper = new LambdaQueryWrapper<>();
        deviceDataLambdaQueryWrapper.eq(DeviceData::getDId, device.getId())
                .orderByDesc(DeviceData::getTime)
                .last("limit 10");
        List<DeviceData> deviceDataList = deviceDataService.list(deviceDataLambdaQueryWrapper);
        for (DeviceData deviceData : deviceDataList) {
            tempReadList.add(deviceData.getTemp_read());
            humiReadList.add(deviceData.getHumi_read());
            //去掉LocalDateTime中的T
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime date = deviceData.getTime();
            date = LocalDateTime.of(date.getYear(), date.getMonth(), date.getDayOfMonth(),
                    date.getHour(), date.getMinute(), date.getSecond());
            String time = date.format(dateTimeFormatter);
            timeList.add(time);
        }

        model.addAttribute("tempReadList", tempReadList);
        model.addAttribute("humiReadList", humiReadList);
        model.addAttribute("timeList", timeList);

        return PREFIX + "data-curves";
    }

    @GetMapping("/alarm-records")
    public String alarmRecords() {
        return PREFIX + "alarm-records";
    }

    @GetMapping("/alarmRecords/page")
    @ResponseBody
    public BasePageVo alarmRecordsPage(@RequestParam("page") Long page,
                                       @RequestParam("limit") Long limit,
                                       HttpSession session) {
        Device deviceSession = (Device) session.getAttribute("device");
        String gatewayId = deviceSession.getGatewayId();
        LambdaQueryWrapper<FacilityAlarm> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FacilityAlarm::getGatewayId, gatewayId);
        Page<FacilityAlarm> pageInfo = new Page<>(page, limit);
        pageInfo = facilityAlarmService.page(pageInfo, queryWrapper);
        BasePageVo pageVo = BasePageVoUtil.getPageVo(pageInfo);
        return pageVo;
    }

    @GetMapping("/alarmRecords/search")
    @ResponseBody
    public BasePageVo alarmRecordsSearch(@RequestParam("page") Long page,
                                         @RequestParam("limit") Long limit,
                                         FacilityAlarm facilityAlarm,
                                         HttpSession session) {
        Device deviceSession = (Device) session.getAttribute("device");
        String gatewayId = deviceSession.getGatewayId();
        String startTime = facilityAlarm.getStartTime();
        String endTime = facilityAlarm.getEndTime();

        Page<FacilityAlarm> pageInfo = new Page<>(page, limit);

        LambdaQueryWrapper<FacilityAlarm> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.between(FacilityAlarm::getTime, startTime, endTime)
                .eq(FacilityAlarm::getGatewayId, gatewayId);
        pageInfo = facilityAlarmService.page(pageInfo, queryWrapper);

        BasePageVo pageVo = BasePageVoUtil.getPageVo(pageInfo);
        return pageVo;
    }

    @PostMapping("/alarmRecords/status")
    @ResponseBody
    public Result alarmRecordsUpdateStatus(@RequestBody FacilityAlarm facilityAlarm) {
        facilityAlarm.setStatus(AirconditionerStatus.STATUS_HANDLED);
        facilityAlarmService.updateById(facilityAlarm);
        return Result.success("修改成功");
    }

    @PostMapping("/alarmRecords/delete")
    @ResponseBody
    public Result alarmRecordsStatus(@RequestBody FacilityAlarm facilityAlarm) {
        facilityAlarmService.removeById(facilityAlarm);
        return Result.success();
    }

    @PostMapping("/parameterSetting/update")
    @ResponseBody
    public Result parameterSettingUpdate(@RequestBody DeviceData deviceData,
                                         HttpSession session) throws Exception {
        Device deviceSession = (Device) session.getAttribute("device");

        LambdaQueryWrapper<DeviceData> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(DeviceData::getDId, deviceSession.getId())
                .orderByDesc(DeviceData::getTime).last("limit 1");
        DeviceData deviceDataReplaced = deviceDataService.getOne(lambdaQueryWrapper);
        if (deviceData.getM_bit1() != null) {
            deviceData.setM_bit1(deviceDataReplaced.getM_bit1() + deviceData.getM_bit1());
        }
        if (deviceData.getM_bit2() != null) {
            deviceData.setM_bit2(deviceDataReplaced.getM_bit2() + deviceData.getM_bit2());
        }
        if (deviceData.getQ_bit3() != null) {
            deviceData.setQ_bit3(deviceDataReplaced.getQ_bit3() + deviceData.getQ_bit3());
        }

        System.out.println(deviceData);
        Gson gson = new GsonBuilder().create();
        String deviceDataJsonStr = gson.toJson(deviceData);
        String deviceId = deviceSession.getDeviceId();
        //获取要发送到的空调名作为前缀
        String prefix = deviceSession.getPrefix();
        JsonObject deviceDataJson = JsonParser.parseString(deviceDataJsonStr).getAsJsonObject();
        JsonObject deviceDataJsonSend = new JsonObject();
        //遍历json，给key添加前缀，放入到deviceDataJsonSend中
        for (Map.Entry<String, JsonElement> stringJsonElementEntry : deviceDataJson.entrySet()) {
            String key = stringJsonElementEntry.getKey();
            JsonElement value = stringJsonElementEntry.getValue();
            deviceDataJsonSend.add(prefix + key, value);
        }

        //固定json格式
        JsonObject jsonObject = new JsonObject();
        JsonArray devices = new JsonArray();
        JsonObject device = new JsonObject();
        device.add("deviceId", JsonParser.parseString(deviceId));
        device.add("deviceData", JsonParser.parseString(deviceDataJsonSend.toString()));
        devices.add(device);
        jsonObject.add("devices", devices);
//        mqttProducer.send("mqtt/" + deviceSession.getCompanyName() + "/" + deviceSession.getGatewayId() + "/plc_cmd"
//                , jsonObject.toString());
        String topic = "mqtt/" + deviceSession.getCompanyName() + "/" + deviceSession.getGatewayId() + "/plc_cmd";

        log.info("开始发送mqtt消息,主题：{},消息：{}", topic, jsonObject.toString());
//        if (StringUtils.isNotBlank(topic)) {
//            mqttProducer.send(topic, data);
            ((MqttPahoMessageHandler) mqttOutbound).setDefaultTopic(topic);
            mqttGateway.sendToMqtt(jsonObject.toString());
            log.info("发送mqtt消息完成,主题：{},消息：{}", topic, jsonObject.toString());
//            return Result.success("execute successful");
//        } else {
//            return Result.error("topic is blank");
//        }

        return Result.success(jsonObject.toString());
    }

}
