package cn.stylefeng.roses.kernel.scanner.api.factory;

import cn.stylefeng.roses.kernel.rule.enums.FieldTypeEnum;
import cn.stylefeng.roses.kernel.scanner.api.constants.ScannerConstants;
import cn.stylefeng.roses.kernel.scanner.api.context.MetadataContext;
import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.FieldMetadata;
import cn.stylefeng.roses.kernel.scanner.api.util.AdvancedClassTypeUtil;
import cn.stylefeng.roses.kernel.scanner.api.util.ClassDescriptionUtil;
import cn.stylefeng.roses.kernel.scanner.api.util.FieldDescriptionUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * 字段信息创建工具
 *
 * @author fengshuonan
 * @since 2022/1/13 13:49
 */
@Slf4j
public class ClassDetailMetadataFactory {

    /**
     * 根据传入的类型，解析出这个类型的所有子字段类型
     *
     * @param fieldType 需要被解析的对象的类型，可以是class也可以是泛型
     * @param uuid      随机字符串，保证唯一性，用来标识从开始到结束一个context周期内的一系列解析
     * @author fengshuonan
     * @since 2022/1/14 14:31
     */
    public static Set<FieldMetadata> createFieldDetailMetadataSet(Type fieldType, String uuid) {

        // 获取参数的类型枚举
        FieldTypeEnum classFieldType = AdvancedClassTypeUtil.getClassFieldType(fieldType);

        // 设置响应结果
        Set<FieldMetadata> fieldMetadata = null;

        // 根据字段类型的不同进行响应的处理
        switch (classFieldType) {
            case BASIC:
                // 基本类型不需要填充子类型信息
                fieldMetadata = null;
                break;
            case BASE_ARRAY:
                // 如果是简单数组类型，则描述一下这个基本类型的信息
                Class<?> baseArrayFieldClazz = (Class<?>) fieldType;
                Class<?> baseArrayComponentType = baseArrayFieldClazz.getComponentType();
                FieldMetadata baseMetadata = ClassDescriptionUtil.createClassMetadata(baseArrayComponentType, FieldTypeEnum.BASIC, uuid);
                fieldMetadata = new LinkedHashSet<>();
                fieldMetadata.add(baseMetadata);
                break;
            case ARRAY_WITH_OBJECT:
                // 如果是带实体的数组，则加上实体对应的字段信息
                Class<?> objArrayClass = (Class<?>) fieldType;
                Class<?> objArrayComponentType = objArrayClass.getComponentType();
                fieldMetadata = ClassDetailMetadataFactory.createFieldDetailMetadataSet(objArrayComponentType, uuid);
                break;
            case BASE_COLLECTION:
                // 如果是基础集合，因为不确定集合的内容，所以使用Object来描述一下集合的具体内容
                FieldMetadata collectionFieldMetadata = ClassDescriptionUtil.createClassMetadata(Object.class, FieldTypeEnum.OBJECT, uuid);
                fieldMetadata = new LinkedHashSet<>();
                fieldMetadata.add(collectionFieldMetadata);
                break;
            case COLLECTION_WITH_OBJECT:
                // 如果是集合里带的具体实体对象，则描述一下具体实体的数据结构
                ParameterizedType collectionParameterizedType = (ParameterizedType) fieldType;
                Type[] actualTypeArguments = collectionParameterizedType.getActualTypeArguments();
                fieldMetadata = ClassDetailMetadataFactory.createFieldDetailMetadataSet(actualTypeArguments[0], uuid);
                break;
            case OBJECT:
                // 如果是实体对象，则描述实体对象的所有字段信息
                Class<?> objectClass = (Class<?>) fieldType;
                Field[] fields = objectClass.getDeclaredFields();
                fieldMetadata = new LinkedHashSet<>();

                // 在处理Object中所有字段之前，将当前父类放进context，所有子字段不能含有父类的类型，否则会递归
                MetadataContext.addClassRecord(uuid, objectClass.getName());

                for (Field field : fields) {
                    FieldMetadata fieldInfo;

                    // 判断字段是否是基础字段例如serialVersionUID，或者delFlag等字段
                    if (ScannerConstants.DONT_PARSE_FIELD.contains(field.getName())) {
                        continue;
                    }

                    // 判断该实体是否被解析过，防止无限递归解析实体
                    if (MetadataContext.ensureFieldClassHaveParse(uuid, field.getGenericType())) {
                        fieldInfo = FieldDescriptionUtil.createBasicMetadata(field, uuid);
                    } else {
                        fieldInfo = FieldDescriptionUtil.createFieldMetadata(field, uuid);
                    }
                    fieldMetadata.add(fieldInfo);
                }
                break;
            case OBJECT_WITH_GENERIC:
                // 如果是带泛型的Object实体类型
                ParameterizedType objWithGenericParameterizedType = (ParameterizedType) fieldType;
                // 先获取主体类型
                Type rawType = objWithGenericParameterizedType.getRawType();
                // 获取具体泛型
                Type genericType = objWithGenericParameterizedType.getActualTypeArguments()[0];

                // 判断带泛型的实体，有没有进行做字段解析，如果解析过，则跳过
                String totalName = fieldType.getTypeName() + genericType.getTypeName();
                if (MetadataContext.ensureFieldClassHaveParse(uuid, totalName)) {
                    return null;
                }
                MetadataContext.addClassRecord(uuid, totalName);

                // 获取主体的所有字段信息
                fieldMetadata = getEntityWithGenericFieldMetadataList(rawType, genericType, uuid);
            default:
        }

        return fieldMetadata;
    }

    /**
     * 获取实体带泛型类型的字段填充详情，例如PageResult<SysUser>这种字段
     *
     * @author fengshuonan
     * @since 2022/1/14 18:51
     */
    public static Set<FieldMetadata> getEntityWithGenericFieldMetadataList(Type fieldType, Type genericType, String uuid) {
        if (fieldType instanceof Class<?>) {
            Class<?> clazz = (Class<?>) fieldType;
            // 获取主类型的所有属性
            Set<FieldMetadata> fieldDetailMetadataSet = createFieldDetailMetadataSet(clazz, uuid);
            if (fieldDetailMetadataSet == null) {
                return null;
            }
            for (FieldMetadata fieldMetadata : fieldDetailMetadataSet) {
                // 如果是带泛型集合，如下情况List<T>，又或是直接 T 这种形式
                if (FieldTypeEnum.COLLECTION_WITH_OBJECT.getCode().equals(fieldMetadata.getFieldType())
                        || FieldTypeEnum.WITH_UNKNOWN_GENERIC.getCode().equals(fieldMetadata.getFieldType())) {
                    // 设置这个字段的子字段描述
                    fieldMetadata.setGenericFieldMetadata(createFieldDetailMetadataSet(genericType, uuid));
                }

                // 如果T在携带在一个实体类上，例如ResponseData<T>这种形式
                else if (FieldTypeEnum.OBJECT_WITH_GENERIC.getCode().equals(fieldMetadata.getFieldType())) {
                    // 设置这个字段的子字段描述
                    Set<FieldMetadata> current = null;
                    try {
                        current = getEntityWithGenericFieldMetadataList(Class.forName(fieldMetadata.getFieldClassPath()), genericType, uuid);
                    } catch (ClassNotFoundException e) {
                        log.error("类无法找到" + fieldMetadata.getFieldClassPath(), e);
                        continue;
                    }
                    fieldMetadata.setGenericFieldMetadata(current);
                }

                // 如果是T这种形式，应该将当前fieldMetadata的类型改为泛型的类型，例如参数genericType是List时候
                if (FieldTypeEnum.WITH_UNKNOWN_GENERIC.getCode().equals(fieldMetadata.getFieldType())) {
                    FieldTypeEnum classFieldType = AdvancedClassTypeUtil.getClassFieldType(genericType);
                    fieldMetadata.setFieldType(classFieldType.getCode());
                }
            }

            return fieldDetailMetadataSet;
        } else {
            log.debug("带泛型的实体主题rawType非Class类型" + fieldType);
            return null;
        }
    }

}
