package com.yonge.cooleshow.biz.dal.support;

import com.alibaba.fastjson.JSONObject;
import com.yonge.toolset.base.exception.BizException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author hgw
 * Created by 2021-12-20
 */
public class WrapperUtil {

    public static final Predicate<? super Object> ObjPredicate = (Predicate<Object>) Objects::nonNull;

    public static final Predicate<? super String> StrPredicate = (Predicate<String>) StringUtils::isNotBlank;

    public static final Predicate<? super Collection<?>> ListPredicate = (Predicate<Collection<?>>) CollectionUtils::isNotEmpty;

    public static <S, O> Integer toInt(Map<S, O> map, S str, String exMsg) {
        Optional<O> o = Optional.ofNullable(map)
                .map(m -> m.get(str));
        return intOptional(o)
                .orElseThrow(() -> new BizException(exMsg));
    }

    public static <S, O> Integer toInt(Map<S, O> map, S str) {
        Optional<O> o = Optional.ofNullable(map)
                .map(m -> m.get(str));
        return intOptional(o)
                .orElse(null);
    }

    public static <S, O> Long toLong(Map<S, O> map, S str) {
        return Optional.ofNullable(map.get(str))
                .map(String::valueOf)
                .map(Long::parseLong)
                .orElse(null);
    }

    public static <S, O> Long toLong(Map<S, O> map, S str, String exMsg) {
        return Optional.ofNullable(map.get(str))
                .map(String::valueOf)
                .map(Long::parseLong)
                .orElseThrow(() -> new BizException(exMsg));
    }

    public static <O> Optional<Integer> intOptional(Optional<O> optional) {
        return optional
                .map(String::valueOf)
                .map(Integer::valueOf);
    }

    public static <S, O> String toStr(Map<S, O> map, S str) {
        Optional<O> o = Optional.ofNullable(map)
                .map(m -> m.get(str));
        return strOptional(o)
                .orElse(null);
    }

    public static <S, O> String toStr(Map<S, O> map, S str, String exMsg) {
        Optional<O> o = Optional.ofNullable(map)
                .map(m -> m.get(str));
        return strOptional(o)
                .orElseThrow(() -> new BizException(exMsg));
    }

    private static <O> Optional<String> strOptional(Optional<O> optional) {
        return optional
                .filter(s -> s instanceof String)
                .map(String::valueOf)
                .filter(StrPredicate);
    }

    public static List<String> toList(String key) {
        return toList(key, ",");
    }

    public static List<Long> toLongList(String key) {
        return toList(key, ",").stream().map(o -> Long.parseLong(o)).collect(Collectors.toList());
    }

    public static List<String> toList(String key, String symbol) {
        return toListOptional(key, symbol)
                .orElse(null);
    }

    /**
     * 将用逗号分割的字符串转换为List
     *
     * @param key 用逗号分割的字符
     * @return Optional
     */
    public static Optional<List<String>> toListOptional(String key) {
        return toListOptional(key, ",");
    }

    public static Optional<List<String>> toListOptional(String key, String symbol) {
        return Optional.ofNullable(key)
                .filter(StrPredicate)
                .map(b -> b.split(symbol))
                .map(WrapperUtil::collectToList)
                .filter(ListPredicate);
    }

    public static List<String> collectToList(String[] b) {
        return Arrays.stream(b)
                .filter(StrPredicate)
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 检查对象是否有值
     */
    public static <T> void checkObj(T obj, String errMsg) {
        Optional.ofNullable(obj).orElseThrow(() -> new BizException(errMsg));
    }

    /**
     * 全部有值则为true
     *
     * @param obj 多个对象
     */
    public static boolean checkObj(Object... obj) {
        return Arrays.stream(obj).noneMatch(Objects::isNull);
    }

    /**
     * 检查str中是否包含 o
     * 包含返回true
     */
    public static boolean checkStr(String o, String... str) {
        if (StrPredicate.test(o) && ObjPredicate.test(str)) {
            return Arrays.asList(str).contains(o);
        } else {
            return false;
        }
    }

    public static boolean checkInObj(Object o, Object... str) {
        if (ObjPredicate.test(str)) {
            return Arrays.asList(str).contains(o);
        } else {
            return false;
        }
    }

    /**
     * 根据分组groupVal进行聚合分组
     *
     * @param list     待聚合的集合
     * @param groupVal 分组value
     */
    public static <S, L> Map<S, List<L>> groupList(List<L> list, Function<? super L, ? extends S> groupVal) {
        return list.stream()
                .filter(ObjPredicate)
                .collect(Collectors.groupingBy(groupVal));
    }

    /**
     * 根据groupVal进行聚合累加
     *
     * @param list   待聚合的集合
     * @param sumVal 需要聚合操作的字段
     */
    public static <L> BigDecimal sumList(List<L> list, Function<? super L, BigDecimal> sumVal) {
        return list.stream()
                .map(sumVal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    /**
     * 对比a,b集和
     * <p> 示例
     * <p>  传入a -> [1,3,4,6]
     * <p>  传入b -> [1,2,4]
     * <p> 执行该方法后a,b会被修改为
     * <p> a -> [3,6]
     * <p> b -> [2]
     */
    @SuppressWarnings("unchecked")
    public static <O> void listDifference(List<O> a, List<O> b) {
        //将ab并集
        Collection<O> abUn = CollectionUtils.retainAll(a, b);
        //删除a中abUn的值
        a.removeAll(abUn);
        //删除b中abUn的值
        b.removeAll(abUn);
    }

    @SuppressWarnings("unchecked")
    public static Map<String, Object> toMap(Object obj) {
        if (obj instanceof Map) {
            return (Map<String, Object>) obj;
        }
        return Optional.ofNullable(obj)
                .map(JSONObject::toJSON)
                .map(o -> JSONObject.toJavaObject((JSONObject) o, Map.class))
                .orElse(new HashMap<String, Object>());
    }

    /**
     * 判断2个时间段是否有交集-默认允许结束时间和开始时间相等
     *
     * @param startDateOne 第一个时间段的开始时间
     * @param endDateOne   第一个时间段的结束时间
     * @param startDateTwo 第二个时间段的开始时间
     * @param endDateTwo   第二个时间段的结束时间
     * @return 有交集 true 无交集 false
     */
    public static Boolean inInterSection(Date startDateOne, Date endDateOne, Date startDateTwo, Date endDateTwo) {
        return inInterSection(startDateOne, endDateOne, startDateTwo, endDateTwo, true);
    }

    /**
     * 判断2个时间段是否有交集
     *
     * @param startDateOne 第一个时间段的开始时间
     * @param endDateOne   第一个时间段的结束时间
     * @param startDateTwo 第二个时间段的开始时间
     * @param endDateTwo   第二个时间段的结束时间
     * @param eq           是否允许结束时间和开始时间相等 true 允许
     * @return 有交集 true 无交集 false
     */
    public static Boolean inInterSection(Date startDateOne, Date endDateOne, Date startDateTwo, Date endDateTwo, boolean eq) {
        Date maxStartDate = startDateOne;
        if (maxStartDate.before(startDateTwo)) {
            maxStartDate = startDateTwo;
        }

        Date minEndDate = endDateOne;
        if (endDateTwo.before(minEndDate)) {
            minEndDate = endDateTwo;
        }
        //是否允许结束时间和开始时间相等 true 允许
        if (eq) {
            return maxStartDate.before(minEndDate);
        } else {
            return maxStartDate.before(minEndDate) || (maxStartDate.getTime() == minEndDate.getTime());
        }
    }

    /**
     * 计算平均价格，保留2位
     * <p> 例如：总数3  总金额10
     * <p> 返回：
     * <p> key   value
     * <p> 1 ：3.33
     * <p> 2 ：3.33
     * <p> 3 ：3.34
     * <p> 最后1 ，2，3的的金额总数还是为10
     *
     * @param totalNum   总数
     * @param totalPrice 总金额
     * @return key 数 value 平均金额
     */
    public static Map<Integer, BigDecimal> getAveragePrice(Integer totalNum, BigDecimal totalPrice) {
        //单价 四舍五入
        BigDecimal unitPrice = totalPrice.divide(BigDecimal.valueOf(totalNum), 2, RoundingMode.HALF_UP);
        //累计总额
        BigDecimal cumulativeAmount = BigDecimal.ZERO;
        Map<Integer, BigDecimal> map = new HashMap<>();
        for (int i = 1; i <= totalNum; i++) {
            //最后一个数，直接用总金额减去累计总额
            if (i == totalNum) {
                unitPrice = totalPrice.subtract(cumulativeAmount).setScale(2, RoundingMode.HALF_UP);
            }
            cumulativeAmount = cumulativeAmount.add(unitPrice);
            map.put(i, unitPrice);
        }
        return map;
    }

    /**
     * 字符串转公式
     *
     * @param formula  公式字符串
     * @param variable 变量
     * @param param    参数
     * @return
     */
    public static Object strToFormula(String formula, String variable, String param) {
        ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
        try {
            return jse.eval(formula.replace(variable, param));
        } catch (ScriptException e) {
            e.printStackTrace();
        }
        return null;
    }
}
