package com.kym.common.utils;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;


/**
 * @author yaopeng
 * @date 2018/8/26 21:48
 */
public class BeanUtil {
    private static Logger logger = LoggerFactory.getLogger(BeanUtil.class);

    private BeanUtil() {
    }

    public static <T> List<T> copyList(Class<T> clz, List<?> dataList) {
        List<T> result = new ArrayList<>();
        for (Object o : dataList) {
            result.add(copy(clz, o));
        }
        return result;
    }

    /**
     * 从原实体中拷贝指定字段目标实体中
     *
     * @param src
     * @param dest
     * @param includes
     * @param excludes
     */
    public static void copyProperties(Object src, Object dest, Set<String> includes, Set<String> excludes) {
        Set<String> includeList = CommUtil.null2Set(includes, true);
        Set<String> excludeList = CommUtil.null2Set(excludes, true);
        Field[] fields = getFields(src.getClass());
        for (Field field : fields) {
            if (isClassParameter(field)) {
                continue;
            }
            String name = field.getName();
            if (excludeList.contains(name)) {
                continue;
            }
            if (!includeList.isEmpty() && includeList.contains(name)) {
                ReflectUtil.setFieldValue(dest, name, ReflectUtil.getFieldValue(src, name));
                continue;
            }

            ReflectUtil.setFieldValue(dest, name, ReflectUtil.getFieldValue(src, name));
        }
    }

    /**
     * 从map集合中复制属性到实体类中
     *
     * @param clz
     * @param map
     * @param <T>
     * @return
     */
    public static <T> T copy(Class<T> clz, Map<String, Object> map) {
        try {
            T to = clz.getDeclaredConstructor().newInstance();
            Field[] ff = getFields(clz);
            for (Field f : ff) {
                if (isClassParameter(f)) {
                    continue;
                }
                try {
                    f.setAccessible(true);
                    f.set(to, map.get(f.getName()));
                } catch (Exception e) {
                    logger.warn("BeanUtil.copy ignore,msg:{}", e.getMessage());
                }
            }
            return to;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 对象转Map
     *
     * @param object
     * @return
     */
    public static Map<String, Object> beanToMap(Object object) {
        Map<String, Object> map = new HashMap<>(32);
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                map.put(field.getName(), field.get(object));
            } catch (IllegalAccessException e) {
                logger.warn("beanToMap error", e);
            }
        }
        return map;
    }

    /**
     * map转对象
     *
     * @param map
     * @param beanClass
     * @param <T>
     * @return
     */
    public static <T> T mapToBean(Map map, Class<T> beanClass) {
        try {
            T object = beanClass.getDeclaredConstructor().newInstance();
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                int mod = field.getModifiers();
                if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
                    continue;
                }
                field.setAccessible(true);
                if (map.containsKey(field.getName())) {
                    field.set(object, map.get(field.getName()));
                }
            }
            return object;
        } catch (Exception e) {
            logger.error("mapToBean error", e);
        }
        throw new RuntimeException();
    }

    /**
     * 是否是类变量
     *
     * @param field
     * @return
     */
    public static boolean isClassParameter(Field field) {
        int modifier = field.getModifiers();
        return Modifier.isFinal(modifier) || Modifier.isStatic(modifier) || Modifier.isNative(modifier);
    }

    public static <T> T copy(Class<T> clz, Object data) {
        try {
            T to = clz.getDeclaredConstructor().newInstance();
            Field[] ff = getFields(data.getClass());
            for (Field f : ff) {
                if (isClassParameter(f)) {
                    continue;
                }
                try {
                    Field toField = ReflectUtil.getField(clz, f.getName());
                    f.setAccessible(true);
                    Object fv = f.get(data);
                    toField.setAccessible(true);
                    toField.set(to, fv);
                    toField.setAccessible(false);
                } catch (Exception e) {
                    logger.debug("BeanUtil.copy ignore ,msg:{}", e.getMessage());
                }
            }
            return to;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static boolean compare(Object source, Object target, String... fieldNames) {
        boolean eq = true;
        if (null == source || null == target) {
            return false;
        }
        if (null == fieldNames || fieldNames.length == 0) {
            throw new IllegalArgumentException("cannot get compared fields ERR");
        }
        for (String fieldName : fieldNames) {
            Object v0 = getFieldValue(fieldName, source);
            Object v1 = getFieldValue(fieldName, target);
            if (null != v0 || null != v1) {
                boolean isDate = v0 instanceof Date || v1 instanceof Date;
                if (isDate) {
                    eq = Objects.equals(DateUtil.parse(v0.toString()), DateUtil.parse(v1.toString()));
                } else {
                    eq = Objects.equals(v0, v1);
                }
            }
        }
        return eq;
    }

    private static Object getFieldValue(String fieldName, Object obj) {
        try {
            Field f = ReflectUtil.getField(obj.getClass(), fieldName);
            f.setAccessible(true);
            return f.get(obj);
        } catch (Exception e) {
            logger.warn("BeanUtil.getFieldValue ignore,msg:{}", e.getMessage());
        }
        return null;
    }

    private static Field[] getFields(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] fields = clazz.getDeclaredFields();
            fieldList.addAll(Arrays.asList(fields));
        }
        return fieldList.toArray(new Field[0]);
    }


    public static boolean isFieldExists(Class<?> clz, String fieldName) {
        try {
            Field field = clz.getField(fieldName);
            return !isClassParameter(field);
        } catch (Exception e) {
            logger.warn("isFieldExists WARN:{}", e.getMessage());
        }
        return false;
    }
}
