package com.ruoyi.device.mqtt.handler.decoder.tax;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.device.config.DecryptDevice;
import com.ruoyi.device.config.DecryptDeviceConfig;
import com.ruoyi.device.mqtt.api.EmqxApiUtil;
import com.ruoyi.device.mqtt.api.constants.ApiPathConstants;
import com.ruoyi.device.mqtt.api.item.ClientInfo;
import com.ruoyi.device.mqtt.api.item.EmqxResult;
import com.ruoyi.device.mqtt.domain.decoder.tax.TaxTransferDataMessage;
import com.ruoyi.device.mqtt.domain.encoder.tax.CiphertextDataDown;
import com.ruoyi.device.mqtt.enums.DeviceRedisEnum;
import com.ruoyi.device.mqtt.enums.MsgTypeEnum;
import com.ruoyi.device.mqtt.enums.VersionEnum;
import com.ruoyi.device.mqtt.handler.HandlerManager;
import com.ruoyi.device.mqtt.handler.decoder.MessageHandler;
import com.ruoyi.device.mqtt.handler.encoder.IEncoder;
import com.ruoyi.device.mqtt.util.MsgHandlerUtil;
import com.ruoyi.device.mqtt.util.TargetTypeConvertUtil;
import com.ruoyi.device.mqtt.vo.CiphertextData;
import com.ruoyi.device.mqtt.vo.CommonHeader;
import com.ruoyi.device.mqtt.vo.CommonTopic;
import com.ruoyi.device.service.ITaxTransferDataService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.xml.bind.DatatypeConverter;
import java.util.Date;
import java.util.List;

/**
 * 报税口密文数据解析与下发服务
 *
 * @author lwm
 */
@Component
public class CiphertextDataService
{
    private static final Logger log = LoggerFactory.getLogger(CiphertextDataService.class);

    @Resource
    private HandlerManager handlerManager;

    @Resource
    private EmqxApiUtil emqxApiUtil;

    @Resource
    private DecryptDeviceConfig decryptDeviceConfig;

    @Resource
    private RedisCache redisCache;

    @Resource
    private ITaxTransferDataService taxTransferDataService;

    /**
     * 组装数据 然后调用外部解密服务 或者 下发解密机
     * @param topic topic
     * @param header header
     * @param body body
     * @param raiseHangFlag 是否有抬挂枪
     */
    public Void decode(CommonTopic topic, CommonHeader header, ByteBuf body, boolean raiseHangFlag)
    {
        ByteBuf device = Unpooled.buffer(256);
        CiphertextData ciphertextData = new CiphertextData();
        Long gatewayNo;
        Long collectorNo;
        if (header.getProtoVer() == VersionEnum.THIRD.getValue())
        {
            TargetTypeConvertUtil.convert(body.readUnsignedShortLE());
            gatewayNo = body.readUnsignedIntLE();
            TargetTypeConvertUtil.convert(body.readUnsignedShortLE());
            collectorNo = body.readUnsignedIntLE();
        }
        else
        {
            gatewayNo = body.readUnsignedIntLE();
            collectorNo = body.readUnsignedIntLE();
        }
        device.writeIntLE(gatewayNo.intValue());
        device.writeIntLE(collectorNo.intValue());
        if (raiseHangFlag)
        {
            // 有抬挂枪
            int gunStatus = body.readByte();
            if (gunStatus == 1)
            {
                int batteryTime = body.readUnsignedShortLE();
                int saveTime = body.readUnsignedShortLE();
                long startTime = System.currentTimeMillis() - (batteryTime + saveTime) * 1000L;
                long endTime = startTime + batteryTime * 1000L;
                ciphertextData.setRaiseTime(new Date(startTime));
                ciphertextData.setHangTime(new Date(endTime));
            }
            else
            {
                body.readUnsignedIntLE();
            }
        }

        ByteBuf copyBuf = body.copy();

        int taxNo = body.readByte();
        int gunNo = body.readByte();
        Long serialNo = body.readUnsignedIntLE();

        body.readBytes(20);
        int data14Len = body.readByte();
        int data11Len = body.readByte();
        if (data14Len != 0x20 || data11Len != 0x24)
        {
            byte[] bytes = new byte[copyBuf.readableBytes()];
            copyBuf.getBytes(copyBuf.readerIndex(), bytes);
            String payloadHex = MessageHandler.bytesToHexLog(bytes);
            log.info("网关:{},采集器:{},密文数据包异常,忽略当前数据包：{}", gatewayNo, collectorNo, payloadHex);
            return null;
        }

        byte[] deviceBytes = new byte[device.readableBytes()];
        device.getBytes(device.readerIndex(), deviceBytes);

        // 去掉crc16
        byte[] bytes = new byte[copyBuf.readableBytes() - 2];
        copyBuf.getBytes(copyBuf.readerIndex(), bytes);

        byte[] dataBytes = ArrayUtils.addAll(deviceBytes, bytes);

        // hash 存储
        String key2 = String.format(ApiPathConstants.FOUR_UNDER_LINE, gatewayNo, collectorNo, taxNo, gunNo, serialNo);

        ciphertextData.setDeviceSn(topic.getDeviceSn());
        ciphertextData.setDeviceType(topic.getDeviceType());
        ciphertextData.setSendTimes(0);
        ciphertextData.setData(DatatypeConverter.printHexBinary(dataBytes));
        ciphertextData.setDateTime(new Date());
        ciphertextData.setKey2(key2);

        if (Boolean.TRUE.equals(decryptDeviceConfig.getOutDecrypt()))
        {
            // 调用远程接口解析
            String post = HttpUtils.sendPost(decryptDeviceConfig.getOutDecryptUrl(), JSON.toJSONString(ciphertextData), MediaType.APPLICATION_JSON_VALUE);
            log.info("调用外部解密结果:{}", post);

            if (StringUtils.isNotBlank(post))
            {
                AjaxResult ajaxResult = JSON.parseObject(post, AjaxResult.class);
                if (ajaxResult != null &&
                        ajaxResult.get(AjaxResult.DATA_TAG) != null && ajaxResult.get(AjaxResult.DATA_TAG) instanceof JSONObject data)
                {
                    log.info("外部解密成功,调用业务服务保存数据:{}", data);
                    TaxTransferDataMessage taxTransferDataMessage = data.to(TaxTransferDataMessage.class);
                    taxTransferDataService.saveTaxTransferData(taxTransferDataMessage);
                }
            }
        }
        else
        {
            if (!send(ciphertextData))
            {
                ciphertextData.setKey1(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_ORIGNAL.getKey());
                redisCache.setCacheMapValue(DeviceRedisEnum.TAX_CIPHERTEXT_DATA_ORIGNAL.getKey(), key2, ciphertextData);
                log.info("收到设备：{} 发送的待解密数据，已存入原始队列，等待任务执行！", key2);
            }
        }
        return null;
    }

    /**
     * 发送待解密数据
     *
     * @param ciphertextData 待解密数据
     */
    private boolean send(CiphertextData ciphertextData)
    {
        for (DecryptDevice decryptDevice : decryptDeviceConfig.getDecryptDeviceList())
        {
            EmqxResult<List<ClientInfo>> clientInfo = emqxApiUtil.getClientInfo(
                decryptDevice.getDeviceType(), decryptDevice.getDeviceSn());
            if (clientInfo.getData() != null && !clientInfo.getData().isEmpty())
            {
                log.info("解密设备：{} 在线", decryptDevice.getDeviceSn());

                // 默认发送条数
                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);
                }

                if (freeSpace > 1)
                {
                    send(ciphertextData, decryptDevice);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 发送数据到解密版
     * 并移除队列加入到发送队列
     * @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);

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