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

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fuyuanshen.common.core.utils.ImageToCArrayConverter;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
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.LightingCommandTypeConstants;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.CRC32;

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.common.core.utils.ImageToCArrayConverter.convertHexToDecimal;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_BOOT_LOGO_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;

/**
 * 星汉设备开机 LOGO 下发规则：
 * <p>
 * 1. 设备上行 sta_PicTarns=great!  => 仅标记成功<br>
 * 2. 设备上行 sta_PicTarns=数字   => 下发第 N 块数据（256B/块，带 CRC32）
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class XinghanBootLogoRule implements MqttMessageRule {

    private final MqttGateway mqttGateway;
    private final ObjectMapper objectMapper;

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

    @Override
    public void execute(MqttRuleContext ctx) {
        final String functionAccessKey = FUNCTION_ACCESS_KEY + "LOGO:" + ctx.getDeviceImei();
        try {
            MqttXinghanLogoJson payload = objectMapper.convertValue(
                    ctx.getPayloadDict(), MqttXinghanLogoJson.class);

            String respText = payload.getStaPicTrans();
            log.warn("设备上报LOGO：{}", respText);

            // --- 去重 START ---
            String dedupKey = "xd:MSG:LOGO:" + ctx.getDeviceImei() + ":" + respText;
            boolean first = RedisUtils.setObjectIfAbsent(dedupKey, "1", Duration.ofSeconds(3));
            if (!first) {
                log.warn("重复消息丢弃 {}", dedupKey);
                return;
            }

            // 1. great! —— 成功标记
            if ("great!".equalsIgnoreCase(respText)) {
                RedisUtils.setCacheObject(functionAccessKey,
                        FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
                log.info("设备 {} 开机 LOGO 写入成功", ctx.getDeviceImei());
                return;
            }

            // 2. 数字 —— 下发数据块
            int blockIndex;
            try {
                blockIndex = Integer.parseInt(respText);
            } catch (NumberFormatException ex) {
                log.warn("设备 {} LOGO 上报非法块号：{}", ctx.getDeviceImei(), respText);
                return;
            }
            String hexImage = RedisUtils.getCacheObject(
                    GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + ctx.getDeviceImei() + DEVICE_BOOT_LOGO_KEY_PREFIX);
            if (StringUtils.isEmpty(hexImage)) {
                return;
            }

            byte[] fullBin = ImageToCArrayConverter.convertStringToByteArray(hexImage);
            byte[] chunk = ImageToCArrayConverter.getChunk(fullBin, blockIndex - 1, CHUNK_SIZE);
            log.info("设备 {} 第 {} 块数据长度: {} bytes", ctx.getDeviceImei(), blockIndex, chunk.length);

            // 组装下发数据
            ArrayList<Integer> dataFrame = new ArrayList<>();
            dataFrame.add(blockIndex);                                // 块号
            ImageToCArrayConverter.buildArr(convertHexToDecimal(chunk), dataFrame);
            dataFrame.addAll(crc32AsList(chunk));                     // CRC32

            Map<String, Object> pub = new HashMap<>();
            pub.put("ins_PicTrans", dataFrame);

            String topic = MqttConstants.GLOBAL_PUB_KEY + ctx.getDeviceImei();
            String json = JsonUtils.toJsonString(pub);
            mqttGateway.sendMsgToMqtt(topic, 1, json);

            log.info("下发开机 LOGO 数据 => topic:{}, payload:{}", topic, json);

        } catch (Exception e) {
            log.error("处理设备 {} 开机 LOGO 失败", ctx.getDeviceImei(), e);
            RedisUtils.setCacheObject(functionAccessKey,
                    FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
        }
    }

    /* ---------- 内部工具 ---------- */

    private static final int CHUNK_SIZE = 256;

    /**
     * 计算数据的CRC32校验值，并将结果转换为整数列表
     *
     * @param data 需要计算CRC32校验值的字节数组
     * @return 包含CRC32校验值的整数列表，每个字节对应一个无符号整数
     */
    private static ArrayList<Integer> crc32AsList(byte[] data) {
        // 计算CRC32校验值
        CRC32 crc = new CRC32();
        crc.update(data);

        // 将CRC32值转换为字节数组
        byte[] crcBytes = ByteBuffer.allocate(4)
                .order(ByteOrder.BIG_ENDIAN)
                .putInt((int) crc.getValue())
                .array();

        // 将字节数组转换为无符号整数列表
        ArrayList<Integer> list = new ArrayList<>(4);
        for (byte b : crcBytes) {
            list.add(Byte.toUnsignedInt(b));
        }
        return list;
    }


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

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