package com.gci.boot.common.string;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.api.R;
import com.gci.boot.common.util.JsonUtil;
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.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 特殊字符切面
 */
@Aspect
@Slf4j
@Component("stringValidAspect")
public class StringValidAspect {

    @Value("${string.specialChar}")
    private String specialChar;

    @Pointcut("execution(public * com.gci.boot.modules..*.*Controller.*edit*(..)) " +
            " && !execution(public * com.gci.boot.modules.driver.controller.TbDriverController.edit(..)) " +
            " && !execution(public * com.gci.boot.modules.system.controller.SysUserController.edit(..)) " +
            " && !execution(public * com.gci.boot.modules.projects.controller.TbContractController.edit(..)) " +
            " && !execution(public * com.gci.boot.modules.system.controller.SysPermissionController.edit(..))" +
            " && !execution(public * com.gci.boot.modules.gis.controller.TbGisFencePlanController.edit(..))")
    public void excudeServiceEdit() {
    }

    @Before("excudeServiceEdit()")
    public Object doBeforeEdit(JoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        if(Objects.nonNull(args) && args.length>0){
            checkArgs(args);
        }
        return null;
    }

    @Pointcut("execution(public * com.gci.boot.modules..*.*Controller.*add*(..)) " +
            " && !execution(public * com.gci.boot.modules.driver.controller.TbDriverController.add(..)) " +
            " && !execution(public * com.gci.boot.modules.system.controller.SysUserController.add(..)) "+
            " && !execution(public * com.gci.boot.modules.projects.controller.TbContractController.add(..)) " +
            " && !execution(public * com.gci.boot.modules.system.controller.SysPermissionController.add(..)) " +
            " && !execution(public * com.gci.boot.modules.gis.controller.TbGisFencePlanController.add(..))")
    public void excudeServiceAdd() {
    }

    @Before("excudeServiceAdd()")
    public Object doBeforeAdd(JoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        if(Objects.nonNull(args) && args.length>0){
            checkArgs(args);
        }
        return null;
    }


    public void checkArgs(Object[] args) throws Exception {
        for (Object arg : args) {
            // 集合校验
            if (arg instanceof Collection) {
                checkCollection((Collection) arg);
            }
            // 实体对象校验
            if (arg instanceof Object) {
                Class<?> clazz = arg.getClass();
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field field : declaredFields) {
                    checkObject(arg, field);
                }
            }
            // 字符串类型校验
            if (arg instanceof String) {
                checkString((String) arg);
            }
        }
    }

    public void checkString(String arg) {
        //todo 校验字符串
        if(Objects.nonNull(arg) && arg.length() > 0){
            boolean isAllSpaces = true;
            for (int i = 0; i < arg.length(); i++) {
                if (!Character.isWhitespace(arg.charAt(i))) {
                    isAllSpaces = false;
                    break;
                }
            }
            if(isAllSpaces){
                throw new RuntimeException("输入的字符串存在全为空格，请修改！");
            }
        }
        if(StrUtil.isNotBlank(arg)){
            log.info("String type:" + arg);
            Pattern p = Pattern.compile(specialChar);
            Matcher m = p.matcher(arg);
            if (m.find()) {
                StringBuilder specialCharacters = new StringBuilder();
                do {
                    specialCharacters.append(m.group());
                } while (m.find());
                throw new RuntimeException("输入的字符串： "+ arg +" 包含特殊字符：" + specialCharacters.toString()+ " ,请修改！");
            }
        }
    }

    public void checkObject(Object object, Field field) throws Exception {
        log.info(field.getGenericType().toString());

        if (field.getGenericType().toString().equals("class java.lang.String")) {
            Method m = object.getClass().getMethod("get" + getMethodName(field.getName()));
            String val = (String) m.invoke(object);
            if (val != null) {
                checkString(val);
            }
        }  else if (field.getGenericType().toString().startsWith("class com.gci.boot.modules")) {
            // 对嵌套对象进行校验
            Method m = object.getClass().getMethod("get" + getMethodName(field.getName()));
            Object val = m.invoke(object);
            if (val != null) {
                Field[] nestedFields = val.getClass().getDeclaredFields();
                for (Field nestedField : nestedFields) {
                    checkObject(val, nestedField);
                }
            }
        } else if (field.getGenericType().toString().startsWith("java.util.List")) {
            // 对嵌套集合进行校验
            Method m = object.getClass().getMethod("get" + getMethodName(field.getName()));
            Object fieldValue = m.invoke(object);
            checkCollection((Collection) fieldValue);
        }

    }

    public void checkCollection(Collection arg) throws Exception {
        Collection<?> collectionParam = (Collection<?>) arg;
        if (CollectionUtils.isNotEmpty(collectionParam)) {
            List<?> listParam = Arrays.asList(collectionParam.toArray());
            Class<?> clazz = listParam.get(0).getClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Object listValue : listParam) {
                for (Field field : declaredFields) {
                    checkObject(listValue, field);
                }
            }
        }
        return;
    }

    // 把一个字符串的第一个字母大写、效率是最高的 get方法是驼峰命名的 因此要这样做
    private static String getMethodName(String fildeName) throws Exception {
        byte[] items = fildeName.getBytes();
        items[0] = (byte) ((char) items[0] - 'a' + 'A');
        return new String(items);
    }

}
