package com.ruoyi.iot.util;

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

import com.ruoyi.bst.bonus.domain.Bonus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.collection.CollectionUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 电池工具类
 * @author wjh
 * 2024/6/24
 */
@Slf4j
public class BatUtil {

    /**
     * 计算电量百分比
     * @param v 电压
     * @param maxV 满电电压
     * @param minV 亏电电压
     * @return 电压百分比
     */
    public static BigDecimal calcPowerPoint(BigDecimal v, BigDecimal maxV, BigDecimal minV) {
        if (v == null || maxV == null || minV == null) {
            return null;
        }
        BigDecimal decimal100 = new BigDecimal("100");

        // 计算电量百分比
        // point = (100 - ((maxV - v) * 100 / (maxV - minV)) )
        BigDecimal point = decimal100.subtract(
                    maxV.subtract(v).multiply(decimal100).divide(
                        maxV.subtract(minV),
                        2,
                        RoundingMode.HALF_UP
                    )
        );

        // 若小于0，则返回0
        if (point.compareTo(BigDecimal.ZERO) < 0) {
            return BigDecimal.ZERO;
        }
        // 若大于100，则返回100
        else if (point.compareTo(decimal100) > 0) {
            return decimal100;
        }
        // 否则，返回本身
        else {
            return point;
        }
    }


    /**
     * 计算电量电压
     * @param point 电量
     * @param maxV 满电电压
     * @param minV 亏电电压
     * @return 电压百分比
     */
    public static BigDecimal calcV(BigDecimal point, BigDecimal maxV, BigDecimal minV) {
        if (point == null || maxV == null || minV == null) {
            return null;
        }
        BigDecimal decimal100 = new BigDecimal("100");

        // 计算电量电压
        // v = maxV - ((1 - point / 100)) * (maxV - minV))
        BigDecimal v = maxV.subtract(
                BigDecimal.ONE.subtract(
                        point.divide(decimal100, 2, RoundingMode.HALF_UP)
                ).multiply(
                        maxV.subtract(minV)
                )
        );

        // 若小于0，则返回0
        if (v.compareTo(BigDecimal.ZERO) < 0) {
            return BigDecimal.ZERO;
        }
        // 否则，返回本身
        else {
            return v;
        }
    }

    /**
     * 分成金额
     * @param result 分成列表
     * @param amount 总金额
     */
    public static void partAmount(List<Bonus> bonusList, BigDecimal totalAmount) {
        if (totalAmount == null || totalAmount.compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("分成总金额不允许小于0");
        }
    
        BigDecimal decimal100 = new BigDecimal("100");
        // 循环遍历，构造分成金额
        for (Bonus bonus : bonusList) {
            if (bonus != null && bonus.getPoint() != null) {
                BigDecimal amount = totalAmount.multiply(bonus.getPoint()).divide(decimal100, 2, RoundingMode.HALF_UP);
                setAmount(bonus, amount);
            }
        }

        // 处理误差
        BigDecimal dividedAmount = CollectionUtils.sumDecimal(bonusList, Bonus::getAmount);
        handlePartDiff(bonusList, totalAmount.subtract(dividedAmount));

        // 处理误差后的分配金额
        dividedAmount = CollectionUtils.sumDecimal(bonusList, Bonus::getAmount);
        ServiceUtil.assertion(dividedAmount.compareTo(totalAmount) != 0, "分成金额分配出错");

    }

    
    /**
     * 处理分成的误差
     */
    public static void handlePartDiff(List<? extends Bonus> bonusList, BigDecimal diff) {
        // 若误差金额为0，则不处理
        if (diff == null || diff.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }

        // 若误差金额为正数，则交给第一个分成方处理
        if (diff.compareTo(BigDecimal.ZERO) > 0) {
            Bonus bonus = bonusList.get(0);
            setAmount(bonus, bonus.getAmount().add(diff));
        }
        // 否则，若为负数，则多个分成方处理
        else {
            // 多个分成方共同承担误差
            BigDecimal remainingDiff = diff;
            for (Bonus bonus : bonusList) {
                if (bonus == null || bonus.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                    continue;
                }

                // 计算当前分成方可以承担的误差金额（确保不会导致金额为负）
                BigDecimal maxDeductible = bonus.getAmount();  // 最大可扣除金额
                BigDecimal adjustAmount = maxDeductible.min(remainingDiff.abs());

                // 直接调整金额
                setAmount(bonus, bonus.getAmount().subtract(adjustAmount));
                remainingDiff = remainingDiff.add(adjustAmount);

                // 如果误差已经分配完毕，则退出
                if (remainingDiff.compareTo(BigDecimal.ZERO) == 0) {
                    return;
                }
            }

            // 如果所有分成方都处理完了还有剩余误差，抛出异常
            if (remainingDiff.compareTo(BigDecimal.ZERO) != 0) {
                throw new ServiceException("分成金额误差处理失败：无法完全分配误差金额，剩余误差：" + remainingDiff);
            }
        }
    }
    
    private static void setAmount(Bonus bonus, BigDecimal amount) {
        ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "分成金额不允许小于0");
        bonus.setAmount(amount);
    }
}
