using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Fuel.Core
{
    public class Sm4Encryptor
    {
        // 加密方法
        public static string Encrypt(string plainText, string hexKey)
        {
            byte[] keyBytes = HexStringToByteArray(hexKey);
            if (keyBytes.Length != 16)
            {
                throw new ArgumentException("Key must be exactly 16 bytes in length.", nameof(hexKey));
            }

            byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);

            IBufferedCipher cipher = new PaddedBufferedBlockCipher(new SM4Engine(), new Pkcs7Padding());
            KeyParameter keyParam = new KeyParameter(keyBytes);

            cipher.Init(true, keyParam);
            byte[] outputBytes = new byte[cipher.GetOutputSize(plainBytes.Length)];
            int length = cipher.ProcessBytes(plainBytes, 0, plainBytes.Length, outputBytes, 0);
            length += cipher.DoFinal(outputBytes, length); // Do the final block

            // Convert to hex instead of base64
            return ByteArrayToHexString(outputBytes);
        }
        private static string ByteArrayToHexString(byte[] bytes)
        {
            StringBuilder hexString = new StringBuilder(bytes.Length * 2);
            foreach (byte b in bytes)
            {
                hexString.AppendFormat("{0:X2}", b);
            }
            return hexString.ToString();
        }
        private static byte[] HexStringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }
        // 解密方法
        public static string Decrypt(string cipherText, string key, string iv)
        {
            byte[] keyBytes = Encoding.UTF8.GetBytes(key);
            byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
            byte[] cipherBytes = Convert.FromBase64String(cipherText);

            IBufferedCipher cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new SM4Engine()), new Pkcs7Padding());

            KeyParameter keyParam = new KeyParameter(keyBytes);
            ParametersWithIV parametersWithIV = new ParametersWithIV(keyParam, ivBytes);

            cipher.Init(false, parametersWithIV);
            byte[] outputBytes = new byte[cipher.GetOutputSize(cipherBytes.Length)];
            int length = cipher.ProcessBytes(cipherBytes, 0, cipherBytes.Length, outputBytes, 0);
            cipher.DoFinal(outputBytes, length); // Do the final block

            return Encoding.UTF8.GetString(outputBytes, 0, length);
        }
    }
}
