package cn.iocoder.foodnexus.framework.common.util;

import cn.hutool.core.util.ReflectUtil;
import lombok.extern.slf4j.Slf4j;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * Cache 工具类
 *
 * @author 京黔源码
 */
@Slf4j
public class CommonUtil {

    public static final String EMPTY_STR = "";

    public static final String DATE_TIMESTAMP_DESC_STR = "yyyy-MM-dd HH:mm:ss";

    /**
     * yyyy-MM-dd HH:mm:ss 的 DateTimeFormatter
     */
    public static final DateTimeFormatter DATE_TIME_FORMATTER =
            DateTimeFormatter.ofPattern(DATE_TIMESTAMP_DESC_STR);

    public static String blankStr() {
        return "";
    }

    public static boolean isEmpty(Collection<?> collection) {
        return (collection == null || collection.isEmpty());
    }

    public static boolean isEmpty(Map<?, ?> map) {
        return (map == null || map.isEmpty());
    }

    public static boolean isBlank(String str) {
        return Objects.isNull(str) || Objects.equals(EMPTY_STR, str.trim());
    }

    public static boolean isAnyBlank(String... strs) {
        for (String str : strs) {
            if (Objects.isNull(str) || Objects.equals(EMPTY_STR, str.trim())) {
                return true;
            }
        }
        return false;
    }

    public static boolean isNullOrZero(Long value) {
        return (value == null || Objects.equals(value, 0L));
    }

    public static boolean isNullOrZero(Integer value) {
        return (value == null || Objects.equals(value, 0));
    }


    public static boolean isNotEmpty(Collection<?> collection) {
        return !isEmpty(collection);
    }

    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }

    public static boolean isNotBlank(String str) {
        return Objects.nonNull(str) && !Objects.equals(EMPTY_STR, str.trim());
    }

    /**
     * 数组转map
     *
     * @param list 数据源
     * @param getKey 获取key的方法
     * @param getValue 获取value的方法
     * @param <K> key类型
     * @param <V> value类型
     * @param <R> 数组元素类型
     * @return map集合
     */
    public static <K, V, R> Map<K, V> listConvertMap(Collection<R> list, Function<R, K> getKey,
                                                     Function<R, V> getValue) {
        if (CommonUtil.isEmpty(list)) {
            return new HashMap<>(8);
        }
        Map<K, V> map = new HashMap<>(Math.min(list.size(), 256));
        list.forEach(item -> map.put(getKey.apply(item), getValue.apply(item)));
        return map;
    }

    /**
     * 数组转map
     *
     * @param list 数据源
     * @param getKey 获取key的方法
     * @param getValue 获取value的方法
     * @param <K> key类型
     * @param <V> value类型
     * @param <R> 数组元素类型
     * @return map集合
     */
    public static <K, V, R> Map<K, V> listConvertTreeMap(Collection<R> list, Function<R, K> getKey,
                                                         Function<R, V> getValue) {
        if (CommonUtil.isEmpty(list)) {
            return new TreeMap<>();
        }
        Map<K, V> map = new TreeMap<>();
        list.forEach(item -> map.put(getKey.apply(item), getValue.apply(item)));
        return map;
    }

    public static <K, V> Map<K, V> listConvertMap(Collection<V> list, Function<V, K> getKey) {
        return listConvertMap(list, getKey, Function.identity());
    }

    public static <K, V> Map<K, V> listConvertTreeMap(Collection<V> list, Function<V, K> getKey) {
        return listConvertTreeMap(list, getKey, Function.identity());
    }

    public static <R, E> List<R> listConvert(List<E> sourceList, Function<E, R> getValue) {
        if (isEmpty(sourceList)) {
            return new ArrayList<>();
        }
        List<R> rList = new ArrayList<>(sourceList.size());
        for (E item : sourceList) {
            R r = getValue.apply(item);
            if (Objects.nonNull(r)) {
                rList.add(r);
            }
        }
        return rList;
    }

    public static <E> List<E> listFilter(List<E> sourceList, Predicate<E> filter) {
        if (isEmpty(sourceList)) {
            return new ArrayList<>();
        }
        return sourceList.stream().filter(filter).collect(Collectors.toList());
    }

    public static <E> List<E> listFilterDiff(List<E> sourceList, List<E> targetList) {
        if (isEmpty(sourceList) && isEmpty(targetList)) {
            return new ArrayList<>();
        }
        return sourceList.stream().filter(e-> !targetList.contains(e)).collect(Collectors.toList());
    }

    public static <R, E> List<R> of(List<E> sourceList, Function<E, List<R>> getValue) {
        if (isEmpty(sourceList)) {
            return new ArrayList<>();
        }
        List<R> rList = new ArrayList<>(sourceList.size());
        for (E item : sourceList) {
            List<R> r = getValue.apply(item);
            if (isNotEmpty(r)) {
                rList.addAll(r);
            }
        }
        return rList;
    }


    public static <K, V, R> List<R> mapConvertList(Map<K, V> source, Function<Map.Entry<K, V>, R> getValue) {
        if (isEmpty(source)) {
            return new ArrayList<>();
        }
        List<R> rList = new ArrayList<>(source.size());
        for (Map.Entry<K, V> e : source.entrySet()) {
            rList.add(getValue.apply(e));
        }
        return rList;
    }

    public static <K, V> Map<K, List<V>> listConvertListMap(List<V> list, Function<V, K> getKey) {
        return listConvertListMap(list, getKey, Function.identity());
    }

    public static <K, V> Map<K, List<V>> listConvertListTreeMap(List<V> list, Function<V, K> getKey) {
        return listConvertListTreeMap(list, getKey, Function.identity());
    }

    public static <K, V, R> Map<K, List<R>> listConvertListMap(List<V> list, Function<V, K> getKey,
                                                               Function<V, R> getValue) {
        if (list == null || list.isEmpty()) {
            return new HashMap<>(8);
        }
        Map<K, List<R>> map = new HashMap<>(Math.min(list.size(), 256));
        K key;
        for (V v : list) {
            key = getKey.apply(v);
            map.putIfAbsent(key, new ArrayList<>());
            R r = getValue.apply(v);
            map.get(key).add(r);
        }
        return map;
    }

    public static <K, V, R> Map<K, List<R>> listConvertListTreeMap(List<V> list, Function<V, K> getKey,
                                                                   Function<V, R> getValue) {
        if (list == null || list.isEmpty()) {
            return new TreeMap<>();
        }
        Map<K, List<R>> map = new TreeMap<>();
        K key;
        for (V v : list) {
            key = getKey.apply(v);
            map.putIfAbsent(key, new ArrayList<>());
            R r = getValue.apply(v);
            map.get(key).add(r);
        }
        return map;
    }

    public static <K, V, R> Map<K, V> listConvertMap(List<R> list, Predicate<R> predicate, Function<R, K> getKey,
                                                     Function<R, V> getValue) {
        if (CommonUtil.isEmpty(list)) {
            return new HashMap<>(8);
        }
        Map<K, V> map = new HashMap<>(Math.min(list.size(), 256));
        for (int i = 0, s = list.size(); i < s; i++) {
            if (predicate.test(list.get(i))) {
                map.put(getKey.apply(list.get(i)), getValue.apply(list.get(i)));
            }
        }
        return map;
    }

    public static <E, R> Set<E> listConvertSet(List<R> list, Predicate<R> predicate, Function<R, E> getKey) {
        if (CommonUtil.isEmpty(list)) {
            return new HashSet<>(8);
        }
        Set<E> set = new HashSet<>(list.size());
        for (int i = 0, s = list.size(); i < s; i++) {
            if (predicate.test(list.get(i))) {
                set.add(getKey.apply(list.get(i)));
            }
        }
        return set;
    }

    public static <E, R> Set<E> listConvertSet(List<R> list, Function<R, E> getKey) {
        return listConvertSet(list, o -> true, getKey);
    }

    public static <R, E> List<R> listConvert(List<E> sourceList, Predicate<E> predicate, Function<E, R> getValue) {
        if (isEmpty(sourceList)) {
            return new ArrayList<>();
        }
        List<R> rList = new ArrayList<>(sourceList.size());
        for (E item : sourceList) {
            if (predicate.test(item)) {
                rList.add(getValue.apply(item));
            }
        }
        return rList;
    }

    public static String blankElse(String value, String elValue) {
        return CommonUtil.isBlank(value) ? elValue : value;
    }

    public static Object nullElse(Object value, Object elValue) {
        return value == null ? elValue : value;
    }



    public static boolean isEmpty(String value) {
        return isBlank(value);
    }

    public static boolean isEmpty(Integer value) {
        return Objects.isNull(value);
    }

    public static boolean isEmpty(Long value) {
        return Objects.isNull(value);
    }

    public static void isEmpty(Object value, String errorMsg) {
        if (isEmpty(value)) {
            throw exception(errorMsg);
        }
    }

    public static void isEmpty(Collection<?> collection, String errorMsg) {
        if (isEmpty(collection)) {
            throw exception(errorMsg);
        }
    }

    public static boolean isEmpty(LocalDateTime value) {
        return Objects.isNull(value);
    }

    public static boolean isEmpty(LocalDate value) {
        return Objects.isNull(value);
    }

    public static boolean isEmpty(Boolean value) {
        return Objects.isNull(value);
    }

    public static boolean isEmpty(Object value) {
        return Objects.isNull(value);
    }


    public static boolean isNotEmpty(String value) {
        return isNotBlank(value);
    }

    public static <T> void isNotEmptyDo(T value, Runnable runa) {
        if (isNotEmpty(value)) {
            runa.run();
        }
    }

    public static void isNotEmptyDo(String value, Runnable runa) {
        if (isNotBlank(value)) {
            runa.run();
        }
    }

    public static void isNotEmptyDo(Collection<?> collection, Runnable runna) {
        if (!isEmpty(collection)) {
            runna.run();
        }
    }

    public static boolean isNotEmpty(Integer value) {
        return Objects.nonNull(value);
    }

    public static boolean isNotEmpty(Long value) {
        return Objects.nonNull(value);
    }

    public static boolean isNotEmpty(LocalDateTime value) {
        return Objects.nonNull(value);
    }

    public static boolean isNotEmpty(LocalDate value) {
        return Objects.nonNull(value);
    }

    public static boolean isNotEmpty(Boolean value) {
        return Objects.nonNull(value);
    }

    public static boolean isNotEmpty(Object value) {
        return Objects.nonNull(value);
    }

    public static <T> T getEls(T value, T def) {
        return isEmpty(value) ? def : value;
    }

    public static String getEls(String value, String def) {
        return isBlank(value) ? def : value;
    }

    /**
     * 截取字符串
     *
     * @param value 原始字符串
     * @param index 开始位置
     * @param size 截取长度
     * @return 截取后的字符串
     */
    public static String subString(String value, int index, int size) {
        if (isBlank(value)) {
            return null;
        }
        int length = value.length();
        if (length < index) {
            return "";
        }
        if (length < size) {
            size = length - 1;
        }
        return value.substring(index, index + size);
    }

    /**
     * 截取字符串后边的数据
     *
     * @param endSize 长度
     * @return 截取后的字符串
     */
    public static String subString(String value, int endSize) {
        if (isBlank(value)) {
            return null;
        }
        int length = value.length();
        if (length < endSize) {
            endSize = length - 1;
        }
        int endIndex = length;
        int start = endIndex - endSize;
        return value.substring(start, endIndex);
    }

    /**
     * 数组转集合
     *
     * @param arr 数组
     * @param <T> 数组对象模板
     * @return 返回数组对应的集合
     */
    @SafeVarargs
    public static <T> List<T> asList(T... arr) {
        List<T> result = new ArrayList<>(arr.length);
        result.addAll(Arrays.asList(arr));
        return result;
    }

    /**
     * 根据身份证获取年龄
     *
     * @param idCard 身份证
     * @return 年龄
     */
    public static Integer getAgeByIdCard(String idCard) {
        try {
            Map<String, Object> map = new HashMap<String, Object>();
            String year = idCard.substring(6).substring(0, 4);// 得到年份
            String yue = idCard.substring(10).substring(0, 2);// 得到月份
            Date date = new Date();// 得到当前的系统时间
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            String fyear = format.format(date).substring(0, 4);// 当前年份
            String fyue = format.format(date).substring(5, 7);// 月份
            int age = 0;
            if (Integer.parseInt(yue) <= Integer.parseInt(fyue)) {
                // 当前月份大于用户出身的月份表示已过生
                age = Integer.parseInt(fyear) - Integer.parseInt(year) + 1;
            } else {// 当前用户还没过生
                age = Integer.parseInt(fyear) - Integer.parseInt(year);
            }
            return age;
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        return 0;
    }


    /**
     * 首字母转大写
     *
     * @param str
     * @return
     */
    public static String firstToUpper(String str) {
        if (isBlank(str)) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    public static String getPackageByPath(String path) {
        return path.split("java")[1].substring(1).replaceAll("\\\\", ".").replaceAll("/", ".");
    }

    public static LocalDateTime toLocalDateTime(String dateStr) {
        return LocalDateTime.parse(dateStr, DATE_TIME_FORMATTER);
    }

    /**
     * 获取某个对象字段值 获取不到则返回 null 对 hutool 反射工具了类的 获取字段值 进行二次处理
     * <p>
     * 注意，无法获取数组对象内的值
     *
     * @param obj – 对象
     * @param params – 字段名集合
     * @return
     */
    public static Object getFieldValue(Object obj, List<String> params) {
        if (Objects.isNull(obj) || isEmpty(params)) {
            return null;
        }
        Object value = obj;
        for (int i = 0; i < params.size(); i++) {
            value = ReflectUtil.getFieldValue(value, params.get(i));
            if ((i + 1) == params.size()) {
                return value;
            }
        }
        return null;
    }

    /**
     * 实体类 转 Map 实体类参数名为 key 实体类参数值为 value
     *
     * @param bean 实体类
     * @return
     */
    public static Map<String, Object> classConvertMap(Object bean) {
        Map<String, Object> returnMap = new HashMap();
        try {
            Class type = bean.getClass();
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor descriptor : propertyDescriptors) {
                String propertyName = descriptor.getName();
                if (!propertyName.equals("class")) {
                    Method readMethod = descriptor.getReadMethod();
                    Object result = readMethod.invoke(bean);
                    returnMap.put(propertyName, result);
                }
            }
            return returnMap;
        } catch (Exception e) {
            return returnMap;
        }
    }

    /**
     * 实体类 转 Map 实体类参数名为 key 实体类参数值为 value
     *
     * @param bean 实体类
     * @return
     */
    public static Map<String, String> classConvertStrMap(Object bean) {
        Map<String, String> returnMap = new HashMap();
        try {
            Class type = bean.getClass();
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor descriptor : propertyDescriptors) {
                String propertyName = descriptor.getName();
                if (!propertyName.equals("class")) {
                    Method readMethod = descriptor.getReadMethod();
                    Object result = readMethod.invoke(bean);
                    if (Objects.isNull(result)) {
                        returnMap.put(propertyName, null);
                    }  else {
                        returnMap.put(propertyName, String.valueOf(result));
                    }
                }
            }
            return returnMap;
        } catch (Exception e) {
            return returnMap;
        }
    }

    /**
     * 实体类 转 TreeMap 实体类参数名为 key 实体类参数值为 value
     *
     * @param bean 实体类
     * @return
     */
    public static TreeMap<String, Object> classConvertTreeMap(Object bean) {
        if(Map.class.isAssignableFrom(bean.getClass())){
            return new TreeMap((Map) bean);
        }
        TreeMap<String, Object> returnMap = new TreeMap();
        try {
            Class type = bean.getClass();
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor descriptor : propertyDescriptors) {
                String propertyName = descriptor.getName();
                if (!propertyName.equals("class")) {
                    Method readMethod = descriptor.getReadMethod();
                    Object result = readMethod.invoke(bean);
                    returnMap.put(propertyName, result);
                }
            }
            return returnMap;
        } catch (Exception e) {
            return returnMap;
        }
    }

    /**
     * 金额单位装换 元 -->> 分
     *
     * @param money 金额（单位：分）
     * @return Long 金额（单位：分）
     */
    public static Long moneyUnitConversion(String money) {
        if (CommonUtil.isBlank(money)) {
            return 0L;
        }
        BigDecimal b1 = new BigDecimal(money);
        BigDecimal b2 = new BigDecimal("100");
        String mul = b1.multiply(b2).setScale(0, BigDecimal.ROUND_HALF_UP).toString();
        return Long.valueOf(mul);
    }

    public static String rightPad(String str, int size, String padStr) {
        if (str == null) {
            return null;
        } else {
            if (isEmpty(padStr)) {
                padStr = " ";
            }

            int padLen = padStr.length();
            int strLen = str.length();
            int pads = size - strLen;
            if (pads <= 0) {
                return str;
            } else if (padLen == 1 && pads <= 8192) {
                return rightPad(str, size, padStr.charAt(0));
            } else if (pads == padLen) {
                return str.concat(padStr);
            } else if (pads < padLen) {
                return str.concat(padStr.substring(0, pads));
            } else {
                char[] padding = new char[pads];
                char[] padChars = padStr.toCharArray();

                for (int i = 0; i < pads; ++i) {
                    padding[i] = padChars[i % padLen];
                }

                return str.concat(new String(padding));
            }
        }
    }

    public static String rightPad(String str, int size, char padChar) {
        if (str == null) {
            return null;
        } else {
            int pads = size - str.length();
            if (pads <= 0) {
                return str;
            } else {
                return pads > 8192 ? rightPad(str, size, String.valueOf(padChar)) : str.concat(repeat(padChar, pads));
            }
        }
    }


    private static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION;
    }

    public static boolean isChinese(String strName) {
        if (isBlank(strName)) {
            return false;
        }
        char[] ch = strName.toCharArray();
        for (char c : ch) {
            if (isChinese(c)) {
                return true;
            }
        }
        return false;
    }


    public static String repeat(char ch, int repeat) {
        if (repeat <= 0) {
            return "";
        } else {
            char[] buf = new char[repeat];

            for (int i = repeat - 1; i >= 0; --i) {
                buf[i] = ch;
            }

            return new String(buf);
        }
    }

    /**
     * 将list拆分成多给指定的大小的list
     */
    public static <R> List<List<R>> splitList(List<R> target, int size) {
        if (isEmpty(target)) {
            return new ArrayList<>(0);
        }
        List<List<R>> listArr;
        // 获取被拆分的数组个数
        if (target.size() > size) {
            int number = target.size() / size;
            listArr = new ArrayList<>(number);
            for (int i = 1; i <= number; i++) {
                List<R> subList = target.subList(size * (i - 1), size * i);
                listArr.add(subList);
            }
            List<R> subList = target.subList(size * number, target.size());
            listArr.add(subList);
        } else {
            listArr = new ArrayList<>(1);
            listArr.add(target);
        }
        return listArr;
    }

    /**
     * 当前时间的时间戳
     *
     * @return 时间
     */
    public static long current() {
        return System.currentTimeMillis();
    }

    /**
     * 当前时间的时间戳（秒）
     *
     * @return 当前时间秒数
     * @since 4.0.0
     */
    public static long currentSeconds() {
        return System.currentTimeMillis() / 1000;
    }


    /**
     * 休眠(秒)
     *
     * @param seconds 秒
     * @return
     */
    public static void sleepBySeconds(long seconds) {
        sleepByMillis(seconds * 1000);
    }

    /**
     * 休眠(毫秒)
     *
     * @param millis 毫秒
     * @return
     */
    public static void sleepByMillis(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            log.error("========= 休眠失败 =========");
        }
    }

    /**
     * 去掉字符中的所有空格
     *
     * @param str 字符串
     * @return
     */
    public static String strTrimAllSpace(String str) {
        if (isBlank(str)) {
            return blankStr();
        }
        return str.replaceAll(" +", "");
    }

    public  static <T> T getElse(T value,T def){
        return Objects.isNull(value) ?def:value;
    }

    /**
     * 获取日期 yyyymmdd
     * @return
     */
    public static String getStringNow() {
        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();

        // 定义格式化器，格式为yyyyMMdd
        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

        // 格式化当前时间
        return now.format(formatter);
    }
}
