package com.sptg.common.core.filter;

import com.sptg.common.core.constant.HttpConstant;
import com.sptg.common.core.util.AesEncryptionUtil;
import com.sptg.common.core.util.WebUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * title: EncryptionFilter
 * package: com.sptg.gateway.filter
 * description: 加解密过滤器
 *
 *                  
 *               
 */
@Slf4j
public class EncryptionFilter implements Filter {

    /**
     * 不需要拦截的请求列表
     */
    @Setter
    private List<String> excludeList;

    @Override
    public void init(FilterConfig filterConfig){

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String uri = request.getRequestURI();
        //  过滤浏览器自动请求logo
        if (uri.endsWith(HttpConstant.LOGO_ICON)) {
            return;
        }
        //  过滤不需要拦截的请求
        if (excludeList.contains(uri)){
            chain.doFilter(servletRequest,servletResponse);
            return;
        }
        //  判断请求体是否为json
        if (!HttpConstant.APPLICATION_JSON.equals(request.getContentType()) &&
                !HttpConstant.APPLICATION_JSON_UTF8.equals(request.getContentType())){
            OutputStream out = servletResponse.getOutputStream();
            out.write("请求头ContentType需为json".getBytes());
            return;
        }
//        log.warn("当前毫秒值:" + System.currentTimeMillis());
//        //  获取时间戳(密钥)
//        String secretKey = request.getHeader(HttpConstant.TIME_STAMP);
//        if (StringUtils.isEmpty(secretKey)){
//            return;
//        }
        //  获取签名
        String sign = request.getHeader(HttpConstant.DATA_SIGN);
        if (StringUtils.isEmpty(sign)){
            servletResponse.getOutputStream().write("请添加data-sign".getBytes());
            return;
        }
        //  获取请求报文
        String requestBody = WebUtils.getRequestBody(request);
//        log.warn("密文:" + requestBody);
//        try{
//            //  对报文进行解密
//            requestBody = AesEncryptionUtil.aesDecrypt(secretKey, requestBody);
//        }catch (Exception e){
//            log.debug("解密失败");
//            return;
//        }
        //  将解密后的报文进行Md5,校验签名
        String md5Body = DigestUtils.md5Hex(requestBody);
        if (!sign.equals(md5Body)){
            OutputStream out = servletResponse.getOutputStream();
            out.write("请求头缺少签名参数".getBytes());
            return;
        }
//        log.warn("明文:" + requestBody);
//        log.warn("当前毫秒值:" + System.currentTimeMillis());
        //  调用接口
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        WrapperResponse wrapperResponse = new WrapperResponse(response);
        chain.doFilter(new WrapperRequest(request,requestBody),wrapperResponse);
        //  获取响应体
        String responseBody = new String(wrapperResponse.getResponseBody(), StandardCharsets.UTF_8);
        try {
//            加密密钥
//            secretKey =  String.valueOf(System.currentTimeMillis());
//            response.setHeader(HttpConstant.TIME_STAMP,secretKey);
            //  数据签名
            response.setHeader(HttpConstant.DATA_SIGN,DigestUtils.md5Hex(responseBody));
//            //  数据加密
//            String data = AesEncryptionUtil.aesEncryption(secretKey, responseBody);
            WebUtils.writeResponse(response,responseBody,HttpConstant.TEXT_PLAIN);
        }catch (Exception e){
            log.error("数据加密异常",e.getMessage());
        }
    }

    @Override
    public void destroy() {

    }

    /**
     * 功能描述: Response 封装类
     *                  
     *                     
     */
    public class WrapperResponse extends HttpServletResponseWrapper {

        /**
         * 真正存储数据的流
         */
        private final ByteArrayOutputStream buffer;
        private final ServletOutputStream outputStream;
        private final PrintWriter printWriter;

        /**
         * Constructs a response adaptor wrapping the given response.
         * @throws IllegalArgumentException if the response is null
         */
        private WrapperResponse(HttpServletResponse response) {
            super(response);
            buffer = new ByteArrayOutputStream();
            printWriter = new PrintWriter(new OutputStreamWriter(buffer,StandardCharsets.UTF_8));
            outputStream = new ServletOutputStream(){

                @Override
                public void write(int b) {
                    buffer.write(b);
                }

                @Override
                public void write(byte[] b, int off, int len){
                    buffer.write(b, off, len);
                }

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

                @Override
                public void setWriteListener(WriteListener writeListener) {

                }
            };
        }

        @Override
        public ServletOutputStream getOutputStream(){
            return this.outputStream;
        }

        @Override
        public PrintWriter getWriter(){
            return this.printWriter;
        }

        @Override
        public void flushBuffer() throws IOException {
            if (null != this.outputStream){
                this.outputStream.flush();
            }
            if (null != this.printWriter){
                this.printWriter.flush();
            }
        }

        @Override
        public void reset() {
            this.buffer.reset();
        }

        /**
         * 功能描述: 获取响应体的字节数组
         *                  
         *                     
         */
        private byte[] getResponseBody()throws IOException{
            this.flushBuffer();
            return this.buffer.toByteArray();
        }
    }

    /**
     * 功能描述: Request 封装类
     *                  
     *                     
     */
    public class WrapperRequest extends HttpServletRequestWrapper {

        /**
         * 请求报文
         */
        private final String requestBody;
        private final HttpServletRequest request;

        /**
         * Constructs a request object wrapping the given request.
         * @param request The request to wrap
         * @param requestBody 请求报文
         * @throws IllegalArgumentException if the request is null
         */
        private WrapperRequest(HttpServletRequest request,String requestBody) {
            super(request);
            this.request = request;
            this.requestBody = requestBody;
        }

        @Override
        public int getContentLength() {
            return this.requestBody.length();
        }

        @Override
        public BufferedReader getReader(){
            return new BufferedReader(new StringReader(this.requestBody));
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            return new ServletInputStream() {

                private InputStream inputStream = new ByteArrayInputStream(requestBody.getBytes(request.getCharacterEncoding()));

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

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

                @Override
                public void setReadListener(ReadListener listener) {

                }

                @Override
                public int read() throws IOException {
                    return this.inputStream.read();
                }

                @Override
                public int read(byte[] buffer, int off, int len) throws IOException {
                    if (null == buffer) {
                        throw new NullPointerException();
                    } else if (off < 0 || len < 0 || len > buffer.length - off) {
                        throw new IndexOutOfBoundsException();
                    } else if (len == 0) {
                        return 0;
                    }
                    return this.inputStream.read(buffer, off, len);
                }
            };
        }
    }
}
