package com.engine.salary.util;

import com.alibaba.fastjson.JSON;
import com.engine.salary.enums.SalaryRoundingModeEnum;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * 实体类相关
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
public class SalaryEntityUtil {

    private static final DecimalFormat decimalFormat = new DecimalFormat("#,##0.00");

    /**
     * 数字正则表达式
     * 包含负数、正数、小数、0、0.00000等
     */
    public static final String NUMBER_REGEX = "(-?[1-9]\\d*\\.?\\d+)|(-?0\\.\\d*[0-9])|(\\d+)";

    /**
     * 千分位格式化
     *
     * @param originMap 原始map
     * @param targetMap 目标map
     */
    public static void thousandthConvert(Map<String, Object> originMap, Map<String, Object> targetMap) {

        if (MapUtils.isNotEmpty(originMap)) {
            originMap.forEach((k, v) -> {
                if (StringUtils.isNotBlank(String.valueOf(v))) {
                    targetMap.put(k, decimalFormat.format(Double.valueOf(String.valueOf(v))));
                }
            });
        }
    }

    /**
     * 千分位格式化
     *
     * @param originString 原始字符串
     * @return 格式化后的字符串
     */
    public static String thousandthConvert(String originString) {
        if (StringUtils.isNotBlank(originString)) {
            return decimalFormat.format(Double.valueOf(originString));
        }
        return "0.00";
    }

    /**
     * 判断对象或对象数组中每一个对象是否为空: 对象为null，字符序列长度为0，集合类、Map为empty
     *
     * @param obj
     * @return
     */
    public static boolean isNullOrEmpty(Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj instanceof CharSequence) {
            return ((CharSequence) obj).length() == 0;
        }
        if (obj instanceof Collection) {
            return ((Collection) obj).isEmpty();
        }
        if (obj instanceof Map) {
            return ((Map) obj).isEmpty();
        }
        if (obj instanceof Object[]) {
            Object[] object = (Object[]) obj;
            if (object.length == 0) {
                return true;
            }
            boolean empty = true;
            for (int i = 0; i < object.length; i++) {
                if (!isNullOrEmpty(object[i])) {
                    empty = false;
                    break;
                }
            }
            return empty;
        }

        return false;
    }

    public static boolean isNotNullOrEmpty(Object obj) {
        return !isNullOrEmpty(obj);
    }

    public static <R, T, A> A properties(Collection<T> objs, Function<T, R> function, Collector<R, ?, A> collectors) {
        return objs.stream().map(function).collect(collectors);
    }

    public static <R, T> Set<R> properties(Collection<T> objs, Function<T, R> function) {
        if (CollectionUtils.isEmpty(objs)) {
            return Sets.newHashSet();
        }
        return properties(objs, function, Collectors.toSet());
    }

    public static <R, T> Map<R, T> convert2Map(Collection<T> objs, Function<T, R> function) {
        if (CollectionUtils.isEmpty(objs)) {
            return Maps.newHashMap();
        }
        return objs.stream().collect(Collectors.toMap(function, Function.identity(), (a, b) -> a));
    }

    public static <T, K, V> Map<K, V> convert2Map(Collection<T> objs, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        if (CollectionUtils.isEmpty(objs)) {
            return Maps.newHashMap();
        }
        return objs.stream()
                .filter(e -> valueMapper.apply(e) != null && keyMapper.apply(e) != null)
                .collect(Collectors.toMap(keyMapper, valueMapper, (a, b) -> a));
    }

    public static <R, T> Map<R, List<T>> group2Map(Collection<T> objs, Function<T, R> function) {
        if (CollectionUtils.isEmpty(objs)) {
            return Maps.newHashMap();
        }
        return objs.stream().collect(Collectors.groupingBy(function));
    }

    public static <T, K, V> Map<K, Set<V>> group2Map(Collection<T> objs, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        if (CollectionUtils.isEmpty(objs)) {
            return Maps.newHashMap();
        }
        return objs.stream()
                .filter(e -> keyMapper.apply(e) != null && valueMapper.apply(e) != null)
                .collect(Collectors.groupingBy(keyMapper,
                        Collectors.collectingAndThen(Collectors.toList(), e -> e.stream().map(valueMapper).collect(Collectors.toSet()))));
    }

    public static <T, K, V> Map<K, List<V>> group2ListMap(Collection<T> objs, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        if (CollectionUtils.isEmpty(objs)) {
            return Maps.newHashMap();
        }
        return objs.stream()
                .filter(e -> keyMapper.apply(e) != null && valueMapper.apply(e) != null)
                .collect(Collectors.groupingBy(keyMapper,
                        Collectors.collectingAndThen(Collectors.toList(), e -> e.stream().map(valueMapper).collect(Collectors.toList()))));
    }

    /**
     * LinkedHashMap有序去重
     *
     * @param keyExtractor
     * @param <T>
     * @return
     */
    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        LinkedHashMap<Object, Boolean> map = new LinkedHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * ConcurrentHashMap无序去重
     *
     * @param keyExtractor
     * @param <T>
     * @return
     */
    public static <T> Predicate<T> distinctByKeyMap(Function<? super T, Object> keyExtractor) {
        ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    public static <T> BigDecimal reduce(Collection<T> objs, Function<T, BigDecimal> function) {
        if (CollectionUtils.isEmpty(objs)) {
            return BigDecimal.ZERO;
        }
        return objs.stream()
                .filter(e -> function.apply(e) != null)
                .map(function)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    public static BigDecimal empty2Zero(String value) {
        if (StringUtils.isEmpty(value)) {
            return BigDecimal.ZERO;
        }
        try {
            return new BigDecimal(value);
        } catch (Exception e) {
            return BigDecimal.ZERO;
        }
    }


    /**
     * 进位规则
     * @param newScale 小数位
     * @param rententionRule 进位规则
     * @param value 值
     * @return
     */
    public static BigDecimal carryRule(Integer newScale, Integer rententionRule, BigDecimal value) {
        RoundingMode roundingMode = RoundingMode.HALF_UP;
        //原始数据
        if (Objects.equals(rententionRule, SalaryRoundingModeEnum.RAW_DATA.getValue())) {
            roundingMode = RoundingMode.DOWN;
        }
        //四舍五入
        if (Objects.equals(rententionRule, SalaryRoundingModeEnum.ROUNDING.getValue())) {
            roundingMode = RoundingMode.HALF_UP;
        }
        //向上舍入
        if (Objects.equals(rententionRule, SalaryRoundingModeEnum.ROUND_UP.getValue())) {
            roundingMode = RoundingMode.UP;
        }
        //向下舍入
        if (Objects.equals(rententionRule, SalaryRoundingModeEnum.ROUND_DOWN.getValue())) {
            roundingMode = RoundingMode.DOWN;
        }
        //见分取角（只取保留小数后一位向上舍入）
        if (Objects.equals(rententionRule, SalaryRoundingModeEnum.CEILING.getValue())) {
            value = value.setScale(newScale + 1, RoundingMode.FLOOR);
            roundingMode = RoundingMode.UP;
        }
        //向上取偶
        if (Objects.equals(rententionRule, SalaryRoundingModeEnum.UP_EVEN.getValue())) {
            value = value.setScale(newScale, RoundingMode.UP);

            int number = value.intValue();
            if (number % 2 != 0) {
                value = value.add(BigDecimal.valueOf(1));
            }
        }

        return value.setScale(newScale, roundingMode);
    }

    /**
     * 两个集合是否有交集
     *
     * @param list1
     * @param list2
     * @param <T>
     * @return
     */
    public static <T> boolean judgeIntersection(List<T> list1, List<T> list2) {
        boolean flag = false;
        List<T> origin = new ArrayList<>();
        origin.addAll(list1);
        origin.retainAll(list2);
        if (origin.size() > 0) {
            flag = true;
        }
        return flag;
    }

    /**
     * String转Long
     *
     * @param obj
     * @return
     */
    public static Long string2Long(String obj) {
        if (NumberUtils.isCreatable(obj)) {
            return Long.valueOf(obj);
        }
        return null;
    }

    /**
     * String转Integer
     *
     * @param obj
     * @return
     */
    public static Integer string2Integer(String obj) {
        if (NumberUtils.isCreatable(obj)) {
            return Integer.valueOf(obj);
        }
        return null;
    }

    /**
     * String转BigDecimal
     *
     * @param obj
     * @return
     */
    public static BigDecimal string2BigDecimal(String obj) {
        if (NumberUtils.isCreatable(obj)) {
            return new BigDecimal(obj);
        }
        return null;
    }

    /**
     * String转BigDecimal
     *
     * @param obj
     * @return
     */
    public static BigDecimal string2BigDecimalDefault0(String obj) {
        if (NumberUtils.isCreatable(obj)) {
            return new BigDecimal(obj);
        }
        return BigDecimal.ZERO;
    }

    /**
     * 判断字符串是否等于0
     *
     * @param obj
     * @return
     */
    public static boolean StringEqZERO(String obj) {
        if (NumberUtils.isCreatable(obj)) {
            return BigDecimal.ZERO.compareTo(new BigDecimal(obj)) == 0;
        }
        return false;
    }


    public static Double string2DoubleDefault0(String obj) {
        if (NumberUtils.isCreatable(obj)) {
            return new Double(obj);
        }
        return new Double("0.0");
    }

    /**
     * 两个集合交集，（forEasy）
     *
     * @param arr1
     * @param arr2
     * @return
     */
    public static <T> Collection<T> intersectionForList(Collection<T> arr1, Collection<T> arr2) {

        Collection<T> resultList = new ArrayList<>();

        if (CollectionUtils.isEmpty(arr1) || CollectionUtils.isEmpty(arr1)) {
            return resultList;
        }
        arr1.forEach(a1 -> {
            if (arr2.contains(a1)) {
                resultList.add(a1);
            }
        });
        return resultList;
    }

    public static String toJSONString(Object obj) {
        if (obj != null) {
            return JSON.toJSONString(obj);
        }
        return "";
    }


}
