package com.dsk.search.util;

import com.alibaba.fastjson2.JSON;
import com.dsk.common.utils.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.CollectionUtils;

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

/**
 * @Author liujie
 * @ClassName ObjectUtils
 * @Date 2021年09月29日22:34
 **/

public class ObjectUtils {

    public static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    public static void copyPropertiesIgnoreNull(Object src, Object target) {
        BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
    }




    /**
     * 方法一：利用递归遍历
     * 用途：从复杂对象中递归遍历，判断对象是否有值
     * @param object 复杂对象  不能是数组
     * @param excludeProNames  排除的属性名
     * @param excludeValues  排除的属性值
     * @return
     */
    public static  Boolean hasValueThroughObj(Object object,List<String> excludeProNames,List<Object> excludeValues){
        Boolean hasValue = false;
        Class clazz = null;
        try {
            if (object==null ){
                return hasValue;
            }
            clazz = object.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                String proType = field.getGenericType().toString();
                String proName = field.getName();
//                System.err.println("proName:" + proName + "----------proType:" + proType );
//                System.out.println(excludeProNames+"---contains---:"+proName+"--"+excludeProNames.contains(proName));
                if (!CollectionUtils.isEmpty(excludeProNames)&&excludeProNames.contains(proName)){
                    continue;
                }
                if ("class java.lang.String".equals(proType) ||"byte".equals(proType)||"class java.util.Date".equals(proType) || "short".equals(proType) || "int".equals(proType)
                        ||"class java.lang.Byte".equals(proType) || "class java.lang.Short".equals(proType) || "class java.lang.Integer".equals(proType) || "class java.lang.Long".equals(proType) || "class java.lang.Double".equals(proType) || "class java.lang.Float".equals(proType) || "class java.lang.Boolean".equals(proType)
                        ||"long".equals(proType)|| "double".equals(proType) || "float".equals(proType) || "boolean".equals(proType)){
                    field.setAccessible(true);
                    Object obj = field.get(object);
                    if (obj==null ){
                        continue;
                    }
                    String proValue = String.valueOf(obj);
                    if (StringUtils.isEmpty(proValue)){
                        continue;
                    }
                    if (!excludeValues.contains(proValue)){
                        hasValue = true;
                        return hasValue;
                    }
                    continue;
                }else if (proType.startsWith("java.util")){
                    //属性是集合类型则遍历
                    if (proType.startsWith("java.util.List")){
                        //对List类型的属性遍历
                        PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                        Method method = descriptor.getReadMethod();
                        List list = (List)method.invoke(object);
                        if (list!=null && list.size()>0){
                            int len = list.size();
                            for (int i= 0;i<len;i++){
                                hasValue = hasValueThroughObj(list.get(i),excludeProNames,excludeValues);
                                if (hasValue) return hasValue;
                            }
                            continue;
                        }
                    }else if (proType.startsWith("java.util.Map")){
                        //对Map类型的属性遍历
                        PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                        Method method = descriptor.getReadMethod();
                        Map map = (Map)method.invoke(object);
                        if (map!=null && map.size()>0){
                            for (Object obj : map.values()){
                                if (hasValue) return hasValue;
                            }
                            continue;
                        }
                    }
                }else if(field.getType().isArray()){
                    //属性是数组类型则遍历
                    field.setAccessible(true);
                    Object[] objArr = (Object[]) field.get(object);
                    Map<String,Object> objMap = new HashMap<>();
                    if (objArr!=null && objArr.length>0){
                        for (Object arr : objArr){
                            hasValue = hasValueThroughObj(arr,excludeProNames,excludeValues);
                            if (hasValue) return hasValue;
                        }
                        continue;
                    }
                }else  {
                    //class类型的遍历
                    PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                    Method method = descriptor.getReadMethod();
                    Object obj = method.invoke(object);
                    if (obj!= null){
                        hasValue = hasValueThroughObj(obj,excludeProNames,excludeValues);
                        if (hasValue) return hasValue;
                        continue;
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("err:" + e);
        }
        return hasValue;
    }




    /**
     * 用途：从复杂对象中递归遍历，判断对象中某些字段是否有值
     * @param object 复杂对象  不能是数组
     * @param excludeProNames  排除的属性名
     * @return
     */
    public static  Boolean hasFieldValueThroughObj(Object object,List<String> excludeProNames){
        Boolean hasValue = false;
        Class clazz = null;
        try {
            if (object==null ){
                return hasValue;
            }
            clazz = object.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                String proType = field.getGenericType().toString();
                if ("class java.lang.String".equals(proType) ||"byte".equals(proType)||"class java.util.Date".equals(proType) || "short".equals(proType) || "int".equals(proType)
                        ||"class java.lang.Byte".equals(proType) || "class java.lang.Short".equals(proType) || "class java.lang.Integer".equals(proType) || "class java.lang.Long".equals(proType) || "class java.lang.Double".equals(proType) || "class java.lang.Float".equals(proType) || "class java.lang.Boolean".equals(proType)
                        ||"long".equals(proType)|| "double".equals(proType) || "float".equals(proType) || "boolean".equals(proType)){
                    field.setAccessible(true);
                    Object obj = field.get(object);
                    if (obj==null ){
                        continue;
                    }
                    String proName = field.getName();
                    if (!CollectionUtils.isEmpty(excludeProNames)&&excludeProNames.contains(proName)){
                        String proValue = String.valueOf(obj);
                        if (StringUtils.isEmpty(proValue)){
                            continue;
                        }
                        hasValue = true;
                        return hasValue;
                    }
                    continue;
                }else if (proType.startsWith("java.util")){
                    //属性是集合类型则遍历
                    if (proType.startsWith("java.util.List")){
                        //对List类型的属性遍历
                        PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                        Method method = descriptor.getReadMethod();
                        List list = (List)method.invoke(object);
                        if (list!=null && list.size()>0){
                            int len = list.size();
                            for (int i= 0;i<len;i++){
                                hasValue = hasFieldValueThroughObj(list.get(i),excludeProNames);
                                if (hasValue) return hasValue;
                            }
                            continue;
                        }
                    }else if (proType.startsWith("java.util.Map")){
                        //对Map类型的属性遍历
                        PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                        Method method = descriptor.getReadMethod();
                        Map map = (Map)method.invoke(object);
                        if (map!=null && map.size()>0){
                            for (Object obj : map.values()){
                                if (hasValue) return hasValue;
                            }
                            continue;
                        }
                    }
                }else if(field.getType().isArray()){
                    //属性是数组类型则遍历
                    field.setAccessible(true);
                    Object[] objArr = (Object[]) field.get(object);
                    Map<String,Object> objMap = new HashMap<>();
                    if (objArr!=null && objArr.length>0){
                        for (Object arr : objArr){
                            hasValue = hasFieldValueThroughObj(arr,excludeProNames);
                            if (hasValue) return hasValue;
                        }
                        continue;
                    }
                }else  {
                    //class类型的遍历
                    PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                    Method method = descriptor.getReadMethod();
                    Object obj = method.invoke(object);
                    if (obj!= null){
                        hasValue = hasFieldValueThroughObj(obj,excludeProNames);
                        if (hasValue) return hasValue;
                        continue;
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("err:" + e);
        }
        return hasValue;
    }

    /**
     * 方法一：利用递归遍历
     * 用途：从复杂对象中递归遍历，获取string类型的目标属性名的值
     * 适用条件：该复杂对象中如果存在多个目标属性targetProName，遍历到第一个atargetProName则退出遍历
     *           targetProName属性必须是string
     *           targetProName可以存在自定义对象中、list、map、数组中
     *           如果复杂对象不包含目标属性则返回空字符串
     *           复杂对象可以是复杂嵌套的BO/List<BO>/Map<Object,BO>,目标属性存在于BO中
     *           对于复杂对象是list或map嵌套的不做支持。比如List<List<BO></BO>> /List<Map<object,BO>> / Map<object,List<BO>>
     * @param object 复杂对象
     * @param targetProName  目标属性名
     * @return
     */
    public static  Map<String,Object> recursionLoopThroughObj(Object object,String targetProName){
        Map<String,Object> resultMap = new HashMap<>();
        Class clazz = null;
        String proValue = "";
        boolean loopFlag = true;
        resultMap.put("loopFlag",loopFlag);
        resultMap.put("proValue",proValue);
        try {
            if (object==null || checkObjectIsSysType(object)){
                //如果object是null/基本数据类型/包装类/日期类型，则不需要在递归调用
                resultMap.put("loopFlag",false);
                resultMap.put("proValue","");
                return resultMap;
            }
            if (object instanceof Map){
                Map map = (Map)object;
                Map<String,Object> objMap = new HashMap<>();
                if (map!=null && map.size()>0){
                    Iterator iterator = map.values().iterator();
                    while (iterator.hasNext()){
                        objMap = recursionLoopThroughObj(iterator.next(),targetProName);
                        if (!(boolean)objMap.get("loopFlag")){
                            return objMap;
                        }
                    }
                }
            }
            clazz = object.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                String proType = field.getGenericType().toString();
                String proName = field.getName();
                System.err.println("proName:" + proName + ",proType:" + proType );
                if ("class java.lang.String".equals(proType) && targetProName.equals(proName)){
                    field.setAccessible(true);
                    proValue = (String)field.get(object);
                    resultMap.put("loopFlag",false);
                    resultMap.put("proValue",proValue);
                    return resultMap;
                }else if ("byte".equals(proType) || "short".equals(proType) || "int".equals(proType)|| "long".equals(proType)|| "double".equals(proType) || "float".equals(proType) || "boolean".equals(proType) ){
                    //属性是基本类型跳过
                    continue;
                }else if ("class java.lang.Byte".equals(proType) || "class java.lang.Short".equals(proType) || "class java.lang.Integer".equals(proType) || "class java.lang.Long".equals(proType) || "class java.lang.Double".equals(proType) || "class java.lang.Float".equals(proType) || "class java.lang.Boolean".equals(proType) || ("class java.lang.String".equals(proType) && !targetProName.equals(proName))){
                    //属性是包装类跳过
                    continue;
                }else if (proType.startsWith("java.util")){
                    //属性是集合类型则遍历
                    if (proType.startsWith("java.util.List")){
                        //对List类型的属性遍历
                        PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                        Method method = descriptor.getReadMethod();
                        List list = (List)method.invoke(object);
                        Map<String,Object> objMap = new HashMap<>();
                        if (list!=null && list.size()>0){
                            int len = list.size();
                            for (int i= 0;i<len;i++){
                                objMap = recursionLoopThroughObj(list.get(i),targetProName);
                                if (!(boolean)objMap.get("loopFlag")){
                                    return objMap;
                                }
                            }
                        }
                    }else if (proType.startsWith("java.util.Map")){
                        //对Map类型的属性遍历
                        PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                        Method method = descriptor.getReadMethod();
                        Map map = (Map)method.invoke(object);
                        Map<String,Object> objMap = new HashMap<>();
                        if (map!=null && map.size()>0){
                            for (Object obj : map.values()){
                                objMap = recursionLoopThroughObj(obj,targetProName);
                                if (!(boolean)objMap.get("loopFlag")){
                                    return objMap;
                                }
                            }
                        }
                    }
                }else if(field.getType().isArray()){
                    //属性是数组类型则遍历
                    field.setAccessible(true);
                    Object[] objArr = (Object[]) field.get(object);
                    Map<String,Object> objMap = new HashMap<>();
                    if (objArr!=null && objArr.length>0){
                        for (Object arr : objArr){
                            objMap = recursionLoopThroughObj(arr,targetProName);
                            if (!(boolean)objMap.get("loopFlag")){
                                return objMap;
                            }
                        }
                    }
                }else  {
                    //class类型的遍历
                    PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                    Method method = descriptor.getReadMethod();
                    Object obj = method.invoke(object);
                    Map<String,Object> objMap = new HashMap<>();
                    if (obj!= null){
                        objMap = recursionLoopThroughObj(obj,targetProName);
                        if (!(boolean)objMap.get("loopFlag")){
                            return objMap;
                        }
                    }else {
                        continue;
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("err:" + e);
        }
        return resultMap;
    }


    /**
     * 检查object是否为java的基本数据类型/包装类/java.util.Date/java.sql.Date
     * @param object
     * @return
     */
    public static boolean checkObjectIsSysType(Object object){
        String objType = object.getClass().toString();
        if ("byte".equals(objType) || "short".equals(objType) || "int".equals(objType)|| "long".equals(objType)|| "double".equals(objType) || "float".equals(objType) || "boolean".equals(objType)){
            return true;
        }else if ("class java.lang.Byte".equals(objType) || "class java.lang.Short".equals(objType) || "class java.lang.Integer".equals(objType) || "class java.lang.Long".equals(objType) || "class java.lang.Double".equals(objType) || "class java.lang.Float".equals(objType) || "class java.lang.Boolean".equals(objType) || "class java.lang.String".equals(objType)){
            return true;
        }else {
            return  false;
        }
    }

    /**
     * 方法二：从复杂对象中获取string类型的目标属性targetProName的值
     * 把对象转换成json字符串，然后截取第一次出现的targetProName的值
     * 适用条件：同方法一
     * @param object 复杂对象
     * @param targetProName 目标属性
     * @return
     */
    public static String getBusinessNoFromArg(Object object,String targetProName){
        String jsonString = JSON.toJSONString(object);
        System.err.println("jsonString=" + jsonString);
        jsonString = StringUtils.substringAfter(jsonString,"\""+targetProName + "\":\"");
        jsonString = StringUtils.substringBefore(jsonString,"\"");
        return jsonString;
    }
}
