package com.esv.freight.file.common.filter;

import com.alibaba.fastjson.JSONObject;
import com.esv.freight.file.common.constants.CommonConstants;
import com.esv.freight.file.common.util.ReqUtils;
import com.esv.freight.file.common.wrapper.RestRequestWrapper;
import com.esv.freight.file.common.wrapper.RestResponseWrapper;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: Rest请求日志Filter
 * @project: SpringCloudTemplate
 * @name: com.esv.springcloud.template.web.filter.RestLogFilter
 * @author: 黄朝斌
 * @email: huangchaobin@esvtek.com
 * @createTime: 2020/03/09 17:44
 * @version:1.0
 */
@Slf4j
public class RestLogFilter implements Filter {

    private Map<String, Integer> API_BODY_LOG_MAP = new HashMap<>();

    @Override
    public void init(FilterConfig filterConfig) {
        // 设置API日志级别[5->4->3->2->1->0]：Fatal->error->warn->info->debug->trace
        API_BODY_LOG_MAP.put("/file/upload/single", 1);
        API_BODY_LOG_MAP.put("/file/download/getFileData", 2);
        API_BODY_LOG_MAP.put("/file/delete/single", 2);;
        API_BODY_LOG_MAP.put("/file/ocr/idcard/front", 1);
        API_BODY_LOG_MAP.put("/file/upload/imageWithWatermark", 1);
    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        RestRequestWrapper requestWrapper = new RestRequestWrapper((HttpServletRequest)servletRequest);
        RestResponseWrapper responseWrapper = new RestResponseWrapper((HttpServletResponse) servletResponse);

        // 日志输出请求体
        this.logReq(requestWrapper);

        // 日志输出请求头
        this.logReqHeader(requestWrapper);

        filterChain.doFilter(requestWrapper, responseWrapper);

        // 日志输出返回体
        this.logRes(requestWrapper, responseWrapper);
    }

    /**
     * 获取请求返回体
     **/
    private String getPostBodyStr(HttpServletRequest request) {
        String bodyStr = null;
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName(CommonConstants.DEFAULT_CHARACTER_ENCODING)));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }

            if (0 == sb.length()) {
                Map<String, String> bodyMap = new HashMap<>();
                Map<String, String[]> parameterMap = request.getParameterMap();
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    for (String value : entry.getValue()) {
                        bodyMap.put(entry.getKey(), value);
                    }
                }
                bodyStr = bodyMap.toString();
            } else {
                bodyStr = sb.toString();
            }
        } catch (IOException e) {
            log.error("解析post参数时发生错误：{}", e.getMessage(), e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
        return bodyStr;
    }

    /**
     * 日志输出请求体
     **/
    private void logReq(RestRequestWrapper requestWrapper) {
        String url = requestWrapper.getRequestURI();
        String method = requestWrapper.getMethod();
        String reqBody = "";

        // 获取get、post请求体
        if (CommonConstants.HTTP_REQUEST_METHOD_GET.equalsIgnoreCase(method)) {
            Enumeration em = requestWrapper.getParameterNames();
            while (em.hasMoreElements()) {
                String k = em.nextElement().toString();
                String v = requestWrapper.getParameter(k);
                reqBody += "&" + k + "=" + v;
            }
            reqBody = reqBody.replaceFirst("&", "");
        } else if (CommonConstants.HTTP_REQUEST_METHOD_POST.equalsIgnoreCase(method)) {
            reqBody = this.getPostBodyStr(requestWrapper);
        }

        // 请求体日志截取
        if (CommonConstants.LOG_MAX_LENGTH < reqBody.length()) {
            reqBody = reqBody.substring(0, CommonConstants.LOG_CUT_LENGTH) + "……";
        }

        // 日志输出请求体
        if (url.startsWith(CommonConstants.SPRING_BOOT_ADMIN_PREFIX_URL)) {
            if (log.isDebugEnabled()) {
                log.debug("[IP={}]收到{}请求，url：{}，params：{}", ReqUtils.getHttpClientIp(requestWrapper), method, url, reqBody);
            }
        } else {
            log.info("[IP={}]收到{}请求，url：{}", ReqUtils.getHttpClientIp(requestWrapper), method, url);
            if (API_BODY_LOG_MAP.containsKey(url) && 1 < API_BODY_LOG_MAP.get(url)) {
                log.info("body：{}", reqBody);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("body：{}", reqBody);
                }
            }
        }
    }

    /**
     * 日志输出请求头
     **/
    private void logReqHeader(RestRequestWrapper request) {
        JSONObject headerJson = new JSONObject();
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            headerJson.put(key, request.getHeader(key));
        }
        log.info("请求头：{}", headerJson.toJSONString());
    }

    /**
     * 日志输出返回体
     **/
    private void logRes(RestRequestWrapper requestWrapper, RestResponseWrapper responseWrapper) {
        byte[] bytes = responseWrapper.getBody();
        String resBody = null;
        try {
            resBody = new String(bytes, CommonConstants.DEFAULT_CHARACTER_ENCODING);
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        }
        String url = requestWrapper.getRequestURI();
        if (url.startsWith(CommonConstants.SPRING_BOOT_ADMIN_PREFIX_URL)) {
            if (log.isDebugEnabled()) {
                log.debug("请求响应：{}", resBody);
            }
        } else if (url.startsWith(CommonConstants.FILE_DOWNLOAD_PREFIX_URL)) {
            if (log.isDebugEnabled()) {
                log.debug("请求响应：{}", resBody);
            } else {
                log.info("请求响应文件数据");
            }
        } else {
            log.info("请求响应：{}", resBody);
        }
    }

}