package cn.freemud.aop;

import cn.freemud.annotations.LogIgnoreFeign;
import cn.freemud.entities.vo.ThirdPartLogVo;
import cn.freemud.utils.AppLogUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.freemud.application.sdk.api.log.ThirdPartyLog;
import com.google.common.collect.Lists;
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.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.List;

/**
 * @author freemud
 * @title: LogIgnoreFeignAop
 * @projectName order-group
 * @description: TODO
 * @date 2021/6/9上午10:53
 */

@Aspect
@Component
public class LogIgnoreFeignAspect {

    /**
     * Apollo全局开关print-feign-response-body-log，一般是false，意思是全局都不打印响应报文，但是系统除了100/200等正常状态码之外，依然会打印响应报文，excludeStatusCodes = {ResponseCodeConstant.询问基础服务正常响应码}。
     */
    @Value("${print-feign-response-body-log:false}")
    private volatile boolean printFeignResponseBodyLog = false;

    /**
     * Apollo临时开关temp-feign-print-body-log-methods，平常都不想打印，只是偶尔想临时打印，使用添加logMessage = "aaa"的方法名称，用逗号拼接即可。
     */
    @Value("${temp-feign-print-body-log-methods:aaa,bbb}")
    private volatile List<String> tempFeignPrintBodyLogMethods = Lists.newArrayList();

    @Pointcut("execution(* cn.freemud.service.thirdparty..*.*(..))")
    public void clientLog() {
    }

    @Around("clientLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Exception ex) {
            ThirdPartLogVo thirdPartLogVo = AppLogUtil.createThirdPartLogVo(joinPoint);
            AppLogUtil.thirdPartError(start, System.currentTimeMillis(), thirdPartLogVo, null);
            throw ex;
        }
        try {
            Signature sig = joinPoint.getSignature();
            MethodSignature msig = null;
            if (sig instanceof MethodSignature) {
                msig = (MethodSignature) sig;
                Method currentMethod = sig.getDeclaringType().getDeclaredMethod(msig.getName(), msig.getParameterTypes());
                Object logReult = result;
                ThirdPartLogVo thirdPartLogVo = AppLogUtil.createThirdPartLogVo(joinPoint);
                JSONObject json = JSON.parseObject(JSON.toJSONString(result));
                String statusCodeValue = json.getString("code");
                String messageValue = json.getString("message");
                // 打印第三方出参日志   如果feign方法没有IgnoreFeignLogAnnotation注解,全部打日志,但是由于statusCodeValue = "ignore",是不是触发日志告警的
                LogIgnoreFeign logIgnore = currentMethod.getAnnotation(LogIgnoreFeign.class);
                if (logIgnore != null) {
                    statusCodeValue = json.getString(logIgnore.statusCodeFieldName());
                    messageValue = json.getString(logIgnore.messageFieldName());
                    if (!this.printFeignResponseBodyLog && !logIgnore.printInfoLog() && !this.tempFeignPrintBodyLogMethods.contains(logIgnore.logMessage())) {
                        //todo 当排除了这个状态码不打印响应的具体内容只会打印状态码和状态描述信息
                        String[] excludeStatusCodes = logIgnore.excludeStatusCodes();
                        if (!StringUtils.isEmpty(statusCodeValue) && this.containStatusCode(excludeStatusCodes, statusCodeValue)) {
                            logReult = result.getClass().newInstance();
                            org.apache.commons.beanutils.BeanUtils.setProperty(logReult, logIgnore.statusCodeFieldName(), statusCodeValue);
                            org.apache.commons.beanutils.BeanUtils.setProperty(logReult, logIgnore.messageFieldName(), messageValue);
                        }
                    }
                }
                ThirdPartyLog.infoConvertJson(statusCodeValue, messageValue, start, System.currentTimeMillis(),
                        thirdPartLogVo.getUri(), thirdPartLogVo.getRequestBody(), logReult);
            }
        } catch (Exception e) {
            AppLogUtil.errorLog("WebAspect Feign Log Ignore error {}", "","",e);
        }
        return result;
    }
//    @Around("clientLog()")
//    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//        long start = System.currentTimeMillis();
//        Object result = null;
//        try {
//            result = joinPoint.proceed();
//        } catch (Exception ex) {
//            ThirdPartLogVo thirdPartLogVo = LogUtil.createThirdPartLogVo(joinPoint);
//            LogUtil.thirdPartError(start, System.currentTimeMillis(), thirdPartLogVo, null);
//            throw ex;
//        }
//        ThirdPartLogVo thirdPartLogVo = LogUtil.createThirdPartLogVo(joinPoint);
//        // 打印第三方出参日志
//        ThirdPartyLog.infoConvertJson(LogThreadLocal.getTrackingNo(), SDKCommonBaseContextWare.getAppName(),start,System.currentTimeMillis(),
//                thirdPartLogVo.getUri(),thirdPartLogVo.getRequestBody(),result);
//        return result;
//    }


    /**
     * 过滤返参code是否在excludeStatusCodes存在
     *
     * @param excludeStatusCodes
     * @param statusCodeValue
     * @return
     */
    private boolean containStatusCode(String[] excludeStatusCodes, String statusCodeValue) {
        if (excludeStatusCodes == null || excludeStatusCodes.length == 0) {
            return false;
        }
        for (int i = 0; i < excludeStatusCodes.length; i++) {
            if (excludeStatusCodes[i].equals(statusCodeValue)) {
                return true;
            }
        }
        return false;
    }

}
