/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
 */
package com.crunii.micro.common.web.filter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import com.crunii.micro.common.constants.CommonConstants;
import com.crunii.micro.common.log.BusiLogComHttp;
import com.crunii.micro.common.utils.BusiLogUtil;
import com.crunii.micro.common.utils.IPUtil;
import com.crunii.micro.common.web.HttpServerHolder;

import lombok.extern.slf4j.Slf4j;

/**
 *
 * @author 田平 create 2020年1月14日下午5:16:48
 *
 */
@Slf4j
public class LoggingFilter extends OncePerRequestFilter {

	@Value("${spring.application.name:NONE}")
	private String appName;

	@Value("${server.port:-1}")
	private int port;

	@Autowired
	private BusiLogComHttp httpLog;

	public void doHttpLog() {
		Map<String, Object> busiInfo = HttpServerHolder.busiLogInfo.get();
		httpLog.busiLogByMap(busiInfo);
		busiInfo.clear();
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
		if (isAsyncDispatch(request)) {
			filterChain.doFilter(request, response);
		} else {
			doFilterWrapped(wrapRequest(request), wrapResponse(response), filterChain);
		}
	}

	protected void doFilterWrapped(ContentCachingRequestWrapper request, ContentCachingResponseWrapper response, FilterChain filterChain) throws ServletException, IOException {
		try {
			beforeRequest(request);
			filterChain.doFilter(request, response);
		} catch (Exception e) {
			BusiLogUtil.setErrorMsg(HttpServerHolder.busiLogInfo.get(), e);
			throw new RuntimeException(e);
		} finally {
			try {
				afterRequest(request, response);
			} catch (Exception e) {
				BusiLogUtil.setCallResult(HttpServerHolder.busiLogInfo.get(), response.getStatus());
				HttpServerHolder.busiLogInfo.get().put("errorMsg", "record log error,sendData and recvData maybe null!");
				String serverAddr = BusiLogUtil.getServerAddr(request, port);
				log.error("afterRequest error!serverAddr:{}", serverAddr, e);
			}
			response.copyBodyToResponse();
			doHttpLog();
		}
	}

	public void beforeRequest(ContentCachingRequestWrapper request) {
		Map<String, Object> busiInfo = HttpServerHolder.getBusiInfoAndClean();
		busiInfo.put("logType", CommonConstants.TYPE_SERVER);
		busiInfo.put("callTime", new Date());
		busiInfo.put("clientAddr", IPUtil.getIPAddr(request));
		busiInfo.put("serverAddr", BusiLogUtil.getServerAddr(request, port));
	}

	public void afterRequest(ContentCachingRequestWrapper request, ContentCachingResponseWrapper response) throws Exception {

		Map<String, Object> busiInfo = HttpServerHolder.busiLogInfo.get();

		Map<String, Object> recvData = new HashMap<>();
		BusiLogUtil.setRequestBase(recvData, request);

		boolean reqLog = BusiLogUtil.checkContentType(request.getContentType());
		if (reqLog) {
			byte[] content = request.getContentAsByteArray();
			String encoding = BusiLogUtil.getEncoding(request.getCharacterEncoding());
			if (content.length > 0) {
				recvData.put("body", new String(content, encoding));
			} else {
				recvData.put("body", null);
			}
		} else {
			log.info("serverAddr:{} contentType:{} not allowed auto record request body,please set body property!", busiInfo.get("serverAddr"), request.getContentType());
		}

		Map<String, Object> sendData = new HashMap<>();
		BusiLogUtil.setResponseBase(sendData, response);

		boolean rspLog = BusiLogUtil.checkContentType(response.getContentType());
		if (rspLog) {
			byte[] content = response.getContentAsByteArray();
			if (content != null && content.length > 0) {
				//服务端都是utf-8
				sendData.put("body", new String(content, StandardCharsets.UTF_8));
			} else {
				sendData.put("body", null);
			}
		} else {
			log.info("serverAddr:{} contentType:{} not allowed auto record response body please set body property!", busiInfo.get("serverAddr"), response.getContentType());
		}
		BusiLogUtil.setCallResult(busiInfo, response.getStatus());
		BusiLogUtil.setSendAndRecv(busiInfo, sendData, recvData);
		BusiLogUtil.setUseTime(busiInfo);
	}

	private static ContentCachingRequestWrapper wrapRequest(HttpServletRequest request) {
		if (request instanceof ContentCachingRequestWrapper) {
			return (ContentCachingRequestWrapper) request;
		} else {
			return new ContentCachingRequestWrapper(request);
		}
	}

	private static ContentCachingResponseWrapper wrapResponse(HttpServletResponse response) {
		if (response instanceof ContentCachingResponseWrapper) {
			return (ContentCachingResponseWrapper) response;
		} else {
			return new ContentCachingResponseWrapper(response);
		}
	}

}
