package com.css.txw.sso.util;

import com.css.ggzc.framework.common.util.gy.GyUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author KEBAO
 * @date 2023/5/9.
 */
public class AesUtil {

    public static final Charset DEFAULT_CHARSET;

    static {
        DEFAULT_CHARSET = StandardCharsets.UTF_8;
    }

    @Nullable
    public static String decryptFormBase64ToString(@Nullable String content, String aesTextKey) {
        byte[] hexBytes = decryptFormBase64(content, aesTextKey);
        return hexBytes == null ? null : new String(hexBytes, DEFAULT_CHARSET);
    }

    @Nullable
    public static byte[] decryptFormBase64(@Nullable String content, String aesTextKey) {
        return StringUtils.isEmpty(content) ? null : decryptFormBase64(content.getBytes(DEFAULT_CHARSET), aesTextKey);
    }

    public static byte[] decryptFormBase64(byte[] content, String aesTextKey) {
        return decrypt(Base64Utils.decode(content), aesTextKey);
    }

    public static byte[] decrypt(byte[] content, String aesTextKey) {
        return decrypt(content, ((String) Objects.requireNonNull(aesTextKey)).getBytes(DEFAULT_CHARSET));
    }

    public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
        return Pkcs7Encoder.decode(aes(encrypted, aesKey, 2));
    }

    private static byte[] aes(byte[] encrypted, byte[] aesKey, int mode) {
        Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");

        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
            cipher.init(mode, keySpec, iv);
            return cipher.doFinal(encrypted);
        } catch (Exception var6) {
            var6.printStackTrace();
            throw new RuntimeException();
        }
    }

    public static String encryptToBase64(String content, String aesTextKey) {
        return Base64Utils.encodeToString(encrypt(content, aesTextKey));
    }

    public static byte[] encrypt(String content, String aesTextKey) {
        return encrypt(content.getBytes(DEFAULT_CHARSET), aesTextKey);
    }

    public static byte[] encrypt(byte[] content, String aesTextKey) {
        return encrypt(content, ((String)Objects.requireNonNull(aesTextKey)).getBytes(DEFAULT_CHARSET));
    }

    public static byte[] encrypt(byte[] content, byte[] aesKey) {
        return aes(Pkcs7Encoder.encode(content), aesKey, 1);
    }

    private static class Pkcs7Encoder {
        private static final int BLOCK_SIZE = 32;

        private Pkcs7Encoder() {
        }

        private static byte[] encode(byte[] src) {
            int count = src.length;
            int amountToPad = 32 - count % 32;
            byte pad = (byte)(amountToPad & 255);
            byte[] pads = new byte[amountToPad];

            int length;
            for(length = 0; length < amountToPad; ++length) {
                pads[length] = pad;
            }

            length = count + amountToPad;
            byte[] dest = new byte[length];
            System.arraycopy(src, 0, dest, 0, count);
            System.arraycopy(pads, 0, dest, count, amountToPad);
            return dest;
        }

        private static byte[] decode(byte[] decrypted) {
            int pad = decrypted[decrypted.length - 1];
            if (pad < 1 || pad > 32) {
                pad = 0;
            }

            return pad > 0 ? Arrays.copyOfRange(decrypted, 0, decrypted.length - pad) : decrypted;
        }
    }

    public static List<Map<String, Object>> castListMapToTree(List<Map<String, Object>> listMap, String FieldName, String parentFieldName, String childFieldName) {
        //返回的map Tree树形结构
        List<Map<String, Object>> treeMap = new ArrayList<>();
        //将传进的参数entityList转为MapList
        //List<Map<String, Object>> listMap = JSON.parseObject(JSON.toJSONString(entityList), List.class);
        //声明一个map用来存listMap中的对象，key为对象id，value为对象本身
        Map<String, Map<String, Object>> entityMap = new Hashtable<>();
        //循环listMap把map对象put到entityMap中去
        listMap.forEach(map -> entityMap.put(map.get(FieldName).toString(), map));
        //循环listMap进行Tree树形结构组装
        listMap.forEach(map -> {
            //获取map的pid
            Object pid = map.get(parentFieldName);
            if (GyUtils.isNull(pid)){ //判断pid是否为空，为空说明是最顶级，直接add到返回的treeMap中去
                treeMap.add(map);
            } else { //如果pid不为空
                //根据当前map的pid获取上级 parentMap
                Map<String, Object> parentMap = entityMap.get(pid);
                if (GyUtils.isNull(parentMap)){ //如果parentMap为空，则说明当前map没有父级，当前map就是顶级
                    treeMap.add(map);
                } else {    //如果parentMap不为空，则当前map为parentMap的子级
                    //取出parentMap的所有子级的List集合
                    List<Map<String, Object>> children = (List<Map<String, Object>>)parentMap.get(childFieldName);
                    if (GyUtils.isNull(children)){  //判断子级集合是否为空，为空则新创建List
                        children = new ArrayList<>();
                        parentMap.put(childFieldName, children);
                    }
                    //把当前map对象add到parentMap的子级List中去
                    children.add(map);
                }
            }
        });
        return treeMap;
    }

}
