package com.skr.mdm.listener;

import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SM4;
import cn.hutool.json.JSONUtil;
import com.skr.mdm.bean.so.RabbitMqSO;
import com.skr.mdm.bean.vo.WebSocketResultMessageVO;
import com.skr.mdm.dao.DeviceEquipmentDao;
import com.skr.mdm.dao.SysSendMsgDao;
import com.skr.mdm.dao.SysSendMsgUserDao;
import com.skr.mdm.entity.SysSendMsgDO;
import com.skr.mdm.enums.EquipmentStateEnum;
import com.skr.mdm.enums.MdmEncryptEnum;
import com.skr.mdm.event.PushUserEvent;
import com.skr.mdm.rabbitmq.RabbitMQProvider;
import com.skr.mdm.util.AESUtils;
import com.skr.mdm.util.SmUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author nfq
 * @date 2023-04-03 9:47
 */
@Slf4j
@Component
public class PushUserListener implements ApplicationListener<PushUserEvent> {

    @Value("${mdm.encrypt}")
    private String mdmEncrypt;

    @Autowired
    private SysSendMsgDao sysSendMsgDao;
    @Autowired
    private SysSendMsgUserDao sysSendMsgUserDao;
    @Autowired
    private RabbitMQProvider rabbitMQProvider;
    @Autowired
    private DeviceEquipmentDao equipmentDao;

    @Override
    public void onApplicationEvent(PushUserEvent event) {
        log.info("推送指令：{} 设备数量：{}", event.getCmdType(), event.getEquipmentIdList().size());
        issuedUser(event.getEquipmentIdList(), event.getCompanyId(), event.getCmdType(), event.getData());
    }

    public void issuedUser(List<Integer> equipmentIdList, Integer companyId, String cmdType, String data) {
        if (CollectionUtils.isEmpty(equipmentIdList)) {
            return;
        }
        // 去重
        equipmentIdList = equipmentIdList.stream().distinct().collect(Collectors.toList());
        // 加密
        byte[] secretKeyBytes = getSecretKeyBytes();
        SM2 sm2 = SmUtils.sm2Crypt();
        String secretKeySm2 = sm2.encryptHex(secretKeyBytes, KeyType.PublicKey);
        data = encryptData(data, secretKeyBytes);
        // 保存
        Long sendMsgId = insertSendMsg(equipmentIdList, companyId, cmdType, data, secretKeySm2);
        List<String> channelIdList = equipmentDao.listOnlineChannelIdByIdList(EquipmentStateEnum.WS_ONLINE.getState(),
                equipmentIdList);
        // 推送
        push(channelIdList, sendMsgId, cmdType, data, secretKeySm2);
    }

    public Long insertSendMsg(List<Integer> equipmentIdList, Integer companyId, String cmdType, String data, String secretKeySm2) {
        // 数据入库
        SysSendMsgDO sysSendMsgDO = new SysSendMsgDO();
        sysSendMsgDO.setData(data);
        sysSendMsgDO.setCreateTime(new Date());
        sysSendMsgDO.setCmdType(cmdType);
        sysSendMsgDO.setCompanyId(companyId);
        sysSendMsgDO.setSecretKey(secretKeySm2);
        sysSendMsgDao.insert(sysSendMsgDO);
        sysSendMsgUserDao.insertBatch(sysSendMsgDO.getId(), 1, equipmentIdList);
        return sysSendMsgDO.getId();
    }


    private void push(List<String> channelIdList, Long sendMsgId, String cmdType, String data, String secretKeySm2) {
        // 在线用户下发
        if (CollectionUtils.isNotEmpty(channelIdList) && sendMsgId != null) {
            WebSocketResultMessageVO resultMessageVO = WebSocketResultMessageVO.builder()
                    .msgId(sendMsgId)
                    .type(cmdType)
                    .data(data)
                    .sendTime(System.currentTimeMillis())
                    .secretKey(secretKeySm2)
                    .build();
            List<WebSocketResultMessageVO> messageVOList = new ArrayList<>();
            messageVOList.add(resultMessageVO);
            RabbitMqSO rabbitMqSO = RabbitMqSO.builder().message(JSONUtil.toJsonStr(JSONUtil.parseArray(messageVOList))).channelIdList(channelIdList).build();
            rabbitMQProvider.pushDataWithWebsocket(rabbitMqSO);
        }
    }

    /**
     * 下发数据加密
     *
     * @param data
     * @param secretKeyBytes
     * @return
     */
    private String encryptData(String data, byte[] secretKeyBytes) {
        if (MdmEncryptEnum.SM.getMdmEncrypt().equals(mdmEncrypt)) {
            SM4 sm4 = SmUtil.sm4(secretKeyBytes);
            data = StringUtils.isBlank(data) ? "" : sm4.encryptHex(data);
        }
        if (MdmEncryptEnum.AES.getMdmEncrypt().equals(mdmEncrypt)) {
            data = StringUtils.isBlank(data) ? "" : AESUtils.encrypt(data);
        }
        return data;
    }

    @NotNull
    private static byte[] getSecretKeyBytes() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] secretKeyBytes = new byte[16];
        secureRandom.nextBytes(secretKeyBytes);
        return secretKeyBytes;
    }
}
