package com.github.wxiaoqi.security.common.support.aop;

import cn.hutool.core.util.ArrayUtil;
import com.github.wxiaoqi.security.common.annotation.BeanValid;
import com.github.wxiaoqi.security.common.annotation.SimpleValid;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import lombok.Data;
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.aspectj.lang.reflect.MethodSignature;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;

/**
 * @author libin
 * @version 1.0
 * @description  切面验证
 * @data 2019/6/13 13:46
 */
@Aspect
@Data
public class ValidParamAop {

     /**
      * 复合类型验证器
      */
     private Validator validator;

     /**
      * 简单类型验证器
      */
     private ExecutableValidator executableValidator;

     @Pointcut("execution(* com.upyuns.platform.rs..rest..*(..))")
     public void validatorExpress(){}

     @Around("validatorExpress()")
     public Object validatorAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{

          //获取方法的所有参数
          Object[] args = proceedingJoinPoint.getArgs();
          if (ArrayUtil.isEmpty(args)){
                //没有参数，不用验证
               return  proceedingJoinPoint.proceed();
          }

          MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
          Method currentMethod = methodSignature.getMethod();
          Parameter[] parameters = currentMethod.getParameters();

           boolean hashSimpleValid = false;

          int argLength = args.length;
          for (int i =0;i<argLength;i++){
               BeanValid beanValidAnnotation = parameters[i].getDeclaredAnnotation(BeanValid.class);
               if (Objects.nonNull(beanValidAnnotation)){
                    if ((args[i] instanceof Collection) || (args[i].getClass().isArray())){
                         Object[] arrayArg = null;
                         if(args[i] instanceof Collection){
                              arrayArg = ((Collection)args[i]).toArray();
                         }else {
                              arrayArg = (Object[])args[i];
                         }
                          for (Object item:arrayArg){
                               Set<ConstraintViolation<Object>> validateResult = this.validator.validate(item, beanValidAnnotation.value());
                               if (!validateResult.isEmpty()){
                                    String message = validateResult.iterator().next().getMessage();
                                    return ObjectRestResponse.createFailedResult(400,message);
                               }
                          }
                    }else {
                         Set<ConstraintViolation<Object>> validateResult = this.validator.validate(args[i], beanValidAnnotation.value());
                         if (!validateResult.isEmpty()){
                              String message = validateResult.iterator().next().getMessage();
                              return ObjectRestResponse.createFailedResult(400,message);
                         }
                    }
                    continue;
               }
               if (!hashSimpleValid){
                    SimpleValid  simpleValidAnnotation = parameters[i].getDeclaredAnnotation(SimpleValid.class);
                    if (Objects.nonNull(simpleValidAnnotation)){
                         hashSimpleValid = true;
                    }
               }
          }
          if (hashSimpleValid){
               Object target = proceedingJoinPoint.getTarget();
               Set<ConstraintViolation<Object>> validResult = this.executableValidator.validateParameters(target, currentMethod, args);
               if(!validResult.isEmpty()){
                    String message = validResult.iterator().next().getMessage();
                    return ObjectRestResponse.createFailedResult(400,message);
               }

          }
          return proceedingJoinPoint.proceed();
     }
}
