package com.heyu.api.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.heyu.api.data.constants.ApiConstants;
import com.heyu.api.data.service.bussiness.RedisSettingService;
import com.heyu.api.data.utils.*;
import com.heyu.api.utils.OsUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 日志aop
 * Created by wutao on 2018/10/12.
 */
@Aspect
@Component
@Order(1)
@Slf4j
public class LogAop {

    private String BLANK_SPACE = " ";

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public static Map<String, Boolean> faceAuthMap = new HashMap<>();

    public static Map<String, String> faceAuthTentMap = new HashMap<>();

    @Value("${eb.config.rabbitQueue.accountAmountQueue}")
    private String accountAmountQueue;

    @Autowired
    private RedisSettingService redisSettingService;

    @Pointcut(value = "(" + "    @within(org.springframework.web.bind.annotation.RestController) || " + "    @within(org.springframework.stereotype.Controller)" + ")")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        Object result = null;
        StringBuilder sb = new StringBuilder();
        String methodName = "";
        String ip = "";
        String args = "";
        String className = "";
        String uri = "";
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String traceId = attributes.getRequest().getHeader("traceId");
        if (StringUtils.isBlank(traceId)) {
            traceId = OrderUtil.getUserPoolOrder("on");
        }
        HttpServletRequest request = attributes.getRequest();
        uri = attributes.getRequest().getRequestURI();

        if (uri.startsWith("/mm/") || uri.startsWith("/app/")) {
            return point.proceed();

        }

        args = recordRequestLog(point.getArgs(), uri);
        Signature sig = point.getSignature();
        Method method = ((MethodSignature) sig).getMethod();
        if (sig instanceof MethodSignature) {
            methodName = ((MethodSignature) sig).getMethod().getName();
        }

        Class clazz = point.getTarget().getClass();
        className = clazz.getName();
        // 类名加方法名
        String classNameAndMethodName = className + "#" + methodName;
        String classNameAndMethodNametencent = className + "#" + methodName + "#tencent";

        sb.append("LoggerAop args:").append(args).append(BLANK_SPACE)
                .append("class:").append(className).append("#").append(methodName).append(BLANK_SPACE)
                .append("uri:").append(uri);


        // 默认对有的接口进行拦截
        Boolean intercept = faceAuthMap.get(className);
        Boolean classMethodConfigNotIntercept = faceAuthMap.get(classNameAndMethodName);

        if (intercept == null) {
            boolean exitClassAnnotation = AnnotationUtils.hasAnnotation(clazz, ApiConstants.NOT_INTERCEPT_ANNOTATION_NAME);
            boolean exitMethodAnnotation = AnnotationUtils.hasAnnotation(method, ApiConstants.NOT_INTERCEPT_ANNOTATION_NAME);
            String tencent = AnnotationUtils.getAnnotation(method, ApiConstants.EB_AUTHENTICATION);
            // 如果存在NotIntercept则不进行拦截，否则进行拦截
            intercept = exitClassAnnotation ? false : true;
            classMethodConfigNotIntercept = exitMethodAnnotation ? true : false;

            faceAuthMap.put(className, intercept);
            faceAuthMap.put(classNameAndMethodName, classMethodConfigNotIntercept);
            faceAuthTentMap.put(classNameAndMethodNametencent, tencent);
            log.info("LogAop one cache className:{},classNameAndMethodName:{},classNameAndMethodNametencent:{}", intercept, classMethodConfigNotIntercept, tencent);
        }
        if (classMethodConfigNotIntercept !=null && classMethodConfigNotIntercept) {
            return point.proceed();
        }
        try {
            MDC.put("traceId", traceId);

            // 如果方法和类上都没有配置NotIntercept注解 ，则需要拦截 ,是linux才进行拦截
            if (intercept && OsUtil.isLinux()) {
                String authConfig = request.getHeader("X-TCloudMarket-Custom-AuthConfig");
                log.info("LogAop X-TCloudMarket-Custom-AuthConfig authConfig:{}", authConfig);
                if (StringUtils.isBlank(authConfig) || !authConfig.startsWith("{")) {
                    log.info("LogAop authConfig value:{} auth error ", authConfig);
                    result = R.error("auth error");
                    return result;
                }

                Map<String, Object> authConfigMap = JSONObject.parseObject(authConfig, Map.class);
                String auth = faceAuthTentMap.get(classNameAndMethodNametencent);
                Object tAuth = authConfigMap.get(ApiConstants.t_auth);

                if (StringUtils.isBlank(auth) || !auth.equals(tAuth + "")) {
                    log.info("LogAop authConfig:{},auth:{},tAuth:{} auth error ", authConfig, auth, tAuth);
                    result = R.error("auth error");
                    return result;
                }
            }
            result = point.proceed();
        } catch (Exception e) {
            log.error("LogAop set error " + sb.toString(), e);
        } finally {
            printLog(result, args, ip, className, methodName, uri);
            MDC.remove("traceId");
        }
        return result;
    }


    public void printLog(Object result, String args, String ip, String className, String methodName,
                         String preUri) {
        StringBuilder sb = new StringBuilder();
        sb.append("LoggerAop args:").append(args).append(BLANK_SPACE)
                .append("ip:").append(ip).append(BLANK_SPACE)
                .append("class:").append(className).append("#").append(methodName).append(BLANK_SPACE)
                .append("uri:").append(preUri).append(BLANK_SPACE)
                .append("result:").append(JSON.toJSONString(result));
        log.info(sb.toString());
    }


    private String recordRequestLog(Object[] argArrs, String uri) {
        String mediaType = "";
        String args = null;
        try {
            List<Object> list = new ArrayList<>();
            if (argArrs != null && argArrs.length > 0) {
                for (Object arg1 : argArrs) {
                    if (arg1 instanceof MultipartFile) {
                        continue;
                    } else if (arg1 instanceof HttpServletRequest) {
                        continue;
                    } else if (arg1 instanceof HttpServletResponse) {
                        continue;
                    } else if (arg1 instanceof MultipartFile[]) {
                        continue;
                    } else {
                        list.add(arg1);
                    }
                }
            }
            Object jsonData = list.size() == 1 ? list.get(0) : list;
            args = JSON.toJSONString(jsonData);
            return args;
        } catch (Exception e) {
            log.error("url " + uri + " , uri " + uri + " ,mediaType " + mediaType + ", 打印请求日志错误" + args, e);
        }

        return "";
    }
}
