/*
 * Copyright [2020-2030] [https://www.stylefeng.cn]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Guns采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Guns源码头部的版权声明。
 * 3.请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://gitee.com/stylefeng/guns
 * 5.在修改包名，模块名称，项目代码等时，请注明软件出处 https://gitee.com/stylefeng/guns
 * 6.若您的项目无法满足以上几点，可申请商业授权
 */
package cn.stylefeng.roses.kernel.scanner.api.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.stylefeng.roses.kernel.scanner.api.enums.ParamTypeEnum;
import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.ParameterMetadata;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import static cn.stylefeng.roses.kernel.scanner.api.constants.ScannerConstants.DEFAULT_VALIDATED;

/**
 * 反射工具类，获取方法的一些元数据
 *
 * @author fengshuonan
 * @since 2020/12/8 17:48
 */
@Slf4j
public class MethodReflectUtil {

    /**
     * 获取方法上的注解
     * <p>
     * 注意，此方法只获取方法第一个参数的所有注解
     *
     * @param method 方法反射信息
     * @return 方法参数上的注解集合
     * @author fengshuonan
     * @since 2020/12/8 17:49
     */
    public static List<Annotation> getMethodFirstParamAnnotations(Method method) {
        if (method == null) {
            return null;
        }

        if (method.getParameterCount() <= 0) {
            return null;
        }

        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        if (parameterAnnotations.length == 0) {
            return null;
        }

        // 只获取第一个参数的所有注解，所以下标为0
        Annotation[] resultAnnotations = parameterAnnotations[0];
        if (resultAnnotations == null || resultAnnotations.length == 0) {
            return null;
        } else {
            return CollectionUtil.toList(resultAnnotations);
        }
    }

    /**
     * 获取方法上校验分组
     * <p>
     * 例如：获取如下方法的校验分组信息SysAppRequest.edit.class
     * <pre>
     * public ResponseData edit(@RequestBody @Validated(SysAppRequest.edit.class) SysAppRequest sysAppParam) {
     *     ...
     * }
     * </pre>
     *
     * @param method 方法反射信息
     * @return 方法的参数校验分组信息
     * @author fengshuonan
     * @since 2020/12/8 17:59
     */
    public static Set<String> getMethodValidateGroup(Method method) {
        List<Annotation> methodFirstParamAnnotations = getMethodFirstParamAnnotations(method);
        if (methodFirstParamAnnotations == null) {
            return null;
        }

        // 判断annotation有没有是@Validated注解类型的
        try {
            for (Annotation annotation : methodFirstParamAnnotations) {
                if (Validated.class.equals(annotation.annotationType())) {
                    Method validateGroupMethod = annotation.annotationType().getMethod("value");
                    Object invoke = validateGroupMethod.invoke(annotation);
                    if (invoke != null) {
                        Class<?>[] result = (Class<?>[]) invoke;
                        HashSet<String> groupClassNames = new HashSet<>();
                        if (result.length > 0) {
                            for (Class<?> groupClass : result) {
                                groupClassNames.add(groupClass.getSimpleName());
                            }
                        } else {
                            groupClassNames.add(DEFAULT_VALIDATED);
                        }
                        return groupClassNames;
                    }
                }
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            log.error("获取方法上的校验分组出错！", e);
        }
        return null;
    }

    /**
     * 返回方法的所有类型参数信息
     *
     * @param method 方法反射信息
     * @return 方法第一个参数的class类型
     * @author fengshuonan
     * @since 2020/12/8 18:16
     */
    public static Type[] getMethodGenericTypes(Method method) {
        if (method == null) {
            return null;
        }
        return method.getGenericParameterTypes();
    }

    /**
     * 获取方法的返回值type类型，type可能是class也可能是带泛型的类型
     *
     * @param method 方法反射信息
     * @return 方法返回值的class类型
     * @author fengshuonan
     * @since 2020/12/8 18:20
     */
    public static Type getMethodReturnType(Method method) {
        if (method == null) {
            return null;
        }
        return method.getGenericReturnType();
    }

    /**
     * 获取方法的所有参数元数据信息
     *
     * @author fengshuonan
     * @since 2022/1/20 11:51
     */
    public static List<ParameterMetadata> getMethodParameterInfos(Method method) {
        List<ParameterMetadata> result = new LinkedList<>();

        if (method == null) {
            return result;
        }

        Parameter[] parameters = method.getParameters();
        if (parameters.length == 0) {
            return result;
        }

        for (Parameter parameter : parameters) {
            ParameterMetadata parameterMetadata = new ParameterMetadata();

            // 设置type类型
            Type parameterizedType = parameter.getParameterizedType();
            parameterMetadata.setParameterizedType(parameterizedType);

            // 设置注解
            Annotation[] annotations = parameter.getAnnotations();
            parameterMetadata.setAnnotations(annotations);

            // 设置参数是param参数还是request body参数
            parameterMetadata.setParamTypeEnum(getParamTypeEnum(annotations));

            // 设置参数名
            parameterMetadata.setParameterName(parameter.getName());

            result.add(parameterMetadata);
        }

        return result;
    }

    /**
     * 根据参数上的注解判断出是param参数还是request body参数
     *
     * @author fengshuonan
     * @since 2022/1/20 13:43
     */
    public static ParamTypeEnum getParamTypeEnum(Annotation[] annotations) {

        // 注解为空，直接判断为param参数
        if (annotations == null || annotations.length == 0) {
            return ParamTypeEnum.QUERY_PARAM;
        }

        // 如果注解中包含@RequestBody注解，则是json请求
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().equals(RequestBody.class)) {
                return ParamTypeEnum.REQUEST_BODY;
            }
        }

        // 其他情况，判定为时param参数
        return ParamTypeEnum.QUERY_PARAM;
    }

}
