package cn.wisenergy.chnmuseum.party.common.mvc;

import cn.wisenergy.chnmuseum.party.common.enums.RESPONSE_CODE_ENUM;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * 参数校验切面
 */
@Slf4j
@Aspect
@Component
public class RequestParamValidAspect {

    private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
    private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    private final ExecutableValidator validator = factory.getValidator().forExecutables();

    private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object[] params) {
        return validator.validateParameters(obj, method, params);
    }

    @Pointcut("execution(* cn.wisenergy.chnmuseum.party.web.*.*.controller.*.*(..))")
    public void controllerBefore() {
    }

    @Before("controllerBefore()")
    public void before(JoinPoint point) {
        // 获得切入方法参数
        Object[] args = point.getArgs();
        if (args.length == 0) {
            return;
        }

        /**************************校验封装好的javabean**********************/
        //寻找带BindingResult参数的方法,然后判断是否有error,如果有则是校验不通过
//            for (Object arg : args) {
//                if (arg instanceof BeanPropertyBindingResult) {
//                    //有校验
//                    BeanPropertyBindingResult result = (BeanPropertyBindingResult) arg;
//                    if (result.hasErrors()) {
//                        List<ObjectError> list = result.getAllErrors();
//                        for (ObjectError error : list) {
//                            log.info(error.getCode() + "---" + Arrays.toString(error.getArguments()) + "--" + error.getDefaultMessage());
//                            //返回第一条校验失败信息。也可以拼接起来返回所有的
//                            return error.getDefaultMessage();
//                        }
//                    }
//                }
//            }

        /**************************校验普通参数*************************/
        Object target = point.getThis();
        // 获得切入的方法
        Method method = ((MethodSignature) point.getSignature()).getMethod();

        // 执行校验,获得校验结果
        Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, args);
        //如果有校验不通过的
        if (!validResult.isEmpty()) {
            // 获得方法的参数名称
            String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);

            for (ConstraintViolation<Object> constraintViolation : validResult) {
                // 获得校验的参数路径信息
                PathImpl pathImpl = (PathImpl) constraintViolation.getPropertyPath();
                // 获得校验的参数位置
                int paramIndex = pathImpl.getLeafNode().getParameterIndex();
                assert parameterNames != null;
                // 获得校验的参数名称
                String paramName = parameterNames[paramIndex];
                //校验信息
                log.info(paramName + ": " + constraintViolation.getMessage());
            }
            //返回第一条校验失败信息,也可以拼接起来返回所有的
            String message = validResult.iterator().next().getMessage();
            throw new InterfaceException(RESPONSE_CODE_ENUM.PARAM_NOT_VALID.getResultCode(), message);
        }
    }

}