package cn.com.poc.common.aspect;

import cn.com.poc.common.utils.JsonUtils;
import cn.com.yict.framemax.core.i18n.I18nMessageException;
import cn.com.yict.framemax.core.utils.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

@Aspect
@Component
@Order(-10)
public class RestLoggingAspect {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(* *..rest..*Rest.*(..))")
    public void restMethod() { // Do nothing
    }

    @Around("restMethod()")
    public Object restMethodAround(ProceedingJoinPoint pjp) throws Throwable {

        Date before = new Date();
        Object retVal = null;
        Object[] args = pjp.getArgs();
        String requestPath = StringUtils.EMPTY;

        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        if (sra == null) {
            return null;
        }
        HttpServletRequest request = sra.getRequest();

        requestPath = request.getRequestURI();

        //将controller入参中的 HttpServletRequest,HttpServletResponse类型参数剔除 不输出日志
        List<Object> objects = new ArrayList<>(Arrays.asList(args));

        objects.removeIf(next -> next instanceof HttpServletRequest || next instanceof HttpServletResponse);

        String argJson = JSON.serialize(objects);
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = pjp.getSignature().getName();
        String remoteAddr = request.getRemoteAddr();

        //避免上传图片将图片base64 字符打印
        if (requestPath.toLowerCase().contains("upload")) {
            logger.info("[Class:{}][Method:{}][Path:{}][Arguments:{}][Start]", className, methodName, requestPath, "upload images");
        } else {
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (parameterMap != null && parameterMap.size() > 0) {
                logger.info("[Class:{}][Method:{}][Path:{}][RequestParams:{}][Arguments:{}][Start]", className, methodName, requestPath, JsonUtils.serialize(parameterMap), argJson);
            } else {
                logger.info("[Class:{}][Method:{}][Path:{}][Arguments:{}][Start]", className, methodName, requestPath, argJson);
            }

        }

        try {
            retVal = pjp.proceed();
        } catch (I18nMessageException e) { // 业务层异常

            logger.error("[Class:{}][Method:{}][Path:{}][BizException:{}-{}]", className, methodName, requestPath, e.getErrorCode(), e.getMessage());
            throw e;
        } catch (Throwable e) {// 系統异常

            logger.error("[Class:{}][Method:{}][Path:{}][Exception:{}]", className, methodName, requestPath, e.getMessage());
            throw e;
        } finally {
            try {
                String printResponse = null;
                if (retVal != null) {

                    printResponse = JsonUtils.serialize(retVal);
                    //返回数据过大，不打印
                    if (StringUtils.isNotBlank(printResponse) && printResponse.length() > 2048) {
                        printResponse = "返回数据过长，忽略输出";
                    }
                }
                logger.info("[Class:{}][Method:{}][Path:{}][results:{}][Interval:{}ms][End]", className, methodName, requestPath, printResponse,
                        System.currentTimeMillis() - before.getTime());
            } catch (Exception e) {
                logger.error("输出日志结果异常,忽略", e);
            }

        }

        return retVal;
    }

}

