package com.fuyuanshen.global.mqtt.rule.xinghan;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.sse.dto.SseMessageDto;
import com.fuyuanshen.common.sse.utils.SseMessageUtils;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceLog;
import com.fuyuanshen.equipment.mapper.DeviceLogMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import com.fuyuanshen.global.mqtt.constants.XingHanCommandTypeConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.common.core.constant.GlobalConstants.GLOBAL_REDIS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_ALARM_MESSAGE_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;

/**
 * 星汉设备发送紧急通知 下发规则：
 * <p>
 * 1. 设备上行 sta_BreakNews=cover!  => 仅标记成功<br>
 * 2. 设备上行 sta_BreakNews=数字   => GBK编码，每行文字为一包，一共4包，第一字节为包序号
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class XinghanSendAlarmMessageRule implements MqttMessageRule {

    private final MqttGateway mqttGateway;
    private final ObjectMapper objectMapper;
    private final ScheduledExecutorService scheduledExecutorService;
    private final DeviceLogMapper deviceLogMapper;
    private final DeviceMapper deviceMapper;
    /**
     * 设备上行确认消息
     */
    public static final String BREAK_NEWS_CONFIRMATION = "I get it";

    /**
     * 设备上行成功标记
     */
    public static final String BREAK_NEWS_SUCCESS = "cover!";

    @Override
    public String getCommandType() {
        return XingHanCommandTypeConstants.XingHan_BREAK_NEWS;
    }

    @Override
    public void execute(MqttRuleContext ctx) {
        String functionAccess = FUNCTION_ACCESS_KEY + "ALARM:" + ctx.getDeviceImei();
        try {
            XinghanSendAlarmMessageRule.MqttXinghanAlarmMsgJson payload = objectMapper.convertValue(
                    ctx.getPayloadDict(), XinghanSendAlarmMessageRule.MqttXinghanAlarmMsgJson.class);

            String respText = payload.getStaBreakNews();
            log.info("设备上报紧急通知握手： {} ", respText);
            // --- 去重 START ---
            String dedupKey = "xd:ALARM:dedup:" + ctx.getDeviceImei() + ":" + respText;
            boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(3));
            if (!first) {
                log.warn("重复消息丢弃 {}", dedupKey);
                return;
            }
            // 1. I get it —— 表示用户确认收到消息
            if (BREAK_NEWS_CONFIRMATION.equalsIgnoreCase(respText)) {
                var device = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_imei", ctx.getDeviceImei()));
                // 使用MyBatis-Plus内置方法查询最新一条紧急通知
                QueryWrapper<DeviceLog> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("device_id", device.getId())
                        .eq("device_action", "发送紧急通知")  // 根据您的表结构调整
                        .orderByDesc("create_time")
                        .last("LIMIT 1");
                DeviceLog latestLog = deviceLogMapper.selectOne(queryWrapper);
                log.info("设备 {} 最新紧急通知：{}", ctx.getDeviceImei(), latestLog);
                if (latestLog == null) {
                    return;
                }
                // 更新数据源字段
                UpdateWrapper<DeviceLog> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("id", latestLog.getId())             // 条件：ID匹配
                        .set("data_source", "设备已收到通知"); // 要更新的字段
                deviceLogMapper.update(null, updateWrapper);
                // 推送SSE消息
                scheduledExecutorService.schedule(() -> {
                    SseMessageDto dto = new SseMessageDto();
                    dto.setMessage(String.format("%s设备已收到通知！", latestLog.getDeviceName()));
                    dto.setUserIds(List.of(latestLog.getCreateBy()));
                    SseMessageUtils.publishMessage(dto);
                }, 2, TimeUnit.SECONDS);
                return;
            }
            // 1. cover! —— 成功标记
            if (BREAK_NEWS_SUCCESS.equalsIgnoreCase(respText)) {
                RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
                log.info("设备 {} 发送紧急通知完成", ctx.getDeviceImei());
                return;
            }
            // 2. 数字 —— 下发数据块
            int blockIndex;
            try {
                blockIndex = Integer.parseInt(respText);
            } catch (NumberFormatException ex) {
                log.warn("设备 {} 紧急通知上报非法块号：{}", ctx.getDeviceImei(), respText);
                return;
            }
            // 将发送的信息原文本以List<String>形式存储在Redis中
            String data = RedisUtils.getCacheObject(GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX + ctx.getDeviceImei() + DEVICE_ALARM_MESSAGE_KEY_PREFIX);
            if (data == null || data.isEmpty()) {
                return;
            }
            //
            ArrayList<Integer> intData = new ArrayList<>();
            intData.add(blockIndex);
            // 获取块原内容 转成GBK  再转成无符号十进制整数
            String blockTxt = data.split("\\|")[blockIndex-1];
            // 再按 GBK 编码把字符串转成字节数组，并逐个转为无符号十进制整数
            for (byte b : blockTxt.getBytes(GBK)) {
                intData.add(b & 0xFF);   // b & 0xFF 得到 0~255 的整数
            }

            Map<String, Object> map = new HashMap<>();
            map.put("ins_BreakNews", intData);

            String topic = MqttConstants.GLOBAL_PUB_KEY + ctx.getDeviceImei();
            String json = JsonUtils.toJsonString(map);
            mqttGateway.sendMsgToMqtt(topic, 1, json);
            log.info("发送设备紧急通知=>topic:{},payload:{}",
                    MqttConstants.GLOBAL_PUB_KEY + ctx.getDeviceImei(),
                    JsonUtils.toJsonString(map));

        } catch (Exception e) {
            log.error("处理发送设备紧急通知时出错", e);
            RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
        }
    }

    private static final Charset GBK = Charset.forName("GBK");

    /* ---------- DTO ---------- */

    @Data
    private static class MqttXinghanAlarmMsgJson {
        /**
         * 设备上行：
         * 数字  -> 请求对应块号
         * cover! -> 写入成功
         */
        @JsonProperty("sta_BreakNews")
        private String staBreakNews;
    }
}
