package com.example.iot_controlhost.utils.control;

import com.blankj.utilcode.constant.TimeConstants;
import com.blankj.utilcode.util.StringUtils;
import com.blankj.utilcode.util.TimeUtils;
import com.example.iot_controlhost.R;
import com.example.iot_controlhost.model.Device;
import com.example.iot_controlhost.model.DeviceErrorInfo;
import com.example.iot_controlhost.model.controller.AirConditionInfrared;
import com.example.iot_controlhost.model.controller.relay.Relay;
import com.example.iot_controlhost.model.sensor.GasSensor;
import com.example.iot_controlhost.model.sensor.THSensor;
import com.example.iot_controlhost.utils.MyUtil;
import com.example.iot_controlhost.utils.PollingTask;
import com.example.iot_controlhost.utils.control.infrared.MyInfraredUtils;
import com.example.iot_controlhost.utils.database.SilkwormDBManager;
import com.example.iot_controlhost.utils.global.AIModelSet;
import com.example.iot_controlhost.utils.global.AutoModelSet;
import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.global.RoomSensor;
import com.example.iot_controlhost.utils.global.RoomSetting;
import com.example.iot_controlhost.utils.global.RxTag;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog;
import com.example.iot_controlhost.utils.network.PublicNetRequest;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 异常检测-预警
 */
public class DeviceAnomalyDetection {
    private static DeviceAnomalyDetection instance;
    /**
     * 上次温度预警时间 为null则表示无异常
     */
    Date lastTemperatureWarningTime;
    /**
     * 上次湿度预警时间 为null则表示无异常
     */
    Date lastHumidityWarningTime;

    public static void init() {
        instance = new DeviceAnomalyDetection();
    }

    public static DeviceAnomalyDetection getInstance() {
        return instance;
    }

    /**
     * 设备出错信息
     * 0室内温度传感器 1室内湿度传感器 2地暖
     * 3室内湿度控制设备 4 室内温度控制设备
     */
    List<DeviceErrorInfo> deviceErrorInfos;
    /**
     * 继电器下设备出错信息
     */
    List<DeviceErrorInfo> relayInfos;

    PollingTask pollingTask;

    private DeviceAnomalyDetection() {
        pollingTask = PollingTask.getInstance();
        relayInfos = new ArrayList<>();
        for (Relay relay : RoomController.getAllRelayDevice()) {
            relayInfos.add(new DeviceErrorInfo(relay.getName(), 0, R.mipmap.warning_other));
        }
        deviceErrorInfos = new ArrayList<>();
        deviceErrorInfos.add(new DeviceErrorInfo("温度计疑似故障", RoomSensor.getAverageIndoorTemperature(), R.mipmap.warning_sensor));
        deviceErrorInfos.add(new DeviceErrorInfo("湿度计疑似故障", RoomSensor.getAverageIndoorHumidity(), R.mipmap.warning_sensor));
        deviceErrorInfos.add(new DeviceErrorInfo("加湿器疑似故障", RoomSensor.getAverageIndoorHumidity(), R.mipmap.warning_humidifier));
        deviceErrorInfos.add(new DeviceErrorInfo("空调疑似故障", RoomSensor.getAverageIndoorTemperature(), R.mipmap.warning_air_conditioning));
        deviceErrorInfos.add(new DeviceErrorInfo(RoomSetting.getFloorName() + "疑似故障", RoomSensor.getFloorTemperature(), R.mipmap.warning_floor));
        deviceErrorInfos.add(new DeviceErrorInfo("换气扇疑似故障", 0, R.mipmap.warning_ventilator));
        deviceErrorInfos.add(new DeviceErrorInfo("设备连接故障", 0, R.mipmap.warning_other));
        pollingTask.startPollingTaskOnIOThread(RxTag.DEVICE_ANOMALY_DETECTION, Variable.DEVICE_ANOMALY_CHECK_TIME, () -> {
            if (RoomSetting.getMode() == 1) {
                //检查硬性错误-全部设备
                checkError(deviceErrorInfos.get(6));
                if (SilkwormDBManager.isStarting()) {
                    //检查疑似错误-室内温度计
                    checkDoubtErrorT(deviceErrorInfos.get(0));
                    //检查疑似错误-室内湿度计
                    checkDoubtErrorH(deviceErrorInfos.get(1));
                    //检查疑似错误-换气控制设备-换气扇
                    checkDoubtErrorVentilator(deviceErrorInfos.get(5));
                    //检查疑似错误-继电器设备-采用BBIT型号继电器
                    if (RoomController.relay.getModel().equals(RoomController.relay.getModels()[2])) {
                        checkRelaySensor();
                    }
                    // 非手动模式
                    if (RoomSetting.getMode() != 0) {
                        double targetTemperature = AutoModelSet.getTargetTemperature();
                        double targetHumidity = AutoModelSet.getTargetHumidity();
                        double minTemperature;
                        double maxTemperature;
                        double minHumidity;
                        double maxHumidity;
                        if (RoomSetting.getMode() == 1) {
                            minTemperature = AutoModelSet.getMinTemperature();
                            maxTemperature = AutoModelSet.getMaxTemperature();
                            minHumidity = AutoModelSet.getMinHumidity();
                            maxHumidity = AutoModelSet.getMaxHumidity();
                        } else {
                            // 智能模式 || 其他情况
                            minTemperature = AIModelSet.getTargetTemperature() - 2;
                            maxTemperature = AIModelSet.getTargetTemperature() + 2;
                            minHumidity = AIModelSet.getTargetHumidity() - 15;
                            maxHumidity = AIModelSet.getTargetHumidity() + 15;
                        }
                        //检查疑似错误-湿度控制设备-加湿器
                        checkDoubtErrorHumidity(deviceErrorInfos.get(2), maxHumidity, minHumidity);
                        //检查疑似错误-温度控制设备-空调、地暖
                        checkDoubtErrorTemperature(deviceErrorInfos.get(3), deviceErrorInfos.get(4), maxTemperature, minTemperature);
                        //检查-室内温湿度-自动或智能模式下是否持续超出预警限度（持续超过最高最低值）
                        warningTH(targetTemperature, maxTemperature, minTemperature, targetHumidity, maxHumidity, minHumidity);
                    }
                }
            }
        });
    }

    /**
     * 检查硬性错误-全部设备
     */
    private void checkError(DeviceErrorInfo deviceErrorInfo) {
        deviceErrorInfo.setErrorMessage(null);
        StringBuffer reportName = new StringBuffer("");
        StringBuffer reportMessage = new StringBuffer("");
        for (Device device : RoomController.getAllDevice()) {
            if (device.getFailTimes() > Variable.WARNING_REPORT_ERROR_TIMES) {
                reportName.append(device.getName() + ";");
                reportMessage.append("<" + device.getName() + ">连续操作失败" + device.getFailTimes() + "次\n");
            }
        }
//        if (!StringUtils.isEmpty(reportName.toString())) {
//            PublicNetRequest.AnomalyNotice("存在设备连续操作失败", 2, 3, reportMessage.toString());
//        }
        // 一旦发现错误，立即发送通知   （此处设为5，为checkNotice()中+1操作后能直接识别为符合语音报警次数）
        deviceErrorInfo.setErrorTimes(DeviceErrorInfo.errorCheckTimes2 - 1);
        deviceErrorInfo.setErrorMessage(reportMessage.toString());
        deviceErrorInfo.checkNotice(2);
    }

    /**
     * 检查疑似错误-室内温度传感器
     */
    private void checkDoubtErrorT(DeviceErrorInfo t) {
        List<DeviceErrorInfo> indoorT = new ArrayList<>();
        for (THSensor sensor : RoomSensor.dynamicTHSensorList) {
            indoorT.add(new DeviceErrorInfo(sensor.getName(), sensor.temperature));
        }
        for (GasSensor sensor : RoomSensor.dynamicCO2SensorList) {
            indoorT.add(new DeviceErrorInfo(sensor.getName(), sensor.getTemperature()));
        }
        hasAnyDifferenceGreaterThan("温度", 4, indoorT, "温度差大于4度，疑似存在故障", t);
    }

    /**
     * 检查疑似错误-室内湿度传感器
     */
    private void checkDoubtErrorH(DeviceErrorInfo h) {
        List<DeviceErrorInfo> indoorH = new ArrayList<>();
        for (THSensor sensor : RoomSensor.dynamicTHSensorList) {
            indoorH.add(new DeviceErrorInfo(sensor.getName(), sensor.humidity));
        }
        for (GasSensor sensor : RoomSensor.dynamicCO2SensorList) {
            indoorH.add(new DeviceErrorInfo(sensor.getName(), sensor.getHumidity()));
        }
        hasAnyDifferenceGreaterThan("湿度", 10, indoorH, "湿度差大于10%，疑似存在故障", h);
    }

    /**
     * 检查疑似错误-室内湿度控制设备
     */
    private void checkDoubtErrorHumidity(DeviceErrorInfo humidity, double maxHumidity, double minHumidity) {
        humidity.setErrorMessage(null);
        double curHumidity = RoomSensor.getAverageIndoorHumidity();
        boolean curState = RoomController.humidifier.isPowerSupply();
        if (curHumidity < minHumidity || curHumidity > maxHumidity) {
            //湿度异常 检测可能的异常
            if (curState && humidity.isOpen() && curHumidity <= humidity.getValue()) {
                humidity.setErrorMessage("加湿器已开启，但湿度未按预期上升，疑似加湿器存在故障");
            }
            if (!curState && !humidity.isOpen() && curHumidity >= humidity.getValue() + 1) {
                humidity.setErrorMessage("加湿器已关闭，但湿度仍持续上升，疑似加湿器存在故障");
            }
        }
        humidity.checkNotice();
        humidity.setOpen(curState);
        humidity.setValue(curHumidity);
    }

    /**
     * 检查疑似错误-室内温度控制设备-空调、地暖
     */
    private void checkDoubtErrorTemperature(DeviceErrorInfo air, DeviceErrorInfo floor, double maxTemperature, double minTemperature) {
        air.setErrorMessage(null);
        floor.setErrorMessage(null);

        //当前参数-空调
        boolean curAirState = MyInfraredUtils.getInstances().isPowerSupply();
        int curAirMode = MyInfraredUtils.getInstances().getMode();
        double curAirTemperature = RoomSensor.getAverageIndoorTemperature();
        //当前参数-地暖
        boolean curFloorState = RoomController.floor.isPowerSupply();
        //之前参数-空调
        boolean lastAirState = air.isOpen();
        int lastAirMode = air.getOperateType();
        double lastAirTemperature = air.getValue();
        //之前参数-地暖
        boolean lastFloorState = floor.isOpen();

        if (curAirTemperature < minTemperature || curAirTemperature > maxTemperature) {
            //温度异常 检测可能的异常
            if (!lastAirState && !curAirState && !lastFloorState && !curFloorState && Math.abs(curAirTemperature - lastAirTemperature) > 1.5) {
                //之前设备全关 && 现在设备全关 && 温度变化超过1.5度
                floor.setErrorMessage("升温设备全关，但温度持续上升，疑似空调故障");
                air.setErrorMessage("升温设备全关，但温度持续上升，疑似" + RoomSetting.getFloorName() + "故障");
            }
            if (lastAirState && curAirState && lastAirMode == 2 && curAirMode == 2 && curAirTemperature > lastAirTemperature) {
                //之前空调开制冷 && 现在空调开制冷  && 现在温度大于之前
                air.setErrorMessage("空调已开启制冷，但温度未按预期下降，疑似空调故障");
            }
            if (lastAirState && curAirState && lastAirMode == 0 && curAirMode == 0 && curAirTemperature < lastAirTemperature) {
                //之前空调开制热 && 现在空调开制热 && 现在温度小于之前
                air.setErrorMessage("空调已开启制热，但温度未按预期上升，疑似空调故障");
            }
            //如果地面温度传感器可用
            if (!RoomSensor.dynamicFloorTSensorList.isEmpty()) {
                //之前地暖关着 && 现在地暖关着 && (地面温度上升 &&(地面温度保持 && 地面温度 > 30))
                double curFloorT = RoomSensor.getFloorTemperature();
                double lastFloorT = floor.getValue();
                if (!lastFloorState && !curFloorState && curFloorT > lastFloorT + 0.3) {
                    //之前地暖关着 && 现在地暖关着 && 地面温度上升
                    floor.setErrorMessage("地暖已关闭，但地面温度持续上升，疑似地暖故障");
                }
                if (!lastFloorState && !curFloorState && curFloorT > 30) {
                    //之前地暖关着 && 现在地暖关着 && 地面温度保持30以上
                    floor.setErrorMessage("地暖已关闭，但地面温度仍保持在30度以上，疑似地暖故障");
                }
            }
            if (lastFloorState && curFloorState && curAirTemperature < lastAirTemperature - 1) {
                //之前地暖开着 && 现在地暖开着 && 现在温度小于之前1度 （因为地暖有时只起保暖作用，所以可能存在地暖开启，但现在温度依然低于之前的情况）
                floor.setErrorMessage("地暖已开启，但温度仍持续下降，疑似地暖故障");
            }
            if (RoomSensor.consumptionSensor.isAvailable() && RoomController.floor.isAvailable() &&  //能耗传感器可用
                    RoomSensor.consumptionSensor.getPower() != 0 &&
                    RoomSensor.consumptionSensor.getPower() < RoomController.floor.getPwm(RoomController.floor.getGear()).getPower() - 200) {//当前功率小于地暖应有功率
                floor.setErrorMessage("地暖已开启，但当前功率小于地暖应有功率温度，疑似地暖故障");
            }
        }
        floor.checkNotice();
        air.checkNotice();
        //更新状态
        air.setOpen(curAirState);
        air.setValue(curAirTemperature);
        air.setOperateType(curAirMode);
        floor.setOpen(curFloorState);
        floor.setValue(RoomSensor.getFloorTemperature());
    }

    /**
     * 检查疑似错误-室内换气控制设备-换气扇
     */
    private void checkDoubtErrorVentilator(DeviceErrorInfo ventilator) {
        ventilator.setErrorMessage(null);
        if (RoomSensor.dynamicCO2SensorList.isEmpty() || !RoomController.fan.isAvailable()) {
            //CO2传感器、换气扇均不可用
            return;
        }
        boolean curState = RoomController.fan.isPowerSupply();
        double curCO2 = RoomSensor.dynamicCO2SensorList.stream().mapToDouble(GasSensor::getValue).average().orElse(0.0);
        if (curState && ventilator.isOpen() && curCO2 >= ventilator.getValue()) {
            ventilator.setErrorMessage("换气扇已开启，但CO2浓度未下降，疑似换气扇故障");
        }
        ventilator.checkNotice();
        ventilator.setOpen(curState);
        ventilator.setValue(curCO2);
    }

    /**
     * 检查疑似错误-新型继电器下的设备
     * 采用BBIT型号继电器，可以通过判断每个端口的功率来判断是否存在疑似故障
     */
    public void checkRelaySensor() {
        for (DeviceErrorInfo info : relayInfos) {
            for (Relay relay : RoomController.getAllAvailableRelays()) {
                if (info.getName().equals(relay.getName())) {
                    if (relay.isPowerSupply() && relay.getPower() < 50
                            && (!relay.getName().equals(RoomController.DEVICE_NAME[1]) || MyInfraredUtils.getInstances().isPowerSupply())) {
                        //已开启，功率少于50W 且（不是空调或 是空调并且红外已开启）
                        info.setErrorMessage(relay.getName() + "已开启，但功率少于50W");
                    } else if (!relay.isPowerSupply() && relay.getPower() >= 50
                            && (!relay.getName().equals(RoomController.DEVICE_NAME[1]) || !MyInfraredUtils.getInstances().isPowerSupply())) {
                        //已关闭，功率大于50W 且（不是空调或 是空调并且红外已关闭）
                        info.setErrorMessage(relay.getName() + "已关闭，但功率大于50W");
                    } else {
                        // 正常 清除错误消息
                        info.setErrorMessage(null);
                    }
                    info.checkNotice(3);
                }
            }
        }

    }

    /**
     * 判断是否室内温湿度传感器是否“疑似故障”
     *
     * @param threshold       波动值
     * @param values          待比较的传感器
     * @param tip             提示值
     * @param deviceErrorInfo 设备类型
     */
    private void hasAnyDifferenceGreaterThan(String type, double threshold, List<DeviceErrorInfo> values, String tip, DeviceErrorInfo deviceErrorInfo) {
        deviceErrorInfo.setErrorMessage(null);
        if (values.size() < 2) {
            // 如果提供的参数不足两个，无法比较差值，返回 false
            deviceErrorInfo.clearErrorTimes();
            return;
        }
        for (int i = 0; i < values.size(); i++) {
            for (int j = i + 1; j < values.size(); j++) {
                DeviceErrorInfo x = values.get(i);
                DeviceErrorInfo y = values.get(j);
                if (Math.abs(x.getValue() - y.getValue()) > threshold) {
                    // 存在任意两个值之间的差值超过指定值
                    deviceErrorInfo.setErrorMessage(x.getName() + "与" + y.getName() + tip);
                }
                // 有任意值为0
                if (x.getValue() == 0 || y.getValue() == 0) {
                    deviceErrorInfo.setErrorMessage((x.getValue() == 0 ? x : y).getName() + type + "值为0，请确认是否正常");
                }
            }
        }
        deviceErrorInfo.checkNotice(1);
    }

    /**
     * 温湿度预警判断
     */
    private void warningTH(double targetTemperature, double maxTemperature, double minTemperature,
                           double targetHumidity, double maxHumidity, double minHumidity) {
        try {
            double curTemperature = RoomSensor.getAverageIndoorTemperature();
            StringBuffer temperatureStr = new StringBuffer(curTemperature + " ℃ ");
            double curHumidity = RoomSensor.getAverageIndoorHumidity();
            StringBuffer HumidityStr = new StringBuffer(curHumidity + " % ");
            StringBuffer remarks = new StringBuffer("");
            boolean needReport = false;
            //温度异常
            if (curTemperature < minTemperature || curTemperature > maxTemperature) {
                if (lastTemperatureWarningTime == null) {
                    //没记录过，记录开始时间
                    MyLog.warningError("（首次）室内温度有误，记录开始异常时间：" + MyUtil.getDateTime());
                    lastTemperatureWarningTime = new Date();
                } else {
                    String tip = (curTemperature < minTemperature ? "温度偏低" : "温度偏高") + "-目标【" + targetTemperature + "℃】";
                    needReport = true;
                    MyLog.warningError(tip);
                    temperatureStr.append(tip);
                    remarks.append("温度已异常" + TimeUtils.getTimeSpan(new Date(), lastTemperatureWarningTime, TimeConstants.MIN) + "分钟，");
                }
            } else {
                MyLog.warning("室内温度无误，初始化异常记录时间");
                lastTemperatureWarningTime = null;
            }
            //湿度异常
            if (curHumidity < minHumidity || curHumidity > maxHumidity) {
                if (lastHumidityWarningTime == null) {
                    MyLog.warning("室内湿度有误，记录异常开始时间：" + MyUtil.getDateTime());
                    //没记录过，记录开始时间
                    lastHumidityWarningTime = new Date();
                } else {
                    needReport = true;
                    HumidityStr.append((curHumidity < minHumidity ? "湿度偏低" : "湿度偏高") + "-目标【" + targetHumidity + "%】");
                    remarks.append("湿度已异常" + TimeUtils.getTimeSpan(new Date(), lastHumidityWarningTime, TimeConstants.MIN) + "分钟，");
                }
            } else {
                MyLog.warning("室内湿度无误，初始化异常记录时间");
                lastHumidityWarningTime = null;
            }
            if (needReport) {
                long tempTimeBias = 0;
                long humTimeBias = 0;
                if (lastTemperatureWarningTime != null) {
                    tempTimeBias = TimeUtils.getTimeSpan(new Date(), lastTemperatureWarningTime, TimeConstants.MIN);
                }
                if (lastHumidityWarningTime != null) {
                    humTimeBias = TimeUtils.getTimeSpan(new Date(), lastHumidityWarningTime, TimeConstants.MIN);
                }
                if (tempTimeBias > Variable.WARNING_REPORT_TIME_INTERVAL_VOICE || humTimeBias > Variable.WARNING_REPORT_TIME_INTERVAL_VOICE) {
                    // 公众号+语音预警 需要优先判断是否超出语音预警时间
                    PublicNetRequest.AnomalyNotice("室内温湿度", 1, 3, remarks.toString());
                    // 发送语音后重置预警检测
                    lastTemperatureWarningTime = null;
                    lastHumidityWarningTime = null;
                } else if (tempTimeBias > Variable.WARNING_REPORT_TIME_INTERVAL || humTimeBias > Variable.WARNING_REPORT_TIME_INTERVAL) {
                    // 公众号预警
                    PublicNetRequest.THWarningToWeChat(temperatureStr.toString(), HumidityStr.toString(), remarks.toString());
                } else {
                    MyLog.warning("预警无需发送：室内温湿度有误，但未到指定错误持续时间");
                }
            } else {
                MyLog.warning("预警无需发送：室内温湿度无误");
            }
        } catch (Exception e) {
            MyLog.warningError("发送温湿度预警信息异常:" + e.getMessage());
        }
    }

}
