package com.jeeplus.test.utils;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * 金额处理工具类
 * @author: 徐滕
 * @version: 2024-01-09 11:16
 * @modifyLog
 */

public class MoneyUtil {

    private MoneyUtil() {}

    private static String MODULUS = "10000";
    /**
     * Double类型需要进行格式化，否则会出现科学计数法
     */
    private static DecimalFormat DF = new DecimalFormat("###################.0000000");

    /**
     * 元转万元（默认四舍五入保留2位小数）
     * @param amt
     * @return
     */
    public static BigDecimal toTenThousand(String amt) {
        if (isNotNumber(amt)) {
            return BigDecimal.ZERO;
        }
        return toTenThousand(new BigDecimal(amt));
    }

    /**
     * 元转万元（默认四舍五入保留6位小数）
     * @param amt 不推荐用Double存金额，数值过大会导致精度丢失
     * @return
     */
    public static BigDecimal toTenThousand(Double amt) {
        if (Objects.isNull(amt)) {
            return BigDecimal.ZERO;
        }
        String amtStr = String.valueOf(DF.format(amt));
        return toTenThousand(amtStr);
    }

    /**
     * 元转万元（默认四舍五入保留2位小数）
     * @param amt
     * @return
     */
    public static BigDecimal toTenThousand(BigDecimal amt) {
        return amt.divide(new BigDecimal(MODULUS)).setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 万元转元（默认四舍五入保留2位小数）
     * @param amt
     * @return
     */
    public static BigDecimal toTheUnit(String amt) {
        if (isNotNumber(amt)) {
            return BigDecimal.ZERO;
        }
        return toTheUnit(new BigDecimal(amt));
    }

    /**
     * 万元转元（默认四舍五入保留2位小数）
     * @param amt 不推荐用Double存金额，数值过大会导致精度丢失
     * @return
     */
    public static BigDecimal toTheUnit(Double amt) {
        if (Objects.isNull(amt)) {
            return BigDecimal.ZERO;
        }
        String amtStr = String.valueOf(DF.format(amt));
        return toTheUnit(amtStr);
    }

    /**
     * 万元转元（默认四舍五入保留2位小数）
     * @param amt
     * @return
     */
    public static BigDecimal toTheUnit(BigDecimal amt) {
        return amt.multiply(new BigDecimal(MODULUS)).setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 数字 + 中文金额转纯金额，单位元（例：500万元，输出结果5000000）
     * @param str
     * @return
     */
    public static BigDecimal getMoney(String str) {
        StringBuffer sb = new StringBuffer();
        if(isNotBlank(str)){
            for(int i = 0; i < str.length(); i++){
                // 46是小数点 （.）
                if(str.charAt(i) >= 48 && str.charAt(i) <= 57 || str.charAt(i) == 46){
                    sb.append(str.charAt(i));
                }
            }
        }
        String res = sb.toString();
        if (isNotBlank(res) && str.contains(UNIT_ARR[7])) {
            return toTheUnit(res);
        }
        if (isNotBlank(res) && str.contains(UNIT_ARR[8])) {
            return new BigDecimal(res).multiply(getUnit(8));
        }
        if (isNotBlank(res)) {
            return new BigDecimal(res);
        }
        return BigDecimal.ZERO;
    }

    /**
     * 对某个数保留小数位数的
     * @author zql
     * @createTime 2020-12-05 16:26:02
     *
     * @param num 某个数
     * @param type 类型，1四舍五入，2五舍六入，3进位处理（就是直接加1），4直接去掉尾数
     * <pre>
     * type = 2，五舍六入时，需要看要求精度后面的第二位（如果存在）是否大于等于5，如果是则会进位
     * </pre>
     * @param count 要保留的小数位数
     * @return 返回double类型的结果
     * <pre>
     * 如果num不是数字时，返回0
     * 如果不处于类型内，则默认四舍五入
     * 如果count小于1时，默认为保留两位小数
     * </pre>
     *
     */
    public static BigDecimal getBigDecimal(String num, int type, int count) {
        if (isNotNumber(num)) {
            return BigDecimal.ZERO;
        }
        if (type < 1 || type > 4) {
            type = 1;
        }
        if (count < 1) {
            count = 2;
        }
        BigDecimal b = new BigDecimal(num);
        BigDecimal r;
        switch (type) {
            case 1:
                // 四舍五入
                r = b.setScale(count, BigDecimal.ROUND_HALF_UP);
                break;
            case 2:
                // 五舍六入，需要看要求精度后面的第二位（如果存在）是否大于等于5，如果是则会进位
                r = b.setScale(count, BigDecimal.ROUND_HALF_DOWN);
                break;
            case 3:
                // 进位处理（就是直接加1）
                r = b.setScale(count, BigDecimal.ROUND_UP);
                break;
            case 4:
                // 直接去掉尾数
                r = b.setScale(count, BigDecimal.ROUND_DOWN);
                break;
            default:
                r = BigDecimal.ZERO;
        }
        return r;
    }

    /**
     * 将大写金额转化为数字金额
     * @author zql
     * @createTime 2020-11-15 21:53:26
     * @version 1.0
     *
     * @param cnStr 大写金额
     * @return
     * @throws Exception
     */
    public static BigDecimal cnToNum(String cnStr) {
        if (isBlank(cnStr)) {
            return BigDecimal.ZERO;
        }
        cnStr = cnStr.replace("元", "圆");
        // 兆级处理
        if (cnStr.indexOf(UNIT_ARR[9]) != -1) {
            return cnToNum1(cnStr);
        } else if (cnStr.indexOf(UNIT_ARR[8]) != -1) {// 亿级处理
            return cnToNum2(cnStr);
        } else if (cnStr.indexOf(UNIT_ARR[7]) != -1) {// 万级处理
            return cnToNum3(cnStr);
        } else {// 低于万级处理
            return cnToNum4(cnStr);
        }
    }

    /**
     * 兆级处理
     * @author zql
     * @createTime 2020-11-15 21:53:26
     * @version 1.0
     *
     * @param cnStr 大写金额
     * @return
     * @throws Exception
     */
    private static BigDecimal cnToNum1(String cnStr) {
        String prefix = cnStr.substring(0, cnStr.indexOf(UNIT_ARR[9]));
        BigDecimal preB = cnToNum4(prefix).multiply(getUnit(9));

        String suffix = cnStr.substring(cnStr.indexOf(UNIT_ARR[9]) + 1, cnStr.length());
        BigDecimal suffB = cnToNum2(suffix);
        return preB.add(suffB);
    }

    /**
     * 亿级处理
     * @author zql
     * @createTime 2020-11-15 21:53:26
     * @version 1.0
     *
     * @param cnStr 大写金额
     * @return
     * @throws Exception
     */
    private static BigDecimal cnToNum2(String cnStr) {
        String prefix = cnStr.substring(0, cnStr.indexOf(UNIT_ARR[8]));
        BigDecimal res = cnToNum4(prefix);
        BigDecimal preB = res.multiply(getUnit(8));

        String suffix = cnStr.substring(cnStr.indexOf(UNIT_ARR[8]) + 1, cnStr.length());
        BigDecimal suffB = cnToNum3(suffix);
        return preB.add(suffB);
    }

    /**
     * 万级处理
     * @author zql
     * @createTime 2020-11-15 21:53:26
     * @version 1.0
     *
     * @param cnStr 大写金额
     * @return
     * @throws Exception
     */
    private static BigDecimal cnToNum3(String cnStr) {
        String prefix = cnStr.substring(0, cnStr.indexOf(UNIT_ARR[7]));
        BigDecimal res = cnToNum4(prefix);
        BigDecimal preB = res.multiply(getUnit(7));

        String suffix = cnStr.substring(cnStr.indexOf(UNIT_ARR[7]) + 1, cnStr.length());
        BigDecimal suffB = cnToNum4(suffix);
        return preB.add(suffB);
    }

    /**
     * 低于万级处理
     * @author zql
     * @createTime 2020-11-15 21:53:26
     * @version 1.0
     *
     * @param cnStr 大写金额
     * @return
     * @throws Exception
     */
    private static BigDecimal cnToNum4(String cnStr) {
        BigDecimal result = BigDecimal.ZERO;
        int lastUnitIndex = 0, num = 0;
        for (int i = 0; i < cnStr.length(); i++) {
            boolean isUnit = true;
            char c = cnStr.charAt(i);
            for (int j = 0; j < NUM_CN.length; j++) {
                // 是数字
                if (c == NUM_CN[j].charAt(0)) {
                    // 数字值 = 索引
                    num = j;
                    isUnit = false;
                    break;
                }
            }
            if (isUnit) {
                // 第一个就是单位，如：拾伍万圆整
                if (i == 0) {
                    num = 1;
                }
                int unitIndex = getUnitIndex(c);
                BigDecimal unit = getUnit(unitIndex);
                // 大写乘单位的结果
                BigDecimal unitSource = new BigDecimal(num).multiply(unit);
                if (unitIndex > lastUnitIndex) {
                    result = result.add(new BigDecimal(num)).multiply(unit);
                } else {
                    result = result.add(unitSource);
                }
                lastUnitIndex = unitIndex;
                num = 0;
            } else if (i == (cnStr.length() - 1)) {// 最后一位处理
                result = result.add(new BigDecimal(num));
            }
        }
        return result.setScale(2, BigDecimal.ROUND_DOWN);
    }

    /**
     * 将数字金额转化为大写金额
     * @author zql
     * @createTime 2020-11-15 21:53:26
     * @version 1.0
     *
     * @param amount
     * @return
     * @throws Exception
     */
    public static String toBigAmt(String amount) throws Exception {
        if (isBlank(amount)) {
            throw new Exception("输入的金额为空！");
        }
        if (isNotNumber(amount)) {
            throw new Exception("输入的金额【" + amount + "】不是数字！" );
        }
        // 四舍五入
        String amt = new BigDecimal(amount).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
        // 取小数位
        String dotPart = "";
        // 取整数位
        String intPart = "";
        // 结果
        String result = null;
        int dotPos;

        if ((dotPos = amt.indexOf(".")) != -1) {
            intPart = amt.substring(0, dotPos);
            dotPart = amt.substring(dotPos + 1);
        } else {
            intPart = amt;
        }
        if (intPart.length() > 16) {
            throw new Exception("金额太大【" + amount + "】，无法转换成大写金额！");
        }
        String intBig = intToBig(intPart);
        String dotBig = dotToBig(dotPart);

        if (dotBig.length() == 0 && intBig.length() != 0) {
            result = intBig + "元整";
        } else if (dotBig.length() == 0 && intBig.length() == 0) {
            result = intBig + "零元";
        } else if (dotBig.length() != 0 && intBig.length() != 0) {
            result = intBig + "元" + dotBig;
        } else {
            result = dotBig;
        }
        return result;
    }

    /**
     * 阿拉伯对应中文
     */
    private static String[] NUM_CN = new String[]{"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
    /**
     * 单位
     */
    private static String[] UNIT_ARR = new String[]{"厘", "分", "角", "圆", "拾", "佰", "仟", "万", "亿", "兆"};

    /**
     * 用来处理整数部分
     * @author zql
     * @createTime 2020-11-15 21:25:44
     * @version 1.0
     *
     * @param intPart
     * @return
     */
    private static String intToBig(String intPart) {
        int grade = 4;
        // 级长
        int gradeLen;

        String result = "";
        String strTmp = "";

        // 得到当前级长
        gradeLen = intPart.length() / grade;
        // 调整级次长度
        if (intPart.length() % grade != 0) {
            gradeLen += 1;
        }
        for (int i = gradeLen; i >= 1; i--) {
            // 取得当前级次数字
            strTmp = getNowGradeVal(intPart, i);
            // 转换大写
            result += getSubUnit(strTmp);
            // 除零 去掉连续的零
            result = dropZero(result);
            // 加级次单位，末位不加单位
            if (i > 1) {
                // 单位不能相连续，连续4个零要特殊处理
                if ("零零零零".equals(getSubUnit(strTmp))) {
                    result += "零";
                } else {
                    result += UNIT_ARR[6 + (i - 1)];
                }
            }
        }
        return result;
    }

    /**
     * 删除“零”
     * @author zql
     * @createTime 2020-11-28 01:31:31
     * @version 1.0
     *
     * @param val
     * @return
     */
    private static String dropZero(String val) {
        // 结果
        String rst;
        // 前一位置字符
        String before;
        // 当前位置字符
        String now;

        before = val.substring(0, 1);
        rst = before;

        for (int i = 1; i < val.length(); i++) {
            now = val.substring(i, i + 1);
            // 不同时为零
            if (!("零".equals(now) && "零".equals(before))) {
                rst += now;
            }
            before = now;
        }
        // 结果长度
        int rstLen = rst.length();
        // 末位去零
        if ("零".equals(rst.substring(rstLen - 1, rstLen))) {
            rst = rst.substring(0, rstLen - 1);
        }
        return rst;
    }
    /**
     * 用来处理小数部分
     * @author zql
     * @createTime 2020-11-15 21:34:39
     * @version 1.0
     *
     * @param dotPart
     * @return
     */
    private static String dotToBig(String dotPart) {
        String ret = "";
        for (int i = 0; i < dotPart.length() && i < 3; i++) {
            int n;
            if ((n = Integer.parseInt(dotPart.substring(i, i + 1))) != 0) {
                ret += NUM_CN[n] + UNIT_ARR[2 - i];
            }
        }
        return ret;
    }

    /**
     * 数值转换
     * @author zql
     * @createTime 2021-01-03 03:09:06
     *
     * @param val
     * @return
     * @throws
     */
    private static String getSubUnit(String val) {
        String rst = "";
        int valLen = val.length();

        for (int i = 0; i < valLen; i++) {
            String s = val.substring(i, i + 1);
            int n = Integer.parseInt(s);
            // “零”作特殊处理，转换后数末不能为零
            if (n == 0 && i != valLen - 1) {
                rst += "零";
            } else {
                rst += NUM_CN[n];
                // 个位不加单位
                if (i != valLen - 1) {
                    rst += UNIT_ARR[6 - (i + 4 - valLen)];
                }
            }
        }
        return rst;
    }

    /**
     * 得到当前级次的字符串
     * @author zql
     * @createTime 2021-01-03 03:09:47
     *
     * @param val
     * @param grade
     * @return
     * @throws
     */
    private static String getNowGradeVal(String val, int grade) {
        int curGrade = 4;
        String rst;
        int valLen = val.length();
        if (valLen <= grade * curGrade) {
            rst = val.substring(0, valLen - (grade - 1) * curGrade);
        } else {
            rst = val.substring(valLen - grade * curGrade, valLen - (grade - 1) * curGrade);
        }
        return rst;
    }

    /**
     * 获取单位索引
     * @param c
     * @return
     */
    private static int getUnitIndex(char c) {
        for (int j = 0; j < UNIT_ARR.length; j++) {
            if (c == UNIT_ARR[j].charAt(0)) {
                return j;
            }
        }
        return 0;
    }

    /**
     * 获取单位金额
     * @param unitIndex 单位索引
     * @return
     */
    private static BigDecimal getUnit(int unitIndex) {
        String num = "0";
        switch (unitIndex) {
            // '厘', '分', '角', '圆', '拾', '佰', '仟', '万', '亿', '兆'
            case 0:
                num = "0.001";
                break;
            case 1:
                num = "0.01";
                break;
            case 2:
                num = "0.1";
                break;
            case 3:
                num = "1";
                break;
            case 4:
                num = "10";
                break;
            case 5:
                num = "100";
                break;
            case 6:
                num = "1000";
                break;
            case 7:
                num = "10000";
                break;
            case 8:
                num = "100000000";
                break;
            case 9:
                num = "1000000000000";
                break;
            default:
                break;
        }
        return new BigDecimal(num);
    }

    private static String REGEX_NUMBER_1 = "[-\\+]{0,1}0|^\\+{0,1}[1-9]\\d*|^-[1-9]\\d*";
    private static String REGEX_NUMBER_2 = "[-\\+]{0,1}\\d{1}\\.\\d+|[-\\+]{0,1}[1-9]{1}\\d*\\.\\d+";
    /**
     * 判断字符串是否不是数字
     * @author zql
     * @createTime 2020-12-05 16:31:34
     *
     * @param str
     * @return 不是数字返回true,是数字返回false
     */
    private static boolean isNotNumber(String str) {
        return !isNumber(str);
    }

    /**
     * 判断字符串是否是数字
     * @author zql
     * @createTime 2020-12-05 16:31:34
     *
     * @param str
     * <pre>
     * 小数点后都是0会判断成不是数字，例如：0.00返回的是false
     * </pre>
     * @return 是数字返回true,否则返回false
     */
    private static boolean isNumber(String str) {
        return isMatch(REGEX_NUMBER_1, str) || isMatch(REGEX_NUMBER_2, str);
    }

    /**
     * 是否和正则表达式匹配
     * @author zql
     * @createTime 2020-12-05 16:27:39
     *
     * @param regex 正则表达式
     * @param matchStr 要匹配的字符串
     * @return 匹配成功返回true，否则返回false;
     */
    private static boolean isMatch(String regex, String matchStr) {
        if (Objects.isNull(matchStr) || matchStr.trim().length() == 0) {
            return false;
        }
        Pattern pattern = Pattern.compile(regex);
        return pattern.matcher(matchStr).matches();
    }


    /**
     * 检查一个字符串是否为空、""或null。
     * <pre>
     * StringUtil.isBlank(null)      = true
     * StringUtil.isBlank("")        = true
     * StringUtil.isBlank(" ")       = true
     * StringUtil.isBlank("bob")     = false
     * StringUtil.isBlank("  bob  ") = false
     * </pre>
     *
     * @param str
     * @return
     */
    public static boolean isBlank(String str) {
        int strLen;
        if (Objects.isNull(str) || (strLen = str.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(str.charAt(i)) == false)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 检查一个字符串是否不为空、不为""或不为null。
     * <pre>
     * StringUtil.isNotBlank(null)      = false
     * StringUtil.isNotBlank("")        = false
     * StringUtil.isNotBlank(" ")       = false
     * StringUtil.isNotBlank("bob")     = true
     * StringUtil.isNotBlank("  bob  ") = true
     * </pre>
     *
     * @param str
     * @return
     */
    public static boolean isNotBlank(String str) {
        return !isBlank(str);
    }

}


