package com.example.iot_controlhost.model.controller;

import com.blankj.utilcode.util.StringUtils;
import com.example.iot_controlhost.base.SetAddress;
import com.example.iot_controlhost.utils.MMKVUtil;
import com.example.iot_controlhost.utils.global.RoomController;
import com.example.iot_controlhost.utils.global.Variable;
import com.example.iot_controlhost.utils.log.MyLog;

/**
 * 西奈 485 除湿机控制器。
 */
public class Dehumidifier485 extends Controller implements SetAddress {

    public static final int MODE_DEHUMIDIFY = 0;
    public static final int MODE_VENTILATION = 1;

    public static final int FAN_LOW = 0;
    public static final int FAN_MIDDLE = 1;
    public static final int FAN_HIGH = 2;

    public static final String[] BAUD_LIST = {"4800", "9600", "19200", "115200"};
    public static final String[] FAN_SPEED_NAMES = {"低风", "中风", "高风"};
    public static final String[] WORK_MODE_NAMES = {"除湿", "通风"};
    public static final String[] ADDRESS_LIST = buildAddressList();

    private static final int REG_POWER = 0x0000;
    private static final int REG_TARGET_HUMIDITY = 0x0001;
    private static final int REG_TARGET_TEMPERATURE = 0x0002;
    private static final int REG_FAN_SPEED = 0x0003;
    private static final int REG_WORK_MODE = 0x0004;
    private static final int REG_SET_ADDRESS = 0x0009;
    private static final int REG_SET_BAUD = 0x000E;
    private static final int REG_BROADCAST_POWER = 0x00EE;
    private static final long POWER_ON_SERIAL_BUFFER_CLEAR_MS = 500;

    private final String TARGET_HUMIDITY = name + "targetHumidity";
    private final String TARGET_TEMPERATURE = name + "targetTemperature";
    private final String FAN_SPEED = name + "fanSpeed";
    private final String WORK_MODE = name + "workMode";

    private Integer environmentHumidity;
    private Integer environmentTemperature;
    private Integer coilTemperature1;
    private Integer coilTemperature2;
    private int workStatus;
    private int faultCode;
    private String lastTestState = "未测试";

    public Dehumidifier485(String name) {
        super(name);
    }

    public String getDisplayName() {
        return "485除湿";
    }

    @Override
    public boolean setPowerSupply(boolean state) {
        if (!isAvailable()) {
            MyLog.controllerError("485除湿机：未正确配置但尝试开关");
            return false;
        }
        boolean result;
        if (state) {
            if (!RoomController.dehumidifier485Relay.isPowerSupply()) {
                MyLog.controller("485除湿机物理电源当前为关闭状态，现开启485除湿机物理开关");
                if (RoomController.dehumidifier485Relay.setPowerSupply(true)) {
                    MyLog.controller("485除湿机物理电源打开成功，等待" + Variable.WAIT_START_485_DE / 1000 + "s后发送485开机指令");
                    try {
                        Thread.sleep(Variable.WAIT_START_485_DE);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return false;
                    }
                    clearPowerOnSerialBuffer();
                } else {
                    MyLog.controllerError("485除湿机物理电源打开失败");
                    return false;
                }
            }
            MyLog.controller("485除湿机：开机");
            result = writePowerState(true);
        } else {
            MyLog.controller("485除湿机：关机");
            result = writePowerState(false);
        }
        if (result) {
            powerSupply = state;
            if (!state) {
                RoomController.doNotNeedDelayCloseDehumidifier485 = false;
            }
        }
        return result;
    }

    @Override
    public boolean isPowerSupply() {
        return super.isPowerSupply() && RoomController.dehumidifier485Relay.isPowerSupply();
    }

    public boolean setTargetHumidity(int humidity) {
        if (humidity < 5 || humidity > 99) {
            return false;
        }
        boolean result = writeRegister(getAddress(), REG_TARGET_HUMIDITY, humidity, true);
        if (result) {
            setTargetHumidityCache(humidity);
        }
        return result;
    }

    public int getTargetHumidity() {
        return MMKVUtil.get(TARGET_HUMIDITY, 60);
    }

    public void setTargetHumidityCache(int humidity) {
        MMKVUtil.put(TARGET_HUMIDITY, limit(humidity, 5, 99));
    }

    public boolean setTargetTemperature(int temperature) {
        if (temperature < 5 || temperature > 70) {
            return false;
        }
        boolean result = writeRegister(getAddress(), REG_TARGET_TEMPERATURE, temperature, true);
        if (result) {
            setTargetTemperatureCache(temperature);
        }
        return result;
    }

    public int getTargetTemperature() {
        return MMKVUtil.get(TARGET_TEMPERATURE, 26);
    }

    public void setTargetTemperatureCache(int temperature) {
        MMKVUtil.put(TARGET_TEMPERATURE, limit(temperature, 5, 70));
    }

    public boolean setFanSpeed(int fanSpeed) {
        if (fanSpeed < FAN_LOW || fanSpeed > FAN_HIGH) {
            return false;
        }
        boolean result = writeRegister(getAddress(), REG_FAN_SPEED, fanSpeed, true);
        if (result) {
            setFanSpeedCache(fanSpeed);
        }
        return result;
    }

    public int getFanSpeed() {
        return MMKVUtil.get(FAN_SPEED, FAN_HIGH);
    }

    public void setFanSpeedCache(int fanSpeed) {
        MMKVUtil.put(FAN_SPEED, limit(fanSpeed, FAN_LOW, FAN_HIGH));
    }

    public String getFanSpeedName() {
        int fanSpeed = getFanSpeed();
        return fanSpeed >= FAN_LOW && fanSpeed <= FAN_HIGH ? FAN_SPEED_NAMES[fanSpeed] : "未知";
    }

    public boolean setWorkMode(int workMode) {
        if (workMode < MODE_DEHUMIDIFY || workMode > MODE_VENTILATION) {
            return false;
        }
        boolean result = writeRegister(getAddress(), REG_WORK_MODE, workMode, true);
        if (result) {
            setWorkModeCache(workMode);
        }
        return result;
    }

    public int getWorkMode() {
        return MMKVUtil.get(WORK_MODE, MODE_DEHUMIDIFY);
    }

    public void setWorkModeCache(int workMode) {
        MMKVUtil.put(WORK_MODE, limit(workMode, MODE_DEHUMIDIFY, MODE_VENTILATION));
    }

    public String getWorkModeName() {
        int workMode = getWorkMode();
        return workMode >= MODE_DEHUMIDIFY && workMode <= MODE_VENTILATION ? WORK_MODE_NAMES[workMode] : "未知";
    }

    public boolean readState() {
        if (!isAvailable() || "00".equals(getAddress())) {
            return false;
        }
        return readState(getAddress());
    }

    public boolean readState(String address) {
        if (StringUtils.isEmpty(address) || "00".equals(address)) {
            lastTestState = "00 地址用于广播或读取设备地址，不能读取运行状态";
            return false;
        }
        String str = address + "0300000014";
        String instruction = str + getCRC(str);
        String result = serialPortUtil.sendAndReceiveSerialData(this, instruction);
        if (StringUtils.isEmpty(result)) {
            lastTestState = "读取状态失败";
            return false;
        }
        boolean parsed = parseState(result);
        lastTestState = parsed ? getDetailState() : "状态数据解析失败";
        return parsed;
    }

    public String readCurrentAddress() {
        String str = "002500DF0001";
        String instruction = str + getCRC(str);
        String result = serialPortUtil.sendAndReceiveSerialData(this, instruction);
        if (StringUtils.isEmpty(result) || result.length() < 12) {
            return null;
        }
        try {
            return result.substring(6, 8);
        } catch (Exception e) {
            return null;
        }
    }

    public String getState() {
        if (!isAvailable()) {
            return "未正确配置";
        }
        if (!isPowerSupply()) {
            return "未启动";
        }
        String humidityText = environmentHumidity == null ? "" : " 湿度" + environmentHumidity + "%";
        return "运行中 " + getWorkModeName() + humidityText + " 目标" + getTargetHumidity() + "%";
    }

    public String getDetailState() {
        return "电源：" + (isPowerSupply() ? "开启" : "关闭")
                + "\n目标湿度：" + getTargetHumidity() + "%"
                + "\n目标温度：" + getTargetTemperature() + "℃"
                + "\n风速：" + getFanSpeedName()
                + "\n模式：" + getWorkModeName()
                + "\n环境湿度：" + getEnvironmentHumidityText()
                + "\n环境温度：" + getEnvironmentTemperatureText()
                + "\n工作状态：" + getWorkStatusText()
                + "\n故障：" + getFaultText();
    }

    public String getEnvironmentHumidityText() {
        return environmentHumidity == null ? "--" : environmentHumidity + "%";
    }

    public String getEnvironmentTemperatureText() {
        return environmentTemperature == null ? "--" : environmentTemperature + "℃";
    }

    public String getFaultText() {
        if (faultCode == 0) {
            return "正常";
        }
        StringBuilder builder = new StringBuilder();
        appendFault(builder, 0, "温湿度传感器故障");
        appendFault(builder, 1, "化霜温度1故障");
        appendFault(builder, 2, "化霜温度2故障");
        appendFault(builder, 3, "高压保护");
        appendFault(builder, 4, "低压保护");
        appendFault(builder, 5, "相序保护");
        appendFault(builder, 6, "缺相保护");
        appendFault(builder, 7, "超温保护");
        appendFault(builder, 8, "电源电压超高保护");
        appendFault(builder, 9, "水满告警");
        appendFault(builder, 10, "漏氟保护");
        return builder.length() == 0 ? "未知故障：" + faultCode : builder.toString();
    }

    public String getWorkStatusText() {
        if (workStatus == 0) {
            return "运行中";
        }
        StringBuilder builder = new StringBuilder();
        appendStatus(builder, 0, "除湿");
        appendStatus(builder, 1, "化霜");
        appendStatus(builder, 2, "制冷");
        appendStatus(builder, 3, "制热");
        return builder.length() == 0 ? "运行中" : builder.toString();
    }

    @Override
    public boolean isAvailable() {
        return isEnable() && !StringUtils.isEmpty(getAddress()) && RoomController.dehumidifier485Relay.isAvailable();
    }

    @Override
    public String[] getModels() {
        return new String[]{"西奈 Z101/Z102/Z103/X86 485除湿机"};
    }

    @Override
    public boolean setAddressToNewAddress(String model, String oldAddress, String oldBaud, String newAddress) {
        if (!model.equals(getModels()[0])) {
            return false;
        }
        int addressValue = parseHex(newAddress, -1);
        if (addressValue < 1 || addressValue > 99) {
            return false;
        }
        if ("00".equals(oldAddress)) {
            return writeRegisterNoResponse(oldAddress, REG_SET_ADDRESS, addressValue);
        }
        return writeRegister(oldAddress, REG_SET_ADDRESS, addressValue, false);
    }

    @Override
    public boolean setBaudToNewBaud(String model, String oldAddress, String newBaud) {
        if (!model.equals(getModels()[0])) {
            return false;
        }
        int baudValue;
        switch (newBaud) {
            case "4800":
                baudValue = 1;
                break;
            case "9600":
                baudValue = 2;
                break;
            case "19200":
                baudValue = 3;
                break;
            case "115200":
                baudValue = 4;
                break;
            default:
                return false;
        }
        if ("00".equals(oldAddress)) {
            return writeRegisterNoResponse(oldAddress, REG_SET_BAUD, baudValue);
        }
        return writeRegister(oldAddress, REG_SET_BAUD, baudValue, false);
    }

    @Override
    public boolean test(String deviceModel, String deviceAddress) {
        if (!deviceModel.equals(getModels()[0])) {
            return false;
        }
        if ("00".equals(deviceAddress)) {
            String address = readCurrentAddress();
            lastTestState = address == null ? "读取设备地址失败" : "当前设备地址：" + address;
            return address != null;
        }
        return readState(deviceAddress);
    }

    @Override
    public String getTestState() {
        return lastTestState;
    }

    private boolean parseState(String result) {
        try {
            if (result.length() < 90 || !"03".equals(result.substring(2, 4))) {
                return false;
            }
            int byteCount = parseHex(result.substring(4, 6), -1);
            if (byteCount < 40) {
                return false;
            }
            int power = getRegister(result, 0);
            powerSupply = power == 1;
            // 设定项由写入成功后更新缓存，状态帧只刷新运行反馈，避免旧帧覆盖手动选择。
            workStatus = getRegister(result, 5);
            int humidity = getRegister(result, 6);
            environmentHumidity = humidity == 0x00FF ? null : humidity;
            int temperature = getRegister(result, 7);
            environmentTemperature = temperature == 0x00FF ? null : toSigned16(temperature);
            faultCode = getRegister(result, 8);
            int coil1 = getRegister(result, 10);
            coilTemperature1 = coil1 == 0x00FF ? null : toSigned16(coil1);
            int coil2 = getRegister(result, 11);
            coilTemperature2 = coil2 == 0x00FF ? null : toSigned16(coil2);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private int getRegister(String result, int index) {
        return getRegister(result, index, 0);
    }

    private int getRegister(String result, int index, int defaultValue) {
        int start = 6 + index * 4;
        if (result.length() < start + 4) {
            return defaultValue;
        }
        return parseHex(result.substring(start, start + 4), defaultValue);
    }

    private boolean writeRegister(String address, int register, int value, boolean checkAvailable) {
        if (checkAvailable && !isAvailable()) {
            return false;
        }
        String str = address + "06" + String.format("%04X", register) + String.format("%04X", value);
        String instruction = str + getCRC(str);
        return instruction.equals(serialPortUtil.sendAndReceiveSerialData(this, instruction));
    }

    private boolean writeRegisterNoResponse(String address, int register, int value) {
        String str = address + "06" + String.format("%04X", register) + String.format("%04X", value);
        String instruction = str + getCRC(str);
        return serialPortUtil.sendSerialDataNoResponse(this, instruction);
    }

    private boolean writePowerState(boolean state) {
        if ("00".equals(getAddress())) {
            return sendBroadcastPower(getAddress(), state, true);
        }
        return writeRegister(getAddress(), REG_POWER, state ? 1 : 0, true);
    }

    private void clearPowerOnSerialBuffer() {
        String droppedData = serialPortUtil.clearInputBuffer(POWER_ON_SERIAL_BUFFER_CLEAR_MS);
        if (!StringUtils.isEmpty(droppedData)) {
            MyLog.controller("485除湿机：清理上电串口残留 " + droppedData);
        }
    }

    private boolean sendBroadcastPower(String address, boolean state, boolean checkAvailable) {
        if (checkAvailable && !isAvailable()) {
            return false;
        }
        String str = address + "06" + String.format("%04X", REG_BROADCAST_POWER) + (state ? "00AA" : "0055");
        String instruction = str + getCRC(str);
        return checkAvailable
                ? executeInstructionsNoResponse(instruction, state ? "广播开机" : "广播关机")
                : serialPortUtil.sendSerialDataNoResponse(this, instruction);
    }

    private void appendFault(StringBuilder builder, int bit, String text) {
        if ((faultCode & (1 << bit)) == 0) {
            return;
        }
        if (builder.length() > 0) {
            builder.append("、");
        }
        builder.append(text);
    }

    private void appendStatus(StringBuilder builder, int bit, String text) {
        if ((workStatus & (1 << bit)) == 0) {
            return;
        }
        if (builder.length() > 0) {
            builder.append("、");
        }
        builder.append(text);
    }

    private int parseHex(String hex, int defaultValue) {
        try {
            return Integer.parseInt(hex, 16);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    private int toSigned16(int value) {
        return value > 0x7FFF ? value - 0x10000 : value;
    }

    private int limit(int value, int min, int max) {
        return Math.max(min, Math.min(max, value));
    }

    private static String[] buildAddressList() {
        String[] result = new String[100];
        result[0] = "00";
        for (int i = 1; i <= 99; i++) {
            result[i] = String.format("%02X", i);
        }
        return result;
    }
}
