/**
 * All rights Reserved, Designed By http://www.pete-cat.com/
 *
 * @Title: DataCopy.java
 * @Package com.petecat.formdesigner.util
 * @Description:复制数据工具类
 * @author: 成都皮特猫科技
 * @date:2017年11月27日 下午4:41:51
 * @version V1.0
 * @Copyright: 2017 www.pete-cat.com Inc. All rights reserved.
 * 注意：本内容仅限于成都皮特猫信息技术有限公司内部传阅，禁止外泄以及用于其他的商业目
 */
package com.face.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.cglib.beans.BeanCopier;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
public class DataCopy {
    private static final LoadingCache<CopyKey, BeanCopier> BEAN_COPIER = CacheBuilder.newBuilder()
            // 设置缓存个数
            .maximumSize(500)
            .build(new CacheLoader<>() {
                @Override
                public BeanCopier load(CopyKey copyKey) {
                    return BeanCopier.create(copyKey.getSourseClass(), copyKey.getDestClass(), false);
                }
            });

    public static <T> T shallowCopyData(Object source, Class<T> cls) {
        try {
            T obj = cls.newInstance();
            copyDataInner(source, obj);
            return obj;
        } catch (Exception e) {
            log.error("copyDataFail", e);
            throw SystemExceptionUtils.buildBusinessException("copyFail");
        }
    }

    private static void copyDataInner(Object source, Object obj) {
        try {
            String key = source.getClass().getName().concat("_").concat(obj.getClass().getName());
            BeanCopier beanCopier = BEAN_COPIER.get(CopyKey.builder().key(key)
                    .sourseClass(source.getClass()).destClass(obj.getClass()).build());
            beanCopier.copy(source, obj, null);
        } catch (Exception e) {
            log.error("copyDataFail", e);
            throw SystemExceptionUtils.buildBusinessException("copyFail");
        }
    }

    public static <T> List<T> shallowCopyList(List<?> datas, Class<T> cls) {
        if (datas != null) {
            return datas.stream().map((t) -> {
                T obj = shallowCopyData(t, cls);
                return obj;
            }).collect(Collectors.toList());
        }
        return null;
    }

    public static <T> List<T> deepCopyList(List<?> datas, TypeReference<List<T>> typeReference) {
        try {
            return JsonUtils.parseObject(JsonUtils.toJsonString(datas), typeReference);
        } catch (Exception e) {
            log.error("copyDataFail", e);
            throw SystemExceptionUtils.buildBusinessException("copyFail");
        }
    }

    public static <T> T deepCopyData(Object source, Class<T> cls) {
        if (source == null) {
            return null;
        }
        try {
            return JsonUtils.parseObject(JsonUtils.toJsonString(source), cls);
        } catch (Exception e) {
            log.error("copyDataFail", e);
            throw SystemExceptionUtils.buildBusinessException("copyFail");
        }
    }

    public static <T> List<T> deepCopyList(List<?> datas, Class<T> cls) {
        try {
            return datas.stream().map(data -> deepCopyData(data, cls)).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("copyDataFail", e);
            throw SystemExceptionUtils.buildBusinessException("copyFail");
        }
    }


    @Builder
    @Getter
    static class CopyKey {

        private String key;

        private Class destClass;

        private Class sourseClass;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            CopyKey copyKey = (CopyKey) o;
            return Objects.equals(key, copyKey.key);
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(key);
        }
    }

    public static void copyExistsField(Object source, Object target) {
        try {
            List<Field> sourceFields = FieldUtils.getAllFieldsList(source.getClass());
            List<Field> targetFields = FieldUtils.getAllFieldsList(target.getClass());
            Object value = null;
            for (Field targetField : targetFields) {
                List<Field> fields =
                        sourceFields.stream().filter(field -> StringUtils.equals(field.getName(),
                                targetField.getName())).toList();
                for (Field field : fields) {
                    if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                        continue;
                    }
                    try {
                        value = FieldUtils.readField(field, source, true);
                        FieldUtils.writeField(targetField, target, value, true);
                    } catch (Exception e) {
                        log.warn("copyExistsFieldFail {} {}", field, value, e);
                    }
                }
            }
        } catch (Exception e) {
            log.error("copyExistsField", e);
            throw SystemExceptionUtils.buildBusinessException("copyFail");
        }
    }

}
