package com.beagle.base.common.util;

import com.beagle.base.common.contant.CodeEnum;
import com.beagle.base.common.exception.BeagleException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.util.CollectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author SmileCircle
 */
public final class BeanUtil extends org.springframework.beans.BeanUtils {
    private BeanUtil() {
    }

    public interface Callback<T, R> {

        /**
         * 回调方法
         *
         * @param source
         * @param targetClass
         * @return
         */
        void doCallback(T source, R targetClass);

    }

    public static <T, R> R convert(T source, Class<R> targetClass, Callback<T, R>
            callback, String... ignoreProperties) {
        if (source == null) {
            return null;
        } else {
            R target = null;

            try {
                target = targetClass.newInstance();
            } catch (IllegalAccessException | InstantiationException var4) {
                throw new BeagleException(CodeEnum.UNKNOWN);
            }

            copyProperties(source, target, ignoreProperties);

            if (callback != null) {
                callback.doCallback(source, target);
            }

            return target;
        }
    }

    public static <T, R> R convert(T source, Class<R> targetClass, Callback<T, R> callback) {
        return convert(source, targetClass, callback, (String[]) null);
    }

    public static <T, R> R convert(T source, Class<R> targetClass, String... ignoreProperties) {
        return convert(source, targetClass, null, ignoreProperties);
    }

    public static <T, R> R convert(T source, Class<R> targetClass) {
        return convert(source, targetClass, null, (String[]) null);
    }

    public static <T, R> List<R> convertList(Collection<T> collection, Class<R> targetType, Callback<T, R> callback, String... ignoreProperties) {
        if (CollectionUtils.isEmpty(collection)) {
            return new ArrayList<>();
        } else {
            List newList = new ArrayList(collection.size());
            collection.forEach(item -> newList.add(convert(item, targetType, callback, ignoreProperties)));
            return newList;
        }
    }

    public static <T, R> List<R> convertList(Collection<T> collection, Class<R> targetType, String... ignoreProperties) {
        return convertList(collection, targetType, null, ignoreProperties);
    }

    public static <T, R> List<R> convertList(Collection<T> collection, Class<R> targetType, Callback<T, R> callback) {
        return convertList(collection, targetType, callback, (String[]) null);
    }

    public static <T, R> List<R> convertList(Collection<T> collection, Class<R> targetType) {
        return convertList(collection, targetType, null, (String[]) null);
    }


    public static <T, R> Set<R> convertSet(Collection<T> collection, Class<R> targetType, Callback<T, R> callback, String... ignoreProperties) {
        if (CollectionUtils.isEmpty(collection)) {
            return new HashSet<>();
        } else {
            Set newSet = new HashSet(collection.size());
            collection.forEach(item -> newSet.add(convert(item, targetType, callback, ignoreProperties)));
            return newSet;
        }
    }

    public static <T, R> Set<R> convertSet(Collection<T> collection, Class<R> targetType, String... ignoreProperties) {
        return convertSet(collection, targetType, null, ignoreProperties);
    }

    public static <T, R> Set<R> convertSet(Collection<T> collection, Class<R> targetType, Callback<T, R> callback) {
        return convertSet(collection, targetType, callback, (String[]) null);
    }

    public static <T, R> Set<R> convertSet(Collection<T> collection, Class<R> targetType) {
        return convertSet(collection, targetType, null, (String[]) null);
    }

    public static void copyPropertiesIgnoreEmpty(Object source, Object target) throws BeansException {
        PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(source.getClass());
        List<String> ignoreProperties = new ArrayList<>();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            Method readMethod = propertyDescriptor.getReadMethod();
            try {
                Object value = readMethod.invoke(source);
                String propertiesName = null;
                if(value instanceof Collection) {
                    if(CollectionUtils.isEmpty((Collection<?>) value)) {
                        propertiesName = readMethod.getName();
                    }
                } else if (value instanceof String) {
                    if (StringUtils.isEmpty((CharSequence) value)) {
                        propertiesName = readMethod.getName();
                    }
                } else {
                    if (Objects.isNull(value)) {
                        propertiesName = readMethod.getName();
                    }
                }
                if(StringUtils.isNotEmpty(propertiesName)) {
                    if(StringUtils.startsWith(propertiesName, "get")) {
                        propertiesName = propertiesName.substring(3);
                    } else if (StringUtils.startsWith(propertiesName, "is")) {
                        propertiesName = propertiesName.substring(2);
                    }
                    String firstName = propertiesName.substring(0, 1);
                    String otherName = propertiesName.substring(1);
                    propertiesName = firstName.toLowerCase(Locale.ROOT) + otherName;
                    ignoreProperties.add(propertiesName);
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new BeagleException(CodeEnum.UNKNOWN);
            }
        }
        copyProperties(source, target, ignoreProperties.toArray(new String[0]));
    }
}

