package com.seer.teach.mp.admin.utils;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import io.swagger.v3.oas.annotations.media.Schema;

import java.lang.annotation.Annotation;
import java.time.temporal.Temporal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 类字段信息提取工具类（支持递归、泛型、注解）
 */
public class ClassFieldUtils {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final Map<ClassCacheKey, List<FieldInfo>> FIELD_INFO_CACHE = new ConcurrentHashMap<>();

    // 注解处理器注册表（可扩展）
    private static final Map<Class<? extends Annotation>, AnnotationProcessor> PROCESSORS = new HashMap<>();

    static {
        // 配置 ObjectMapper 访问所有字段
        MAPPER.setVisibility(MAPPER.getSerializationConfig()
                .getDefaultVisibilityChecker()
                .withFieldVisibility(JsonAutoDetect.Visibility.ANY));

        // 注册内置处理器
        PROCESSORS.put(Schema.class, new ApiModelPropertyProcessor());
        // 示例：后续可添加 @Schema、@NotNull 等支持
        // PROCESSORS.put(Schema.class, new SchemaProcessor());
    }

    // ===================== Public API =====================

    /**
     * 获取类的字段信息（指定注解类）
     *
     * @param className         完整类名
     * @param annotationClass   注解类型
     * @return 字段信息列表
     * @throws ClassNotFoundException 类未找到
     */
    public static List<FieldInfo> getFieldInfo(String className, Class<? extends Annotation> annotationClass)
            throws ClassNotFoundException {
        Class<?> clazz = Class.forName(className);
        return getFieldInfo(clazz, annotationClass);
    }

    /**
     * 获取类的字段信息（指定注解类）—— 安全版本（不抛异常）
     *
     * @param className         完整类名
     * @param annotationClass   注解类型
     * @return Optional 包装的结果
     */
    public static Optional<List<FieldInfo>> getFieldInfoSafe(String className, Class<? extends Annotation> annotationClass) {
        try {
            return Optional.of(getFieldInfo(className, annotationClass));
        } catch (Exception e) {
            return Optional.empty();
        }
    }

    /**
     * 获取类的字段信息（指定注解类）
     *
     * @param clazz            类对象
     * @param annotationClass  注解类型
     * @return 字段信息列表
     */
    public static List<FieldInfo> getFieldInfo(Class<?> clazz, Class<? extends Annotation> annotationClass) {
        Objects.requireNonNull(clazz, "clazz cannot be null");
        Objects.requireNonNull(annotationClass, "annotationClass cannot be null");

        ClassCacheKey key = new ClassCacheKey(clazz.getName(), annotationClass.getName());
        return FIELD_INFO_CACHE.computeIfAbsent(key, k -> doGetFieldInfo(k.toClass(), k.annotationClass));
    }

    /**
     * 清空缓存
     */
    public static void clearCache() {
        FIELD_INFO_CACHE.clear();
    }

    /**
     * 注册自定义注解处理器（支持扩展）
     */
    public static void registerProcessor(Class<? extends Annotation> annType, AnnotationProcessor processor) {
        PROCESSORS.put(annType, processor);
    }

    // ===================== 核心实现 =====================

    private static List<FieldInfo> doGetFieldInfo(Class<?> clazz, Class<? extends Annotation> annotationClass) {
        List<FieldInfo> result = new ArrayList<>();
        Set<String> processedClassNames = new HashSet<>(); // 使用类名避免 ClassLoader 问题
        collectFieldInfo(clazz, result, processedClassNames, "", annotationClass);
        return result;
    }

    private static void collectFieldInfo(Class<?> clazz,
                                         List<FieldInfo> result,
                                         Set<String> processedClassNames,
                                         String parentPath,
                                         Class<? extends Annotation> annotationClass) {
        String className = clazz.getName();
        if (shouldSkipClass(clazz, processedClassNames)) {
            return;
        }
        processedClassNames.add(className);

        JavaType javaType = MAPPER.constructType(clazz);
        BeanDescription beanDesc = MAPPER.getSerializationConfig().introspect(javaType);

        for (BeanPropertyDefinition prop : beanDesc.findProperties()) {
            FieldInfo fieldInfo = new FieldInfo();
            processFieldInfo(prop, fieldInfo, parentPath, annotationClass);
            result.add(fieldInfo);

            // 处理泛型类型（如 List<User> 中的 User）
            JavaType primaryType = prop.getPrimaryType();
            if (primaryType.hasGenericTypes()) {
                for (JavaType paramType : primaryType.getBindings().getTypeParameters()) {
                    Class<?> rawClass = paramType.getRawClass();
                    if (!isBasicType(paramType) && !processedClassNames.contains(rawClass.getName())) {
                        collectFieldInfo(rawClass, result, processedClassNames, fieldInfo.getName(), annotationClass);
                    }
                }
            }

            Class<?> rawClass = primaryType.getRawClass();
            if (!isBasicType(primaryType) && !rawClass.isArray() && !processedClassNames.contains(rawClass.getName())) {
                collectFieldInfo(rawClass, result, processedClassNames, fieldInfo.getName(), annotationClass);
            }
        }
    }

    private static void processFieldInfo(BeanPropertyDefinition prop,
                                         FieldInfo fieldInfo,
                                         String parentPath,
                                         Class<? extends Annotation> annotationClass) {
        String fieldName = prop.getName();
        String fullPath = parentPath.isEmpty() ? fieldName : parentPath + "." + fieldName;
        fieldInfo.setName(fullPath);

        JavaType fieldType = prop.getPrimaryType();
        Class<?> rawType = fieldType.getRawClass();
        fieldInfo.setType(rawType.getSimpleName());

        // 泛型信息
        if (fieldType.hasGenericTypes()) {
            String genericTypes = fieldType.getBindings().getTypeParameters().stream()
                    .map(t -> t.getRawClass().getSimpleName())
                    .collect(Collectors.joining(", "));
            fieldInfo.setGenericType(genericTypes);
        }

        // 注解处理
        AnnotatedMember member = prop.getAccessor() != null ? prop.getAccessor() : prop.getField();
        if (member != null) {
            Annotation annotation = member.getAnnotation(annotationClass);
            if (annotation != null) {
                AnnotationProcessor processor = PROCESSORS.get(annotationClass);
                if (processor != null) {
                    processor.process(annotation, fieldInfo);
                } else {
                    fieldInfo.getExtensions().put("annotation", annotationClass.getSimpleName());
                }
            }
        }
    }

    private static boolean shouldSkipClass(Class<?> clazz, Set<String> processed) {
        return clazz == null ||
                clazz == Object.class ||
                processed.contains(clazz.getName()) ||
                clazz.isPrimitive() ||
                clazz.isArray() || // 数组本身不递归
                clazz.isInterface() ||
                clazz.getPackage() == null || // 修复 null package
                clazz.getPackage().getName().startsWith("java.");
    }

    private static boolean isBasicType(JavaType type) {
        Class<?> raw = type.getRawClass();
        if (raw.isArray()) return true;
        if (raw.isPrimitive()) return true;
        return raw == String.class ||
                Number.class.isAssignableFrom(raw) ||
                Boolean.class == raw ||
                Date.class.isAssignableFrom(raw) ||
                raw == java.sql.Date.class ||
                Temporal.class.isAssignableFrom(raw) ||
                raw == UUID.class ||
                raw.isEnum();
    }


    public static class FieldInfo {
        private String name;
        private String type;
        private String genericType;
        private String description;
        private Object example;
        private Map<String, Object> extensions = new LinkedHashMap<>();

        // --- getters and setters ---

        public String getName() { return name; }
        public void setName(String name) { this.name = name; }

        public String getType() { return type; }
        public void setType(String type) { this.type = type; }

        public String getGenericType() { return genericType; }
        public void setGenericType(String genericType) { this.genericType = genericType; }

        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }

        public Object getExample() { return example; }
        public void setExample(Object example) { this.example = example; }

        public Map<String, Object> getExtensions() { return extensions; }
        public void setExtensions(Map<String, Object> extensions) { this.extensions = extensions; }

        @Override
        public String toString() {
            return "FieldInfo{" +
                    "name='" + name + '\'' +
                    ", type='" + type + '\'' +
                    ", genericType='" + genericType + '\'' +
                    ", description='" + description + '\'' +
                    ", example=" + example +
                    ", extensions=" + extensions +
                    '}';
        }
    }

    // ===================== 注解处理器 SPI =====================

    @FunctionalInterface
    public interface AnnotationProcessor {
        void process(Annotation annotation, FieldInfo fieldInfo);
    }

    private static class ApiModelPropertyProcessor implements AnnotationProcessor {
        @Override
        public void process(Annotation annotation, FieldInfo fieldInfo) {
            Schema amp = (Schema) annotation;
            fieldInfo.setDescription(amp.description().isEmpty() ? null : amp.description());
            if (!amp.example().isEmpty()) {
                fieldInfo.setExample(amp.example()); // 可进一步解析类型
            }
        }
    }

    // ===================== 缓存 Key =====================

    private static class ClassCacheKey {
        final String className;
        final String annotationClassName;
        public Class<? extends Annotation> annotationClass;

        ClassCacheKey(String className, String annotationClassName) {
            this.className = className;
            this.annotationClassName = annotationClassName;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ClassCacheKey that = (ClassCacheKey) o;
            return Objects.equals(className, that.className) &&
                    Objects.equals(annotationClassName, that.annotationClassName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(className, annotationClassName);
        }

        Class<?> toClass() {
            try {
                return Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("Class not found: " + className, e);
            }
        }
    }
}