package com.ruoyi.platform.utils;


import org.apache.commons.beanutils.PropertyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;


/**
 * @Author ifredom
 * @Description 类型转换: Entity - Vo转换
 * @Date 2022/5/10 15:59
 * @Param [params]
 **/
public class ConvertUtil {
    public static final Logger logger = LoggerFactory.getLogger(ConvertUtil.class);


    /**
     * 将实体对象转换为VO对象
     *
     * @param source 源实体对象
     * @param target 目标VO对象的类
     * @return 转换后的VO对象
     */
    public static <T> T entityToVo(Object source, Class<T> target) {
        if (source == null) {
            return null;
        }
        T targetObject = null;
        try {
            targetObject = target.newInstance();
            BeanUtils.copyProperties(source, targetObject);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return targetObject;
    }


    // 将实体对象列表转换为目标类型的列表
    public static <T> List<T> entityToVoList(Collection<?> sourceList, Class<T> target) {
        if (sourceList == null) {
            return null;
        }
        List<T> targetList = new ArrayList<>(sourceList.size());

        try {
            for (Object source : sourceList) {
                T targetObject = target.newInstance();
                BeanUtils.copyProperties(source, targetObject);
                targetList.add(targetObject);
            }
        } catch (Exception e) {
            logger.error("convert error ", e);
        }
        return targetList;
    }

    public static <T> List<T> convertListMapToObjectList(List<Map<String, Object>> listMap, Class<T> targetClass) {
        List<T> resultList = new ArrayList<>();

        if (listMap != null) {
            for (Map<String, Object> map : listMap) {
                T targetObject = convertMapToObject(map, targetClass);
                if (targetObject != null) {
                    resultList.add(targetObject);
                }
            }
        }

        return resultList;
    }

    public static <T> T convertMapToObject(Map<String, Object> map, Class<T> targetClass) {
        try {
            T targetObject = targetClass.newInstance();
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                String camelCaseKey = toCamelCase(key);
                if (hasProperty(targetClass, camelCaseKey)) {
                    if (!(value instanceof LinkedHashMap)) {
                        PropertyUtils.setProperty(targetObject, camelCaseKey, value);
                    }
                }
            }
            return targetObject;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T convertMapToObject2(Map<String, Object> map, Class<T> targetClass) {
        try {
            T targetObject = targetClass.newInstance();
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                String camelCaseKey = toCamelCase(key);

                if (hasProperty(targetClass, camelCaseKey)) {
                    // 获取目标属性的类型
                    Class<?> propertyType = PropertyUtils.getPropertyType(targetObject, camelCaseKey);

                    if (propertyType.isAssignableFrom(LinkedHashMap.class)) {
                        // 如果属性类型是 HashMap，则递归转换
                        Map<String, Object> mapValue = (Map<String, Object>) value;
                        Object convertedValue = convertMapToObject2(mapValue, propertyType);
                        PropertyUtils.setProperty(targetObject, camelCaseKey, convertedValue);
                    } else if (!(value instanceof LinkedHashMap)) {
                        PropertyUtils.setProperty(targetObject, camelCaseKey, value);
                    }
                }
            }
            return targetObject;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }


    private static boolean hasProperty(Class<?> clazz, String propertyName) {
        try {
            Field field = clazz.getDeclaredField(propertyName);
            return field != null;
        } catch (NoSuchFieldException e) {
            Class<?> superClazz = clazz.getSuperclass();
            if (superClazz != Object.class) {
                return hasProperty(superClazz, propertyName);
            }
            return false;
        }
    }

    private static String toCamelCase(String key) {
        StringBuilder sb = new StringBuilder();
        boolean nextUpperCase = false;
        for (char c : key.toCharArray()) {
            if (c == '_') {
                nextUpperCase = true;
            } else {
                if (nextUpperCase) {
                    sb.append(Character.toUpperCase(c));
                    nextUpperCase = false;
                } else {
                    sb.append(c);
                }
            }
        }
        return sb.toString();
    }

    private static String toSnakeCase(String camelCase) {
        return camelCase.replaceAll(
                String.format("%s|%s|%s",
                        "(?<=[a-z])(?=[A-Z])", // 小写字母后接大写字母
                        "(?<=[^A-Z])(?=[A-Z][a-z])", // 非大写字母后接大写字母开头的小写字母
                        "(?<=[A-Z])(?=[A-Z][a-z][0-9])" // 大写字母后接另一个大写字母开头的小写字母和数字（可选，根据需求调整）
                ),
                "_"
        ).toLowerCase();
    }

    public static Map<String, String> compareObjects(Object obj1, Object obj2) throws IllegalAccessException {
        Map<String, String> differences = new HashMap<>();

        // Get the class of the first object
        Class<?> clazz = obj1.getClass();

        // Iterate over all fields of the class
        for (Field field : clazz.getDeclaredFields()) {
            // Set the fields to be accessible
            field.setAccessible(true);

            // Get the field value from both objects
            Object value1 = field.get(obj1);
            Object value2 = field.get(obj2);

            // Compare the field values
            if ((value1 !=null && !value1.equals(value2)) || (value2 !=null && !value2.equals(value1))) {
                differences.put(toSnakeCase(field.getName()), "Field " + field.getName() + " differs: " + value1 + " vs " + value2);
            }
        }

        return differences;
    }
}

