package com.cm.common.aspect;

import com.cm.common.annotation.CheckRequestBodyAnnotation;
import com.cm.common.exception.ParamsException;
import com.cm.common.utils.annotation.AnnotationUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
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.bind.annotation.RequestBody;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: ApiParamsAspect
 * @Description: api接口
 * @Author: WangGeng
 * @Date: 2019/11/14 15:36
 * @Version: 1.0
 **/
@Aspect
@Component
@Order(1)
public class ApiParamsAspect {
    private static final Logger LOG = LoggerFactory.getLogger(ApiParamsAspect.class);

    @Pointcut("execution(public * com.cm.*.controller..*.*(..))")
    public void apiCutPoint() {
    }

    @Pointcut("execution(public * com.cm.common.plugin.controller..*.*(..))")
    public void pluginCutPoint() {
    }

    @Pointcut("execution(public * com.cm.common.plugin.oauth.controller..*.*(..))")
    public void oauthCutPoint() {
    }

    @Pointcut("execution(public * com.cm.common.article.controller..*.*(..))")
    public void articleCutPoint() {
    }

    @Pointcut("execution(public * com.cm.common.wechat.controller..*.*(..))")
    public void wechatCutPoint() {
    }

    @Pointcut("execution(public * com.cm.central.control.controller..*.*(..))")
    public void centralControlPoint() {
    }

    @Before("apiCutPoint()")
    public void beforeApiCutPoint(JoinPoint joinPoint) throws ParamsException {
        beforeCutPoint(joinPoint);
    }

    @Before("pluginCutPoint()")
    public void beforePluginCutPoint(JoinPoint joinPoint) throws ParamsException {
        beforeCutPoint(joinPoint);
    }

    @Before("oauthCutPoint()")
    public void beforeOauthCutPoint(JoinPoint joinPoint) throws ParamsException {
        beforeCutPoint(joinPoint);
    }

    @Before("articleCutPoint()")
    public void beforeArticleCutPoint(JoinPoint joinPoint) throws ParamsException {
        beforeCutPoint(joinPoint);
    }

    @Before("wechatCutPoint()")
    public void beforeWechatCutPoint(JoinPoint joinPoint) throws ParamsException {
        beforeCutPoint(joinPoint);
    }

    @Before("centralControlPoint()")
    public void beforeCentralControlPoint(JoinPoint joinPoint) throws ParamsException {
        beforeCutPoint(joinPoint);
    }

    private void beforeCutPoint(JoinPoint joinPoint) throws ParamsException {
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        Class<?> targetClazz = joinPoint.getTarget().getClass();
        Method method = getMethod(signature.getName(), targetClazz.getMethods(), args);
        if (method == null) {
            return;
        }

        if (method.isAnnotationPresent(CheckRequestBodyAnnotation.class)) {
            LOG.debug("校验参数");
            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters) {
                if (parameter.isAnnotationPresent(RequestBody.class)) {
                    for (Object arg : args) {
                        if (parameter.getType() == arg.getClass()) {
                            AnnotationUtil.checkField(arg);
                        }
                    }
                }
            }
        }
    }

    private Method getMethod(String methodName, Method[] methods, Object[] args) {
        for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 方法名称相同
            if (StringUtils.equals(methodName, method.getName())) {
                // 参数长度相同
                if (args.length == parameterTypes.length) {
                    // 参数类型顺序相同
                    boolean isThisMethod = true;
                    for (int argIndex = 0; argIndex < args.length; argIndex++) {
                        if (!parameterTypes[argIndex].isInstance(args[argIndex])) {
                            isThisMethod = false;
                            break;
                        }
                    }
                    if (isThisMethod) {
                        return method;
                    }
                }
            }
        }
        return null;
    }

}
