package com.ruoyi.iot.util;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.iot.domain.IotDeviceSysInfo;
import com.ruoyi.iot.domain.ObjBody;
import com.ruoyi.iot.domain.response.CommandResponse;
import com.ruoyi.iot.enums.IotHttpStatus;

import lombok.extern.slf4j.Slf4j;


/**
 * 功能描述: OneNet数据推送接收程序工具类。
 *
 * Created by Roy on 2017/5/17.
 *
 */
@Slf4j
public class IotUtil {

    private static Logger logger = LoggerFactory.getLogger(IotUtil.class);

    private static MessageDigest mdInst;

    static {
        try {
            mdInst = MessageDigest.getInstance("MD5");
            Security.addProvider(new BouncyCastleProvider());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }


    /**
     * 功能描述:在OneNet平台配置数据接收地址时，平台会发送URL&token验证请求<p>
     *          使用此功能函数验证token
     * @param msg 请求参数 <msg>的值
     * @param nonce 请求参数 <nonce>的值
     * @param signature 请求参数 <signature>的值
     * @param token OneNet平台配置页面token的值
     * @return token检验成功返回true；token校验失败返回false
     */
    public static boolean checkToken(String msg,String nonce,String signature, String token) throws UnsupportedEncodingException {

        byte[] paramB = new byte[token.length() + 8 + msg.length()];
        System.arraycopy(token.getBytes(), 0, paramB, 0, token.length());
        System.arraycopy(nonce.getBytes(), 0, paramB, token.length(), 8);
        System.arraycopy(msg.getBytes(), 0, paramB, token.length() + 8, msg.length());
        String sig =  com.sun.org.apache.xerces.internal.impl.dv.util.Base64.encode(mdInst.digest(paramB));
//        logger.info("url&token validation: result {},  detail receive:{} calculate:{}", sig.equals(signature.replace(' ','+')),signature,sig);
        return sig.equals(signature.replace(' ','+'));
    }

    /**
     * 功能描述: 检查接收数据的信息摘要是否正确。<p>
     *          方法非线程安全。
     * @param obj 消息体对象
     * @param token OneNet平台配置页面token的值
     * @return
     */
    public static boolean checkSignature(ObjBody obj, String token)  {
        try {
            //计算接受到的消息的摘要
            //token长度 + 8B随机字符串长度 + 消息长度
            byte[] signature = new byte[token.length() + 8 + obj.getMsg().toString().length()];
            System.arraycopy(token.getBytes(StandardCharsets.UTF_8), 0, signature, 0, token.length());
            System.arraycopy(obj.getNonce().getBytes(StandardCharsets.UTF_8), 0, signature, token.length(), 8);
            System.arraycopy(obj.getMsg().toString().getBytes(StandardCharsets.UTF_8), 0, signature, token.length() + 8, obj.getMsg().toString().length());
            String calSig = Base64.encodeBase64String(mdInst.digest(signature));
//        logger.info("check signature: result:{}  receive sig:{},calculate sig: {}",calSig.equals(obj.getSignature()),obj.getSignature(),calSig);
            return calSig.equals(obj.getSignature());
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     *  功能描述 解密消息
     * @param obj 消息体对象
     * @param encodeKey OneNet平台第三方平台配置页面为用户生成的AES的BASE64编码格式秘钥
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     */
    public static String decryptMsg(ObjBody obj, String encodeKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        byte[] encMsg = Base64.decodeBase64(obj.getMsg().toString());
        byte[] aeskey = Base64.decodeBase64(encodeKey + "=");
        SecretKey secretKey = new SecretKeySpec(aeskey, 0, 32, "AES");
        Cipher cipher = null;
        cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(aeskey, 0, 16));
        byte[] allmsg = cipher.doFinal(encMsg);
        byte[] msgLenBytes = new byte[4];
        System.arraycopy(allmsg, 16, msgLenBytes, 0, 4);
        int msgLen = getMsgLen(msgLenBytes);
        byte[] msg = new byte[msgLen];
        System.arraycopy(allmsg, 20, msg, 0, msgLen);
        return new String(msg);
    }

    /**
     * 功能描述 解析数据推送请求，生成code>BodyObj</code>消息对象
     * @param body 数据推送请求body部分
     * @param encrypted 表征是否为加密消息
     * @return  生成的<code>BodyObj</code>消息对象
     */
    public static ObjBody resolveBody(String body, boolean encrypted) {
        JSONObject jsonMsg =JSON.parseObject(body);
        ObjBody obj = new ObjBody();
        obj.setNonce(jsonMsg.getString("nonce"));
        obj.setSignature(jsonMsg.getString("signature"));
        if (encrypted) {
            if (!jsonMsg.containsKey("enc_msg")) {
                return null;
            }
            obj.setMsg(jsonMsg.getString("enc_msg"));
        } else {
            if (!jsonMsg.containsKey("msg")) {
                return null;
            }
            obj.setMsg(jsonMsg.get("msg"));
        }
        return obj;
    }

    private static int getMsgLen(byte[] arrays) {
        int len = 0;
        len += (arrays[0] & 0xFF) << 24;
        len += (arrays[1] & 0xFF) << 16;
        len += (arrays[2] & 0xFF) << 8;
        len += (arrays[3] & 0xFF);
        return len;
    }

    /**
     * 解析蓝牙设备状态字符串为Map
     * @param statusStr 状态字符串 例如：V0.00@S0@A0.00@W0.00@P0.00@M0.00@T0.00@SET0@POW0
     * @return Map<String, String> 解析结果
     */
    public static Map<String, String> parseBltInfo(String statusStr) {
        Map<String, String> result = new HashMap<>();

        if (StringUtils.isEmpty(statusStr)) {
            return result;
        }

        try {
            // 按@分割字符串
            String[] items = statusStr.split("@");

            for (String item : items) {
                // 找到第一个数字或小数点的位置
                int splitIndex = 0;
                for (int i = 0; i < item.length(); i++) {
                    char c = item.charAt(i);
                    if (Character.isDigit(c) || c == '.') {
                        splitIndex = i;
                        break;
                    }
                }

                // 分割键值
                String key = item.substring(0, splitIndex);
                String value = item.substring(splitIndex);

                result.put(key, value);
            }
        } catch (Exception e) {
            log.error("解析状态字符串失败: {}", statusStr, e);
        }

        return result;
    }

    public static boolean isSuccess(CommandResponse result) {
        if (result == null) {
            return false;
        }
        return result.isSuccess();
    }

    public static boolean isOffline(CommandResponse result) {
        if (result == null) {
            return false;
        }
        return result.isNotOnline();
    }

    public static String getMsg(CommandResponse result) {
        if (result == null) {
            return "未知错误";
        }
        IotHttpStatus status = IotHttpStatus.convertByCode(result.getCode());
        return status.getMsg();
    }

    public static IotDeviceSysInfo toSysInfo(Object value) {
        if (value == null) {
            return null;
        }
        IotDeviceSysInfo sys = JSON.parseObject(value.toString(), IotDeviceSysInfo.class);

        // 旧版硬件BUG，电压超过100V的需要除以10
        if (sys.getBat() != null && sys.getBat().compareTo(BigDecimal.valueOf(100)) >= 0) {
            sys.setBat(sys.getBat().divide(BigDecimal.valueOf(10), 2, BigDecimal.ROUND_HALF_UP));
        }

        return sys;
    }
}
