package com.younike.common.utils.redenvelope;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * 红包分配 工具类
 * 2021-9-22 Another
 */
public class RedEnvelopeUtils {

    // 红包最大金额（未使用，注掉了，之后根据需求放开或调整）
    private static final BigDecimal MAX_MONEY =  new BigDecimal("200");

    /**
     * 红包拆分生成list集合
     * 1.生成count个红包的list，将最小金额分配到每个红包上
     * 2.随机生成一个数值，在原list上做加法
     * @param money        总金额
     * @param count        总数
     * @param minMoney     最小金额
     * @param maxMoney     最大金额
     * @param bigRed       大包固定金额（未使用，注掉了，之后根据需求放开或调整）
     * @param bigCount     大包个数（未使用，注掉了，之后根据需求放开或调整）
     * @return
     */
    public static List<BigDecimal> splitRedPackets(BigDecimal money, BigDecimal count, BigDecimal minMoney, BigDecimal maxMoney
            , BigDecimal bigRed, BigDecimal bigCount) {

        //计算小包金额总数和总金额
        /*count = count.subtract(bigCount);
        money = money.subtract(bigCount.multiply(bigRed));

        //大包固定金额集合
        List<BigDecimal> bigList = new ArrayList<>();
        for(int i=0; i<bigCount.intValue(); i++) {
            bigList.add(bigRed);
        }*/

        //原始list--小包list
        List<BigDecimal> list = new ArrayList<>();
        // maxMoney = (maxMoney.compareTo(MAXMONEY) > 0) ? MAXMONEY : maxMoney;

        // 1.将最小金额分配到每个红包上
        // 2.减去分配的小包金额
        // 3.剩余总金额 =总金额-最小金额*最小金额数
        for(int i=0; i<count.intValue(); i++) {
            list.add(minMoney);
        }
        BigDecimal minSum = minMoney.multiply(count);
        BigDecimal totalMoney = money.subtract(minSum);
        BigDecimal realMaxMoney = maxMoney.subtract(minMoney);

        //判断是否符合取值区间
        if(!isRight(totalMoney, count, realMaxMoney, new BigDecimal("0"))) {
            return null;
        }

        //合并后的新包
        List<BigDecimal> newList = new ArrayList<>();
        for(int i=0; i<list.size(); i++) {
            BigDecimal one = randomRedPacket(totalMoney, new BigDecimal("0"), realMaxMoney, new BigDecimal(count.intValue()-i));
            newList.add(list.get(i).add(one));
            totalMoney = totalMoney.subtract(one);
        }

        //合并打包固定金额集合
        // newList.addAll(bigList);
        Collections.shuffle(newList);
        return newList;
    }


    /**
     * 随机方法产生一个在最大值和最小值之间的一个红包，
     * 并判断该红包是否合法，是否在产生这个红包之后红包金额变成负数。
     * 另外，在这次产生红包值较小时，下一次就产生一个大一点的红包。
     *
     * @param money   总金额
     * @param minMoney    最小金额
     * @param maxMoney    最大金额
     * @param count   红包总数
     * @return 一个随机红包金额
     */
    private static BigDecimal randomRedPacket(BigDecimal money, BigDecimal minMoney, BigDecimal maxMoney, BigDecimal count) {
        if(count.intValue() == 1) {
            return money.setScale(2, RoundingMode.UP);
        }
        if(minMoney.compareTo(maxMoney) == 0) {
            return minMoney;    //如果最大值和最小值一样，就返回 minMoney
        }

        BigDecimal max = (maxMoney.compareTo(money) > 0) ? money : maxMoney;
        //返回指定范围的随机数,保留两位小数
        BigDecimal random = BigDecimal.valueOf(Math.random());
        BigDecimal middle = maxMoney.subtract(minMoney);
        BigDecimal middle2 = random.multiply(middle).setScale(2, RoundingMode.HALF_UP);
        BigDecimal one = middle2.add(minMoney);

        BigDecimal moneyOther = money.subtract(one);
        if(isRight(moneyOther, count.subtract(new BigDecimal("1")), maxMoney, minMoney)) {
            return one;
        } else {
            //重新分配
            BigDecimal avg = moneyOther.divide(count.subtract(new BigDecimal("1")),2, RoundingMode.UP);
            if(avg.compareTo(minMoney) < 0) {
                return randomRedPacket(money, minMoney, one, count);
            } else if(avg.compareTo(maxMoney) > 0) {
                return randomRedPacket(money, one, maxMoney, count);
            }
        }
        return one;
    }

    /**
     * 判断是否符合取值区间
     * @param money  总金额
     * @param count  总数
     * @return 返回是否合法
     */
    private static boolean isRight(BigDecimal money,BigDecimal count,BigDecimal maxs,BigDecimal mins) {
        BigDecimal avg = money.divide(count,2, RoundingMode.UP);
        if(avg.compareTo(mins) < 0) {
            return false;
        } else return avg.compareTo(maxs) <= 0;
    }

}
