package org.superboot.global.wrapper;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xiaoleilu.hutool.crypto.asymmetric.KeyType;
import com.xiaoleilu.hutool.crypto.asymmetric.RSA;
import com.xiaoleilu.hutool.lang.Base64;
import com.xiaoleilu.hutool.util.CharsetUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import org.superboot.base.BaseConstants;
import org.superboot.base.BaseException;
import org.superboot.base.StatusCode;
import org.superboot.utils.AESUtil;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.util.Enumeration;
import java.util.Set;

/**
 * <b> 重构上行请求信息，对上行内容进行解密操作 </b>
 * <p>
 * 功能描述:重构上行请求信息，主要是对已经加密后的Body内容进行统一解密操作
 * </p>
 */
public class RequestBodyDecryptWrapper extends HttpServletRequestWrapper {
    private Logger log = LoggerFactory.getLogger(getClass());


    private byte[] requestBody = null;

    public RequestBodyDecryptWrapper(HttpServletRequest request)
            throws IOException {
        super(request);
        try {
            requestBody = StreamUtils.copyToByteArray(request.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (requestBody == null) {
            requestBody = new byte[0];
        }
        //变更请求内容
        setRequestBody();
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }
        };
    }


    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public String getHeader(String name) {
        return super.getHeader(name);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        return super.getHeaderNames();
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        return super.getHeaders(name);
    }

    /**
     * 将RequestBody参数转为字符串
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    public String getRequestBody() throws UnsupportedEncodingException {
        return (new String(requestBody, "UTF-8"));
    }

    /**
     * 变更Json对象内容信息
     *
     * @throws UnsupportedEncodingException
     */
    public void setRequestBody() throws UnsupportedEncodingException {
        //读取Body内容后变更为对象
        String body = getRequestBody();
        if (StrUtil.isNotBlank(body)) {
            Object json = null;
            //扑捉转换异常调用JSON转换
            try {
                json = JSON.parse(body);
            } catch (Exception e) {
                json = JSON.toJSON(body);
            }
            String secretKey = getHeader(BaseConstants.SECRET_KEY);

            log.debug("--------------执行密钥解密------------------");
            //执行RSA解密
            try {
                secretKey = rsaDecrypt(secretKey);
            } catch (Exception e) {
                throw new BaseException(StatusCode.DECODE_FAIL);
            }
            log.debug("--------------执行数据解密------------------");
            if (StrUtil.isNotBlank(secretKey)) {
                //判断是否为数组
                if (json instanceof JSONArray) {
                    JSONArray jsonArray = JSON.parseArray(json.toString());
                    jsonArray = aesJsonArray(secretKey, jsonArray);
                    //返回变更后的对象信息
                    this.requestBody = jsonArray.toJSONString().getBytes("UTF-8");
                } else if (json instanceof JSONObject) {
                    JSONObject jsonObject = JSON.parseObject(json.toString());
                    jsonObject = aesJsonObject(secretKey, jsonObject);
                    //返回变更后的对象信息
                    this.requestBody = jsonObject.toJSONString().getBytes("UTF-8");
                } else {
                    //非JSON对象直接返回到后台业务内处理
                    this.requestBody = json.toString().getBytes("UTF-8");
                }

            }
        }

    }

    /**
     * 解密JSON数组对象
     *
     * @param secretKey 密钥
     * @param jsonArray JSON数组
     * @return
     */
    public JSONArray aesJsonArray(String secretKey, JSONArray jsonArray) {
        for (int i = 0; i < jsonArray.size(); i++) {
            //判断是否为JSON对象
            if (jsonArray.get(i) instanceof JSONObject) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                //变更JSON数组对象
                jsonArray.set(i, aesJsonObject(secretKey, jsonObject));
            } else {
                //非JSON对象直接执行解密
                jsonArray.set(i, aesDecrypt(secretKey, jsonArray.getString(i)));
            }

        }
        return jsonArray;
    }


    /**
     * 解密JSON对象
     *
     * @param secretKey  密钥
     * @param jsonObject JSON数组
     * @return
     */
    public JSONObject aesJsonObject(String secretKey, JSONObject jsonObject) {
        Set<String> keys = jsonObject.keySet();
        for (String key : keys) {
            //获取内部嵌套对象是否为数组，如果是数组则进行数组循环解密
            Object jsonItem = jsonObject.get(key);
            if (null != jsonItem) {
                if (jsonItem instanceof JSONArray) {
                    JSONArray jsonArray = JSON.parseArray(jsonItem.toString());
                    jsonArray = aesJsonArray(secretKey, jsonArray);
                    //便跟对象内容
                    jsonObject.put(key, jsonArray);
                }else if (jsonItem instanceof JSONObject) {
                    JSONObject item = JSON.parseObject(jsonItem.toString());
                    item = aesJsonObject(secretKey, item);
                    jsonObject.put(key, item);
                }  else {
                    //获取内容
                    String value = jsonItem.toString();
                    //执行AES解密
                    value = aesDecrypt(secretKey, value);
                    //便跟对象内容
                    jsonObject.put(key, value);
                }
            }

        }

        return jsonObject;
    }


    /**
     * 执行数据解密
     *
     * @param secretKey 密钥
     * @param value     加密后内容
     * @return
     */
    public String aesDecrypt(String secretKey, String value) {
        //解密密文信息
        try {
            String deValue = AESUtil.aesDecrypt(value, secretKey);
            log.debug("--------------执行字段解密------------------");
            return deValue;
        } catch (Exception e) {
            throw new BaseException(StatusCode.DECODE_FAIL);
        }
    }

    /**
     * RSA解密
     *
     * @param text 密文
     * @return
     */
    public String rsaDecrypt(String text) throws Exception {
        //使用hutool进行RSA解密，这个的优势是免去使用第三方JAR包的依赖
        RSA rsa = new RSA(BaseConstants.DEFAULT_PKCS8_PRIVATE_KEY, BaseConstants.DEFAULT_PUBLIC_KEY);
        //URL转码
        return new String(rsa.decrypt(Base64.decode(text), KeyType.PrivateKey), CharsetUtil.UTF_8);
    }

}