package com.seer.teach.common.utils;

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 java.lang.reflect.Array;
import java.time.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 将 List 属性值拼接为逗号字符串的扁平化 Map
 */
public class BeanToMapConverter {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final Set<String> BASIC_TYPES = Stream.of(
                    String.class,
                    Boolean.class, Boolean.TYPE,
                    Integer.class, Integer.TYPE,
                    Long.class, Long.TYPE,
                    Double.class, Double.TYPE,
                    Float.class, Float.TYPE,
                    Short.class, Short.TYPE,
                    Byte.class, Byte.TYPE,
                    Character.class, Character.TYPE,
                    Date.class,
                    java.sql.Date.class,
                    LocalDate.class,
                    LocalDateTime.class,
                    LocalTime.class,
                    Instant.class,
                    Duration.class,
                    Period.class,
                    UUID.class
            ).map(Class::getName)
            .collect(Collectors.toSet());
    private static final Map<Class<?>, List<BeanPropertyDefinition>> PROPERTY_CACHE = new ConcurrentHashMap<>();

    // 列表展开模式
    public enum ListFlattenMode {
        // 使用 [0], [1] 索引
        INDEXED,
        // 拼接为字符串
        CONCAT_FIELDS
    }

    public static Map<String, Object> toFlatMap(Object bean) {
        return toFlatMap(bean, ListFlattenMode.CONCAT_FIELDS);
    }

    public static Map<String, Object> toFlatMap(Object bean, ListFlattenMode mode) {
        Map<String, Object> result = new LinkedHashMap<>();
        if (bean != null) {
            flatten("", bean, result, mode);
        }
        return result;
    }


    private static void flatten(String prefix, Object value, Map<String, Object> result, ListFlattenMode mode) {
        if (value == null) {
            result.put(prefix, null);
            return;
        }

        Class<?> type = value.getClass();

        if (isBasicType(type)) {
            result.put(prefix, value);
            return;
        }

        // 数组
        if (type.isArray()) {
            int length = Array.getLength(value);
            if (length == 0) {
                handleEmptyCollection(prefix, result);
            } else {
                Object first = Array.get(value, 0);
                if (isBasicOrWrapper(first)) {
                    List<String> values = IntStream.range(0, length)
                            .mapToObj(i -> String.valueOf(Array.get(value, i)))
                            .collect(Collectors.toList());
                    result.put(prefix, String.join(", ", values));
                } else {
                    // 对象数组：提取字段并拼接
                    concatObjectCollection(prefix, toListFromArray(value), result);
                }
            }
            return;
        }

        // 集合（List/Set）
        if (value instanceof Collection) {
            Collection<?> collection = (Collection<?>) value;
            if (collection.isEmpty()) {
                handleEmptyCollection(prefix, result);
            } else {
                Object first = collection.iterator().next();
                if (isBasicOrWrapper(first)) {
                    String joined = collection.stream()
                            .map(Objects::toString)
                            .collect(Collectors.joining(", "));
                    result.put(prefix, joined);
                } else {
                    concatObjectCollection(prefix, new ArrayList<>(collection), result);
                }
            }
            return;
        }

        // Map
        if (value instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) value;
            for (Map.Entry<?, ?> entry : map.entrySet()) {
                String key = prefix.isEmpty() ? entry.getKey().toString() : prefix + "." + entry.getKey();
                flatten(key, entry.getValue(), result, mode);
            }
            return;
        }

        // JavaBean
        if (isJavaBean(type)) {
            for (BeanPropertyDefinition prop : getBeanProperties(type)) {
                AnnotatedMember accessor = prop.getAccessor();
                if (accessor == null) continue;
                try {
                    Object fieldValue = accessor.getValue(value);
                    String fieldName = prop.getName();
                    String key = prefix.isEmpty() ? fieldName : prefix + "." + fieldName;
                    flatten(key, fieldValue, result, mode);
                } catch (Exception e) {
                    result.put(prefix.isEmpty() ? prop.getName() : prefix + "." + prop.getName(), null);
                }
            }
            return;
        }

        result.put(prefix, value.toString());
    }


    private static void concatObjectCollection(String prefix, List<?> list, Map<String, Object> result) {
        if (list.isEmpty()) {
            return;
        }

        Class<?> elementType = list.get(0).getClass();

        // 1. 处理 Number 类型（如 Integer、Double 等）
        if (Number.class.isAssignableFrom(elementType)) {
            double sum = list.stream()
                    .filter(Objects::nonNull)  // 过滤 null 值
                    .mapToDouble(n -> ((Number) n).doubleValue())  // 转换为 double 计算
                    .sum();
            result.put(prefix, sum);
            return;
        }

        // 2. 处理非 JavaBean 类型（如 String、基本类型等）
        if (!isJavaBean(elementType)) {
            String joined = list.stream()
                    .filter(Objects::nonNull)  // 过滤 null 值
                    .map(Objects::toString)    // 转为字符串
                    .collect(Collectors.joining(", "));  // 逗号拼接
            result.put(prefix, joined);
            return;
        }

        // 3. 处理 JavaBean 类型（递归处理属性）
        for (BeanPropertyDefinition prop : getBeanProperties(elementType)) {
            AnnotatedMember accessor = prop.getAccessor();
            if (accessor == null) continue;

            try {
                List<Object> values = new ArrayList<>();
                for (Object item : list) {
                    if (item == null) {
                        continue;
                    }
                    Object val = accessor.getValue(item);
                    if (val == null){
                        continue;
                    }
                    values.add(val);
                }

                String key = prefix + "." + prop.getName();

                // 如果属性值是 Number 类型，则计算总和
                if (!values.isEmpty() && values.get(0) instanceof Number) {
                    double sum = values.stream()
                            .mapToDouble(n -> ((Number) n).doubleValue())
                            .sum();
                    result.put(key, sum);
                }
                // 否则拼接成字符串
                else {
                    result.put(key, values.stream()
                            .map(Objects::toString)
                            .collect(Collectors.joining(", ")));
                }
            } catch (Exception e) {
                result.put(prefix + "." + prop.getName(), "error");
            }
        }
    }

    private static void handleEmptyCollection(String prefix, Map<String, Object> result) {
        result.put(prefix, "");
    }


    private static boolean isBasicType(Class<?> type) {
        return BASIC_TYPES.contains(type.getName()) ||
                type.isPrimitive() ||
                type == void.class || type == Void.class;
    }

    private static boolean isBasicOrWrapper(Object obj) {
        if (obj == null) return true;
        Class<?> type = obj.getClass();
        return type.isPrimitive() ||
                obj instanceof String ||
                obj instanceof Number ||
                obj instanceof Boolean ||
                obj instanceof Character ||
                obj instanceof Date;
    }

    private static boolean isJavaBean(Class<?> type) {
        return !type.isInterface() &&
                !type.isArray() &&
                !type.isEnum() &&
                !type.getName().startsWith("java.") &&
                !type.getName().startsWith("javax.") &&
                !Collection.class.isAssignableFrom(type) &&
                !Map.class.isAssignableFrom(type);
    }

    private static List<BeanPropertyDefinition> getBeanProperties(Class<?> clazz) {
        return PROPERTY_CACHE.computeIfAbsent(clazz, c -> {
            JavaType javaType = MAPPER.constructType(c);
            BeanDescription desc = MAPPER.getSerializationConfig().introspect(javaType);
            return new ArrayList<>(desc.findProperties());
        });
    }

    private static List<Object> toListFromArray(Object array) {
        int length = Array.getLength(array);
        List<Object> list = new ArrayList<>(length);
        for (int i = 0; i < length; i++) {
            list.add(Array.get(array, i));
        }
        return list;
    }
}
