package com.bonus.canteen.core.config;

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.util.PrimitiveArrayUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.symmetric.SM4;
import com.bonus.canteen.core.common.utils.SpringContextHolder;
import com.google.common.base.Joiner;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.SortedMap;

public class SmUtils {
   private static final Logger log = LoggerFactory.getLogger(SmUtils.class);
   private static SecureProperties secureProperties = SpringContextHolder.getBean(SecureProperties.class);
   public static void setSecureProperties(SecureProperties secureProperties) {
      if (SmUtils.secureProperties == null) {
         SmUtils.secureProperties = secureProperties;
      }
   }

   public static String signBySm3(SortedMap<String, String> data, String clientKey) {
      data.put(secureProperties.getSecurity().getClientKeySignParamName(), clientKey);
      String joinedParam = Joiner.on("&").useForNull("").withKeyValueSeparator("=").join(data);
      log.debug("Sign param:{}", joinedParam);
      return Base64Encoder.encode(SmUtil.sm3().digest(joinedParam));
   }

   public static String signBySm3WithServerKey(SortedMap<String, String> data, String clientKey) {
      data.put(secureProperties.getSecurity().getServerKeySignParamName(), secureProperties.getSecurity().getServerSm4Key());
      return signBySm3(data, clientKey);
   }

   public static String signAuthTokenBySm3(SortedMap<String, String> data, String clientKey) {
      return signBySm3WithServerKey(data, clientKey);
   }

   public static String decryptBySm2(String data) {
      return SmUtil.sm2(secureProperties.getSecurity().getServerSm2Key(), secureProperties.getSecurity().getClientSm2Key()).decryptStr(data, KeyType.PrivateKey);
   }

   public static String signBySm2(String data) {
      return Base64Encoder.encode(SmUtil.sm2(secureProperties.getSecurity().getServerSm2Key(), secureProperties.getSecurity().getClientSm2Key()).sign(data.getBytes(StandardCharsets.UTF_8)));
   }

   public static String decryptBySm4(String data, byte[] key) {
      byte[] decodedData = Base64Decoder.decode(data);
      if (decodedData.length <= 16) {
         throw new IllegalArgumentException("Encrypted data byte length must greater than 16");
      } else {
         byte[] iv = PrimitiveArrayUtil.sub(decodedData, 0, 16);
         byte[] dataBytes = PrimitiveArrayUtil.sub(decodedData, 16, decodedData.length);
         return (new SM4(Mode.CBC, Padding.PKCS5Padding, key, iv)).decryptStr(dataBytes);
      }
   }

   public static String decryptBySm4(String data, String key) {
      return decryptBySm4(data, Base64Decoder.decode(key));
   }

   public static String decryptBySm4WithServerKey(String data) {
      return decryptBySm4(data, secureProperties.getSecurity().getServerSm4KeyBytes());
   }

   public static String encryptBySm4(String data, byte[] key) {
      SM4 var10000 = new SM4(Mode.CBC, Padding.PKCS5Padding, key, RandomUtil.randomBytes(16));
      String var10001 = RandomUtil.randomString(16);
      return var10000.encryptBase64(var10001 + data);
   }

   public static String encryptBySm4WithServerKey(String data) {
      return encryptBySm4(data, secureProperties.getSecurity().getServerSm4KeyBytes());
   }

   public static void main(String[] args) throws NoSuchAlgorithmException {
      //sm2() 移到 SmUtilsTest.java
      sm4();
      sm4WithoutIV();
   }

   private static void sm4() throws NoSuchAlgorithmException {
      System.out.println("================sm4==================");
      BouncyCastleProvider provider = new BouncyCastleProvider();
      KeyGenerator generator = KeyGenerator.getInstance("SM4", provider);
      SecretKey secretKey = generator.generateKey();
      byte[] encoded = secretKey.getEncoded();
      String key = Base64Encoder.encode(encoded);
      System.out.println("key: " + key);
      String data = "liolay";
      String encryptBySm4 = encryptBySm4(data, encoded);
      System.out.println("encrypt: " + encryptBySm4);
      PrintStream var10000 = System.out;
      String var10001 = decryptBySm4(encryptBySm4, encoded);
      var10000.println("decrypt: " + var10001);
      var10000 = System.out;
      var10001 = decryptBySm4(encryptBySm4, key);
      var10000.println("decrypt: " + var10001);
   }

   private static void sm4WithoutIV() throws NoSuchAlgorithmException {
      System.out.println("================sm4WithoutIV==================");
      BouncyCastleProvider provider = new BouncyCastleProvider();
      KeyGenerator generator = KeyGenerator.getInstance("SM4", provider);
      SecretKey secretKey = generator.generateKey();
      byte[] key = secretKey.getEncoded();
      String keyEncode = Base64Encoder.encode(key);
      System.out.println("key: " + keyEncode);
      String plainData = "liolay";
      String encryptedData = encryptBySm4(plainData, key);
      System.out.println("encrypt: " + encryptedData);
      String decryptData = decryptBySm4(encryptedData, key);
      System.out.println("decrypt: " + decryptData);
   }
}
