package com.rrtx.services.common.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.DynaBean;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.reflect.TypeToken;

import java.util.*;
import java.util.stream.Collectors;

/**
 *
 *
 * ClassName: <br>
 * Description:  <br>
 * Company: rrtx
 * @author Jie Zhang
 * @version  v1.0.0    2019/12/27 下午2:38  Jie Zhang     文件初始创建
 *
 */
public class MergeUtil{

    /**
     *
     * @param majorList 返回结果条数与majorList一致
     * @param mergedList 根据joinFieldMap字段与majorList关联
     * @param joinFieldMap 关联字段 key为majorList字段 value为mergedList
     * @param majorMapping majorList与合并后list字段的映射 <k,v> k为majorList中源字段， v为目标字段
     * @param joinMapping  mergedList中被合并的字段 <k,v> k为mergedList中源字段， v为目标字段
     * @return
     */
    public static <T,M,N> List<T> merge(List<M> majorList, List<N> mergedList,
                                        TreeMap<String, String> joinFieldMap, Map<String, String> majorMapping,
                                        Map<String, String> joinMapping) {

        int resultLength = majorList.size();
        Map<String,List<N> > groupMap = new HashMap<>(1);
        groupMap.put("mergedList" , mergedList);
        Iterator<Map.Entry<String, String>> iterator =  joinFieldMap.entrySet().iterator();
        Map<String, Object> mergedGroup = group(mergedList, iterator, iterator.next());


        List<T> resultList = new ArrayList<>(resultLength);
        for (M m : majorList){

            TypeToken<T> resultType = new TypeToken<T>(MergeUtil.class) {
            };
            Class<T> rawType = (Class<T>) resultType.getRawType();


            T t = JSONObject.parseObject("{}", rawType);
            N n = mappedN(m,joinFieldMap, mergedGroup);
            DynaBean dynaBean4T = BeanUtil.createDynaBean(t);
            DynaBean dynaBean4M = BeanUtil.createDynaBean(m);
            majorMapping.forEach((k,v)->dynaBean4T.set(v,dynaBean4M.get(k)));
            DynaBean dynaBean4N = BeanUtil.createDynaBean(n);
            joinMapping.forEach((k,v)-> dynaBean4T.set(v,dynaBean4N.get(k)));
            resultList.add(t);
        }
        return resultList;

    }


    /**
     *
     * @param majorList 返回结果条数与majorList一致
     * @param mergedList 根据joinFieldMap字段与majorList关联
     * @param joinFieldMap 关联字段 key为majorList字段 value为mergedList
     * @param joinMapping  mergedList中被合并的字段 <k,v> k为mergedList中源字段， v为目标字段
     * @return
     */
    public static <T,M,N> List<T> merge(List<M> majorList, List<N> mergedList,
                                        TreeMap<String, String> joinFieldMap,
                                        Map<String, String> joinMapping) {

        int resultLength = majorList.size();
        Map<String,List<N> > groupMap = new HashMap<>(1);
        groupMap.put("mergedList" , mergedList);
        Iterator<Map.Entry<String, String>> iterator =  joinFieldMap.entrySet().iterator();
        Map<String, Object> mergedGroup = group(mergedList, iterator, iterator.next());


        List<T> resultList = new ArrayList<>(resultLength);
        for (M m : majorList){

            TypeToken<T> resultType = new TypeToken<T>(MergeUtil.class) {};
            Class<T> rawType = (Class<T>) resultType.getRawType();

            String s = JSONObject.toJSONString(m, SerializerFeature.WriteMapNullValue);
            T t = JSONObject.parseObject(s, rawType);
            N n = mappedN(m,joinFieldMap, mergedGroup);
            DynaBean dynaBean4T = BeanUtil.createDynaBean(t);
            DynaBean dynaBean4N = BeanUtil.createDynaBean(n);
            joinMapping.forEach((k,v)-> dynaBean4T.set(v,dynaBean4N.get(k)));
            resultList.add(t);
        }
        return resultList;

    }


    /**
     *
     * @param majorList 返回结果条数与majorList一致
     * @param mergedList 根据joinFieldMap字段与majorList关联
     * @param joinFieldMap 关联字段 key为majorList字段 value为mergedList
     * @return
     */
    public static <T,M,N> List<T> merge(List<M> majorList, List<N> mergedList,
                                        TreeMap<String, String> joinFieldMap) {

        int resultLength = majorList.size();
        Map<String,List<N> > groupMap = new HashMap<>(1);
        groupMap.put("mergedList" , mergedList);
        Iterator<Map.Entry<String, String>> iterator =  joinFieldMap.entrySet().iterator();
        Map<String, Object> mergedGroup = group(mergedList, iterator, iterator.next());


        List<T> resultList = new ArrayList<>(resultLength);
        for (M m : majorList){
            TypeToken<T> resultType = new TypeToken<T>(MergeUtil.class) {
            };
            Class<T> rawType = (Class<T>) resultType.getRawType();

            String s = JSONObject.toJSONString(m, SerializerFeature.WriteMapNullValue);
            T t = JSONObject.parseObject(s, rawType);
            N n = mappedN(m,joinFieldMap, mergedGroup);
            BeanUtil.copyProperties(n, t);

            resultList.add(t);
        }
        return resultList;

    }

    private static <N, M> N mappedN(M m, TreeMap<String, String> joinFieldMap, Map<String, Object> mergedGroup) {
        Iterator<Map.Entry<String, String>> iterator =  joinFieldMap.entrySet().iterator();
        DynaBean dynaBean = BeanUtil.createDynaBean(m);
        return mappedN(dynaBean, mergedGroup, iterator);
    }

    private static <N> N mappedN(DynaBean dynaBean, Map<String, Object> mergedGroup, Iterator<Map.Entry<String, String>> iterator) {

        Map.Entry<String,String> entry = iterator.next();
        while (iterator.hasNext()){
            mergedGroup = (Map<String, Object>) mergedGroup.get(dynaBean.get(entry.getKey()));
            if (mergedGroup == null){
                return null;
            }
            entry = iterator.next();
        }
        if (null == mergedGroup.get(dynaBean.get(entry.getKey()))){
            return null;
        }
        return ((List<N>)mergedGroup.get(dynaBean.get(entry.getKey()))).get(0);
    }

    private static <N> Map<String, Object> group(List<N> nList, Iterator<Map.Entry<String, String>> iterator,Map.Entry<String,String> entry) {
        Map<String, Object> resultMap = new HashMap<>(nList.size());

        Map<String,List<N>> group = nList.stream().collect(Collectors.groupingBy(n -> {
            DynaBean dynaBean = BeanUtil.createDynaBean(n);
            return dynaBean.get(entry.getValue());
        }));

        if (iterator.hasNext()){
            Map.Entry<String,String> entry1 = iterator.next();
            group.forEach((k,v) ->{
                resultMap.put(k, group(v, iterator, entry1));
            });
        }else {
            group.forEach((k,v)->resultMap.put(k,v));
        }

        return resultMap;
    }

}
