package ink.wgink.gateway.util;

import ink.wgink.gateway.annoation.*;
import ink.wgink.gateway.exception.ParamsException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: ReportBodyFieldCheckUtil
 * @Description: 上报内容字段校验
 * @Author: wanggeng
 * @Date: 2021/4/23 3:49 下午
 * @Version: 1.0
 */
public class RequestFieldCheckUtil {

    public static <T> void check(T t) {
        Class<?> clazz = t.getClass();
        List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        setSuperClassField(clazz, fields);
        Method[] methods = clazz.getMethods();
        try {
            for (Field field : fields) {
                Method method = getGetMethodByField(methods, field.getName());
                if (method == null) {
                    continue;
                }
                Object fieldValue = method.invoke(t);
                if (field.isAnnotationPresent(CheckNullAnnotation.class)) {
                    CheckNullAnnotation checkNullAnnotation = field.getAnnotation(CheckNullAnnotation.class);
                    if (fieldValue == null) {
                        throw new ParamsException(String.format("%s不能为空", checkNullAnnotation.name()));
                    }
                    checkTypes(checkNullAnnotation.name(), fieldValue.toString(), checkNullAnnotation.types());
                } else if (field.isAnnotationPresent(CheckEmptyAnnotation.class)) {
                    CheckEmptyAnnotation checkEmptyAnnotation = field.getAnnotation(CheckEmptyAnnotation.class);
                    if (fieldValue == null || StringUtils.isBlank(fieldValue.toString())) {
                        throw new ParamsException(String.format("%s不能为空或空串", checkEmptyAnnotation.name()));
                    }
                    if(checkEmptyAnnotation.noBlank() && (fieldValue.toString().contains(" ") || fieldValue.toString().contains("　"))) {
                        throw new ParamsException(String.format("%s不能有空格", checkEmptyAnnotation.name()));
                    }
                    checkRegular(checkEmptyAnnotation.name(), fieldValue.toString(), checkEmptyAnnotation.verifyType(), checkEmptyAnnotation.regex());
                    checkTypes(checkEmptyAnnotation.name(), fieldValue.toString(), checkEmptyAnnotation.types());
                } else if (field.isAnnotationPresent(CheckNumberAnnotation.class)) {
                    CheckNumberAnnotation checkNumberAnnotation = field.getAnnotation(CheckNumberAnnotation.class);
                    if (fieldValue == null || !NumberUtils.isNumber(fieldValue.toString())) {
                        throw new ParamsException(String.format("%s必须为数字", checkNumberAnnotation.name()));
                    }
                    Double value = Double.parseDouble(fieldValue.toString());
                    if (value < checkNumberAnnotation.min()) {
                        throw new ParamsException(String.format("%s最小值为%f", checkNumberAnnotation.name(), checkNumberAnnotation.min()));
                    }
                    if (value > checkNumberAnnotation.max()) {
                        throw new ParamsException(String.format("%s最大值为%f", checkNumberAnnotation.name(), checkNumberAnnotation.max()));
                    }
                    checkTypes(checkNumberAnnotation.name(), fieldValue.toString(), checkNumberAnnotation.types());
                } else if (field.isAnnotationPresent(CheckBooleanAnnotation.class)) {
                    CheckBooleanAnnotation checkBooleanAnnotation = field.getAnnotation(CheckBooleanAnnotation.class);
                    if (fieldValue == null) {
                        throw new ParamsException(String.format("%s必须为布尔", checkBooleanAnnotation.name()));
                    }
                    checkTypes(checkBooleanAnnotation.name(), fieldValue.toString(), checkBooleanAnnotation.types());
                } else if (field.isAnnotationPresent(CheckListAnnotation.class)) {
                    CheckListAnnotation checkListAnnotation = field.getAnnotation(CheckListAnnotation.class);
                    if (fieldValue == null) {
                        throw new ParamsException(String.format("%s不能为空", checkListAnnotation.name()));
                    }
                    if (fieldValue instanceof List) {
                        List fieldValueList = (List) fieldValue;
                        if (fieldValueList.isEmpty()) {
                            throw new ParamsException(String.format("%s不能为空", checkListAnnotation.name()));
                        }
                        for (Object obj : fieldValueList) {
                            checkField(obj);
                        }
                    } else if (fieldValue instanceof String[]) {
                        String[] fieldValueArray = (String[]) fieldValue;
                        for (Object obj : fieldValueArray) {
                            checkField(obj);
                        }
                    }
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            throw new ParamsException("系统错误");
        }
    }

    /**
     * 校验字段
     *
     * @param object
     * @throws ParamsException
     */
    public static void checkField(Object object) throws ParamsException {
        Class<?> clazz = object.getClass();
        List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        setSuperClassField(clazz, fields);
        Method[] methods = clazz.getMethods();
        try {
            for (Field field : fields) {
                Method method = getGetMethodByField(methods, field.getName());
                if (method == null) {
                    continue;
                }
                Object fieldValue = method.invoke(object);
                if (field.isAnnotationPresent(CheckNullAnnotation.class)) {
                    CheckNullAnnotation checkNullAnnotation = field.getAnnotation(CheckNullAnnotation.class);
                    if (fieldValue == null) {
                        throw new ParamsException(String.format("%s不能为空", checkNullAnnotation.name()));
                    }
                    checkTypes(checkNullAnnotation.name(), fieldValue.toString(), checkNullAnnotation.types());
                } else if (field.isAnnotationPresent(CheckEmptyAnnotation.class)) {
                    CheckEmptyAnnotation checkEmptyAnnotation = field.getAnnotation(CheckEmptyAnnotation.class);
                    if (fieldValue == null || StringUtils.isBlank(fieldValue.toString())) {
                        throw new ParamsException(String.format("%s不能为空或空串", checkEmptyAnnotation.name()));
                    }
                    checkRegular(checkEmptyAnnotation.name(), fieldValue.toString(), checkEmptyAnnotation.verifyType(), checkEmptyAnnotation.regex());
                    checkTypes(checkEmptyAnnotation.name(), fieldValue.toString(), checkEmptyAnnotation.types());
                } else if (field.isAnnotationPresent(CheckNumberAnnotation.class)) {
                    CheckNumberAnnotation checkNumberAnnotation = field.getAnnotation(CheckNumberAnnotation.class);
                    if (fieldValue == null || !NumberUtils.isNumber(fieldValue.toString())) {
                        throw new ParamsException(String.format("%s必须为数字", checkNumberAnnotation.name()));
                    }
                    Double value = Double.parseDouble(fieldValue.toString());
                    if (value < checkNumberAnnotation.min()) {
                        throw new ParamsException(String.format("%s最小值为%f", checkNumberAnnotation.name(), checkNumberAnnotation.min()));
                    }
                    if (value > checkNumberAnnotation.max()) {
                        throw new ParamsException(String.format("%s最大值为%f", checkNumberAnnotation.name(), checkNumberAnnotation.max()));
                    }
                    checkTypes(checkNumberAnnotation.name(), fieldValue.toString(), checkNumberAnnotation.types());
                } else if (field.isAnnotationPresent(CheckBooleanAnnotation.class)) {
                    CheckBooleanAnnotation checkBooleanAnnotation = field.getAnnotation(CheckBooleanAnnotation.class);
                    if (fieldValue == null) {
                        throw new ParamsException(String.format("%s必须为布尔", checkBooleanAnnotation.name()));
                    }
                    checkTypes(checkBooleanAnnotation.name(), fieldValue.toString(), checkBooleanAnnotation.types());
                } else if (field.isAnnotationPresent(CheckListAnnotation.class)) {
                    CheckListAnnotation checkListAnnotation = field.getAnnotation(CheckListAnnotation.class);
                    if (fieldValue == null) {
                        throw new ParamsException(String.format("%s不能为空", checkListAnnotation.name()));
                    }
                    if (fieldValue instanceof List) {
                        List fieldValueList = (List) fieldValue;
                        if (fieldValueList.isEmpty()) {
                            throw new ParamsException(String.format("%s不能为空", checkListAnnotation.name()));
                        }
                        for (Object obj : fieldValueList) {
                            checkField(obj);
                        }
                    } else if (fieldValue instanceof String[]) {
                        String[] fieldValueArray = (String[]) fieldValue;
                        for (Object obj : fieldValueArray) {
                            checkField(obj);
                        }
                    }
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            throw new ParamsException("系统错误");
        }
    }

    /**
     * 检查类型
     *
     * @param name
     * @param value
     * @param types
     * @throws ParamsException
     */
    private static void checkTypes(String name, String value, String[] types) throws ParamsException {
        if (types != null && types.length > 0) {
            StringBuilder typeSB = new StringBuilder();
            for (String type : types) {
                if (StringUtils.equals(value, type)) {
                    return;
                }
                if (typeSB.length() > 0) {
                    typeSB.append(",");
                }
                typeSB.append("\"").append(type).append("\"");
            }
            throw new ParamsException(String.format("%s必须是如下类型：[%s]", name, typeSB.toString()));
        }
    }

    /**
     * 检查正则
     *
     * @param name
     * @param value
     * @param verifyType
     * @param regex
     */
    private static void checkRegular(String name, String value, String verifyType, String regex) {
        if (StringUtils.isBlank(verifyType)) {
            return;
        }
        if (StringUtils.equals("username", verifyType)) {
            if (!RegexUtil.isUsername(value)) {
                throw new ParamsException(String.format("%s格式只能是字母、数字和下划线", name));
            }
            return;
        } else if (StringUtils.equals("phone", verifyType)) {
            if (!RegexUtil.isPhone(value)) {
                throw new ParamsException(String.format("%s格式非手机格式", name));
            }
            return;
        } else if (StringUtils.equals("email", verifyType)) {
            if (!RegexUtil.isEmail(value)) {
                throw new ParamsException(String.format("%s格式非邮件格式", name));
            }
            return;
        } else if (StringUtils.equals("url", verifyType)) {
            if (!RegexUtil.isUrl(value)) {
                throw new ParamsException(String.format("%s格式非url格式", name));
            }
            return;
        } else if (StringUtils.equals("path", verifyType)) {
            if (!RegexUtil.isPath(value)) {
                throw new ParamsException(String.format("%s格式非路径格式", name));
            }
            return;
        } else if (StringUtils.equals("number", verifyType)) {
            if (!NumberUtils.isNumber(value)) {
                throw new ParamsException(String.format("%s格式非数字格式", name));
            }
            return;
        } else if (StringUtils.equals("date", verifyType)) {
            if (!RegexUtil.isDate(value)) {
                throw new ParamsException(String.format("%s格式非日期格式", name));
            }
            return;
        } else if (StringUtils.equals("datetime", verifyType)) {
            if (!RegexUtil.isDatetime(value)) {
                throw new ParamsException(String.format("%s格式非时间戳格式", name));
            }
            return;
        } else if (StringUtils.equals("identity", verifyType)) {
            if (!RegexUtil.isIdentity(value)) {
                throw new ParamsException(String.format("%s格式非身份证格式", name));
            }
            return;
        } else if (StringUtils.equals("letter", verifyType)) {
            if (!RegexUtil.isLetter(value)) {
                throw new ParamsException(String.format("%s格式非字母格式", name));
            }
            return;
        } else if (StringUtils.equals("chinese", verifyType)) {
            if (!RegexUtil.isLetter(value)) {
                throw new ParamsException(String.format("%s格式非中文格式", name));
            }
            return;
        } else if (StringUtils.equals("custom", verifyType)) {
            if (StringUtils.isBlank(regex)) {
                return;
            }
            if (!Pattern.compile(regex).matcher(value).matches()) {
                throw new ParamsException(String.format("%s格式不正确", name));
            }
        }
    }

    /**
     * 通过字段获取get方法
     *
     * @param methods
     * @param fieldName
     * @return
     */
    public static Method getGetMethodByField(Method[] methods, String fieldName) {
        String getMethodName = String.format("get%s", fieldName).toLowerCase();
        for (Method method : methods) {
            if (StringUtils.equals(getMethodName, method.getName().toLowerCase())) {
                return method;
            }
        }
        return null;
    }

    /**
     * 设置父类属性列表
     *
     * @param clazz
     * @param fields
     */
    private static void setSuperClassField(Class<?> clazz, List<Field> fields) {
        Class<?> superClazz = clazz.getSuperclass();
        if (superClazz == null) {
            return;
        }
        Field[] superFields = superClazz.getDeclaredFields();
        if (superFields == null) {
            return;
        }
        fields.addAll(Arrays.asList(superFields));
        setSuperClassField(superClazz, fields);
    }

}
