package com.ruoyi.device.task;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.device.config.DecryptDevice;
import com.ruoyi.device.config.DecryptDeviceConfig;
import com.ruoyi.device.mqtt.enums.DeviceRedisEnum;
import com.ruoyi.device.mqtt.api.EmqxApiUtil;
import com.ruoyi.device.mqtt.api.item.ClientInfo;
import com.ruoyi.device.mqtt.api.item.EmqxResult;
import com.ruoyi.device.mqtt.domain.encoder.tax.CiphertextDataDown;
import com.ruoyi.device.mqtt.enums.MsgTypeEnum;
import com.ruoyi.device.mqtt.handler.HandlerManager;
import com.ruoyi.device.mqtt.handler.encoder.IEncoder;
import com.ruoyi.device.mqtt.util.MsgHandlerUtil;
import com.ruoyi.device.mqtt.vo.CiphertextData;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.xml.bind.DatatypeConverter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 密文解密定时调度：失败重试与待解密队列下发
 */
@Component("ciphertextDataHandler")
public class CiphertextDataHandler
{
    private static final Logger log = LoggerFactory.getLogger(CiphertextDataHandler.class);

    @Resource
    private DecryptDeviceConfig decryptDeviceConfig;

    @Resource
    private HandlerManager handlerManager;

    @Resource
    private EmqxApiUtil emqxApiUtil;

    @Resource
    private RedisCache redisCache;

    /**
     * 密文解密定时调度：失败重试与待解密队列下发
     */
    public void sendCiphertextDataDecrypt()
    {
        // 检查是否有超时未返回的数据
        Map<String, CiphertextData> timeOutQueue = redisCache.getCacheMap(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_FAIL.getKey());
        if (timeOutQueue != null && !timeOutQueue.isEmpty())
        {
            Long currentTime = System.currentTimeMillis();

            for (CiphertextData ciphertextData : timeOutQueue.values())
            {
                if (currentTime - ciphertextData.getSendTime() > decryptDeviceConfig.getTimeout())
                {
                    redisCache.deleteCacheMapValue(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_SEND.getKey(), ciphertextData.getKey2());
                    if (decryptDeviceConfig.getDefaultFailTimes() > ciphertextData.getSendTimes())
                    {
                        // 插入失败队列
                        ciphertextData.setKey1(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_FAIL.getKey());
                        redisCache.setCacheMapValue(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_FAIL.getKey(), ciphertextData.getKey2(), ciphertextData);
                        log.info("已发送数据解密超时，加入到失败队列等待下次发送");
                    }
                    else
                    {
                        log.info("已发送数据解密超时，已过最大失败次数，数据删除");
                    }
                }
            }
        }

        List<DecryptDevice> conversion = new ArrayList<>();
        // 先判断谁在线
        for (DecryptDevice decryptDevice : decryptDeviceConfig.getDecryptDeviceList())
        {
            EmqxResult<List<ClientInfo>> clientInfo = emqxApiUtil.getClientInfo(decryptDevice.getDeviceType(), decryptDevice.getDeviceSn());
            if (clientInfo.getData() != null && !clientInfo.getData().isEmpty())
            {
                conversion.add(decryptDevice);
                log.info("解密设备：{} 在线", decryptDevice.getDeviceSn());
            }
        }

        List<CiphertextData> waitQueue = getWaitQueue();
        if (waitQueue.isEmpty())
        {
            log.info("待解密队列为空");
            return;
        }

        for (DecryptDevice decryptDevice : conversion)
        {
            // 默认发送条数
            int freeSpace = decryptDeviceConfig.getDefaultSendSize();
            // 获取剩余空间
            String obj = redisCache.getCacheObject(
                String.format(DeviceRedisEnum.DEVICE_FREE_SPACE.getKey(), decryptDevice.getDeviceSn()));
            if (StringUtils.isNotBlank(obj))
            {
                freeSpace = Integer.parseInt(obj);
            }

            List<List<CiphertextData>> list = taskSplit(waitQueue, freeSpace);
            if (list.isEmpty())
            {
                log.info("解密设备:{},未分配到解密任务，结束！", decryptDevice.getDeviceSn());
                break;
            }

            if (list.size() > 1)
            {
                waitQueue = list.get(1);
                log.info("解密设备:{},分配到解密任务数量:{}，剩余待解密队列数量:{}！",
                    decryptDevice.getDeviceSn(), list.get(0).size(), list.get(1).size());
            }
            else
            {
                log.info("解密设备:{},分配到解密任务数量:{}，剩余待解密队列数量:0,开始发送数据到解密板！",
                    decryptDevice.getDeviceSn(), list.get(0).size());
                waitQueue = null;
            }

            for (CiphertextData ciphertextData : list.get(0))
            {
                send(ciphertextData, decryptDevice);
            }
        }
    }

    /**
     * 待解密队列任务分配 获取指定数量合集 和剩余合集
     *
     * @param waitQueue 待解密队列
     * @param size 分配数量
     */
    private List<List<CiphertextData>> taskSplit(List<CiphertextData> waitQueue, int size)
    {
        // 存放结果的集合
        List<List<CiphertextData>> result = new ArrayList<>();
        if (waitQueue != null && !waitQueue.isEmpty())
        {
            if (waitQueue.size() >= size)
            {
                List<CiphertextData> sublist = waitQueue.subList(0, size);
//                for (int startIndex = 0; startIndex < waitQueue.size(); startIndex += size)
//                {
//                    List<CiphertextData> sublist = waitQueue.subList(startIndex, Math.min(startIndex + size, waitQueue.size()));
                    result.add(sublist);
//                }
            }
            else
            {
                result.add(waitQueue);
            }
        }
        return result;
    }

    /**
     * 获取待解密队列
     */
    private List<CiphertextData> getWaitQueue()
    {
        Map<String, CiphertextData> failQueue = redisCache.getCacheMap(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_FAIL.getKey());
        Map<String, CiphertextData> originalQueue = redisCache.getCacheMap(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_ORIGNAL.getKey());

        List<CiphertextData> result = new ArrayList<>();
        if (failQueue != null && !failQueue.isEmpty())
        {
            result.addAll(failQueue.values());
            log.info("失败队列数据条数：{}", failQueue.size());
        }

        if (originalQueue != null && !originalQueue.isEmpty())
        {
            result.addAll(originalQueue.values());
            log.info("原始队列数据条数：{}", originalQueue.size());
        }

        return result;
    }

    /**
     * 发送数据到解密版
     * 并移除队列加入到发送队列
     *
     * @param ciphertextData 待解密数据
     * @param decryptDevice 解密设备
     */
    @SuppressWarnings("unchecked")
    private void send(CiphertextData ciphertextData, DecryptDevice decryptDevice)
    {
        CiphertextDataDown request = new CiphertextDataDown();
        request.setCiphertextData(DatatypeConverter.parseHexBinary(ciphertextData.getData()));
        request.setDeviceSn(decryptDevice.getDeviceSn());
        request.setDeviceType(decryptDevice.getDeviceType());
        String key = MsgHandlerUtil.getEncoderKey(MsgTypeEnum.CIPHERTEXT_DATA_DECRYPT_DOWN);
        IEncoder<CiphertextDataDown> encoder = (IEncoder<CiphertextDataDown>) handlerManager.getEncoder(key);
        encoder.encode(request);
        // 队列中移除
        redisCache.deleteCacheMapValue(ciphertextData.getKey1(), ciphertextData.getKey2());

        // 添加 发送队列 发送次数+1
        ciphertextData.setSendTimes(ciphertextData.getSendTimes() + 1);
        ciphertextData.setSendTime(System.currentTimeMillis());
        redisCache.setCacheMapValue(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_SEND.getKey(), ciphertextData.getKey2(), ciphertextData);
    }
}
