package cn.qinys.platform.base.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.Security;

/**
 * Description: 编码工具类
 *
 * @author tanlie
 * Date: 2024/2/26 10:32
 */
@Slf4j
public class EncodeUtils {

    /**
     * CBC 填充方式
     */
    public static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";
    public static final String CIPHER_ALGORITHM_CBC7 = "AES/CBC/PKCS7Padding";
    /**
     * 默认密钥
     */
    public static final String KEY = "1q2w3e4r5t6y7u8i";
    /**
     * 偏移量
     */
    public static final String IV = "1234567812345678";

    private EncodeUtils() {
    }

    /**
     * MD5加密
     *
     * @param input 输入
     * @return String
     */
    public static String encodeMd5(String input) {
        return DigestUtils.md5Hex(input);
    }

    /**
     * MD5加密
     *
     * @param input 输入
     * @param salt  盐值
     * @return String
     */
    public static String encodeMd5(String input, String salt) {
        return DigestUtils.md5Hex(input + salt);
    }

    /**
     * Base64编码
     *
     * @param input 输入
     * @return String
     */
    public static String encodeBase64(byte[] input) {
        return new String(Base64.encodeBase64(input));
    }

    /**
     * Base64编码
     *
     * @param input 输入
     * @return String
     */
    public static String encodeBase64(String input) {
        try {
            return new String(Base64.encodeBase64(input.getBytes(StandardCharsets.UTF_8.name())));
        } catch (UnsupportedEncodingException e) {
            return "";
        }
    }

    /**
     * Base64解码
     *
     * @param input 输入
     * @return byte[]
     */
    public static byte[] decodeBase64(String input) {
        return Base64.decodeBase64(input);
    }

    /**
     * Base64解码
     *
     * @param input    输入
     * @param isDecode 是否编过码
     * @return byte[]
     */
    public static byte[] decodeBase64(String input, boolean isDecode) {
        if (isDecode) {
            return Base64.decodeBase64(input);
        } else {
            return Base64.decodeBase64(input.getBytes());
        }
    }

    /**
     * Html转码
     *
     * @param html html
     * @return String
     */
    public static String escapeHtml(String html) {
        return StringEscapeUtils.escapeHtml4(html);
    }

    /**
     * Html解码
     *
     * @param htmlEscaped 转义过的html
     * @return String
     */
    public static String unEscapeHtml(String htmlEscaped) {
        return StringEscapeUtils.unescapeHtml4(htmlEscaped);
    }

    /**
     * Xml转码
     *
     * @param xml xml
     * @return String
     */
    public static String escapeXml(String xml) {
        return StringEscapeUtils.escapeXml10(xml);
    }

    /**
     * Xml解码
     *
     * @param xmlEscaped 转义过的xml
     * @return String
     */
    public static String unEscapeXml(String xmlEscaped) {
        return StringEscapeUtils.unescapeXml(xmlEscaped);
    }

    /**
     * URL编码, Encode默认为UTF-8
     *
     * @param part part
     * @return String
     */
    public static String encodeUrl(String part) {
        return encodeUrl(part, StandardCharsets.UTF_8.name());
    }

    /**
     * URL解码, Encode默认为UTF-8
     *
     * @param part part
     * @return String
     */
    public static String decodeUrl(String part) {
        return decodeUrl(part, StandardCharsets.UTF_8.name());
    }

    /**
     * URL编码, Encode自定义
     *
     * @param part     String
     * @param encoding 编码
     * @return String
     */
    public static String encodeUrl(String part, String encoding) {
        try {
            return new URLCodec().encode(part, encoding);
        } catch (UnsupportedEncodingException e) {
            return "";
        }
    }

    /**
     * URL解码, Encode自定义
     *
     * @param part     part
     * @param encoding 编码
     * @return String
     */
    public static String decodeUrl(String part, String encoding) {
        try {
            return new URLCodec().decode(part, encoding);
        } catch (DecoderException | UnsupportedEncodingException e) {
            return "";
        }
    }

    /**
     * 使用默认密钥加密
     *
     * @param content 原文
     * @return 密文
     */
    public static String encodeAes(String content) {
        return encodeAes(KEY, content);
    }

    /**
     * 加密
     *
     * @param strKey  密钥
     * @param content 原文
     * @return 密文
     */
    public static String encodeAes(String strKey, String content) {
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
            cipher.init(Cipher.ENCRYPT_MODE,
                    new SecretKeySpec(strKey.getBytes(StandardCharsets.UTF_8), "AES"),
                    new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8))
            );
            byte[] encryptedByteArr = cipher.doFinal(content.getBytes());
            return encodeBase64(encryptedByteArr);
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 使用默认密钥解密
     *
     * @param content 密文
     * @return 原文
     */
    public static String decodeAes(String content) {
        return decodeAes(KEY, content);
    }

    /**
     * 解密
     *
     * @param strKey  密钥
     * @param content 密文
     * @return 原文
     */
    public static String decodeAes(String strKey, String content) {
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
            cipher.init(Cipher.DECRYPT_MODE,
                    new SecretKeySpec(strKey.getBytes(StandardCharsets.UTF_8), "AES"),
                    new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8))
            );
            byte[] encryptedByteArr = decodeBase64(content, false);
            byte[] original = cipher.doFinal(encryptedByteArr);
            return new String(original, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("解析AES串出错, 原串值:[{}]", content, e);
            return "";
        }
    }

    /**
     * 解密
     *
     * @param cipherText 解密方式
     * @param content    内容byte
     * @param keyByte    keyByte
     * @param ivByte     ivByte
     * @return 字符串
     */
    public static String decodeAes(String cipherText, byte[] content, byte[] keyByte, byte[] ivByte) {
        Security.addProvider(new BouncyCastleProvider());
        try {
            Cipher cipher = Cipher.getInstance(cipherText);
            cipher.init(Cipher.DECRYPT_MODE,
                    new SecretKeySpec(keyByte, "AES"),
                    new IvParameterSpec(ivByte)
            );
            byte[] original = cipher.doFinal(content);
            return new String(original, StandardCharsets.UTF_8);
        } catch (Exception e) {
            return "";
        }
    }


    /**
     * 加密
     *
     * @param plainData 明文
     * @param key       密钥
     * @return
     */
    public static byte[] encryptHex(byte[] plainData, byte[] key) {
        if (plainData == null || plainData.length == 0 || key == null) {
            return null;
        }
        int[] intArr = encryptHex(toIntArray(plainData, true), toIntArray(key, false));
        return toByteArray(intArr, false);
    }

    /**
     * 解密
     *
     * @param cipherData 密文
     * @param key        密钥
     * @return 明文
     */
    public static byte[] decryptHex(byte[] cipherData, byte[] key) {
        if (cipherData == null || cipherData.length == 0 || key == null) {
            return null;
        }
        int[] intArr = decryptHex(toIntArray(cipherData, false), toIntArray(key, false));
        return toByteArray(intArr, true);
    }

    /**
     * Encrypt data with key.
     *
     * @param v
     * @param k
     * @return
     */
    private static int[] encryptHex(int[] v, int[] k) {
        int n = v.length - 1;
        if (n < 1) {
            return v;
        }
        if (k.length < 4) {
            int[] key = new int[4];
            System.arraycopy(k, 0, key, 0, k.length);
            k = key;
        }
        int z = v[n], y = v[0], delta = 0x9E3779B9, sum = 0, e;
        int p, q = 6 + 52 / (n + 1);
        while (q-- > 0) {
            sum = sum + delta;
            e = sum >>> 2 & 3;
            for (p = 0; p < n; p++) {
                y = v[p + 1];
                z = v[p] += (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
            }
            y = v[0];
            z = v[n] += (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
        }
        return v;
    }

    /**
     * Decrypt data with key.
     *
     * @param v
     * @param k
     * @return
     */
    private static int[] decryptHex(int[] v, int[] k) {
        int n = v.length - 1;
        if (n < 1) {
            return v;
        }
        if (k.length < 4) {
            int[] key = new int[4];
            System.arraycopy(k, 0, key, 0, k.length);
            k = key;
        }
        int z = v[n], y = v[0], delta = 0x9E3779B9, sum, e;
        int p, q = 6 + 52 / (n + 1);

        sum = q * delta;
        while (sum != 0) {
            e = sum >>> 2 & 3;
            for (p = n; p > 0; p--) {
                z = v[p - 1];
                y = v[p] -= (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
            }
            z = v[n];
            y = v[0] -= (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
            sum = sum - delta;
        }
        return v;
    }

    /**
     * Convert byte array to int array.
     *
     * @param data
     * @param includeLength
     * @return
     */
    private static int[] toIntArray(byte[] data, boolean includeLength) {
        int n = (((data.length & 3) == 0) ? (data.length >>> 2) : ((data.length >>> 2) + 1));
        int[] result;

        if (includeLength) {
            result = new int[n + 1];
            result[n] = data.length;
        } else {
            result = new int[n];
        }
        n = data.length;
        for (int i = 0; i < n; i++) {
            result[i >>> 2] |= (0x000000ff & data[i]) << ((i & 3) << 3);
        }
        return result;
    }

    /**
     * Convert int array to byte array.
     *
     * @param data
     * @param includeLength
     * @return
     */
    private static byte[] toByteArray(int[] data, boolean includeLength) {
        int n = data.length << 2;
        if (includeLength) {
            int m = data[data.length - 1];

            if (m > n || m <= 0) {
                return null;
            } else {
                n = m;
            }
        }
        byte[] result = new byte[n];

        for (int i = 0; i < n; i++) {
            result[i] = (byte) ((data[i >>> 2] >>> ((i & 3) << 3)) & 0xff);
        }
        return result;
    }

    public static boolean checkPassword(String password) {
        String regexp = "^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[.=#?!@$%^&+*-]).{6,}$";
        return !StringUtils.isBlank(password) && password.matches(regexp);
    }

}
