package com.ruoyi.bst.suit.utils;

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

import com.ruoyi.bst.suit.domain.SuitVO;
import com.ruoyi.bst.suit.domain.enums.SuitRentalUnit;
import com.ruoyi.bst.suit.domain.enums.SuitRidingRule;
import com.ruoyi.bst.suit.domain.vo.SuitIntervalFeeRule;
import com.ruoyi.bst.suit.domain.vo.SuitStartFeeRule;
import com.ruoyi.common.utils.MathUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;

public class SuitUtil {

    /**
     * 计算总可用时长
     * @param suit 套餐
     * @return
     */
    public static long calcTotalTime(SuitVO suit) {
        if (suit == null) {
            return 0;
        }

        // 租赁单位
        SuitRentalUnit rentalUnit = SuitRentalUnit.parse(suit.getRentalUnit());
        if (rentalUnit == null) {
            return 0;
        }
        
        long seconds = 0;

        // 预存款
        BigDecimal depositAmount = suit.getDepositAmount();
        if (depositAmount == null || depositAmount.compareTo(BigDecimal.ZERO) <= 0) {
            seconds = 0;
        }

        // 起步价计费
        if (SuitRidingRule.START_FEE.getCode().equals(suit.getRidingRule())) {
            seconds = calcTotalTimeForStartFee(depositAmount, suit.getStartRule(), rentalUnit);
        }
        // 区间计费
        else if (SuitRidingRule.INTERVAL_FEE.getCode().equals(suit.getRidingRule())) {
            seconds = calcTotalTimeForIntervalFee(depositAmount, suit.getIntervalRule(), rentalUnit);
        }

        // 不允许时间低于免费时长
        if (suit.getFreeRideTime() != null) {
            seconds = Math.max(seconds, suit.getFreeRideTime() * 60);
        }

        return seconds;
    }

    /**
     * 计算起步价计费总可用时长
     * @param depositAmount 预存款
     * @param startRule 起步价计费规则
     * @param rentalUnit 租赁单位
     * @return 总可用时长（秒）
     */
    public static long calcTotalTimeForStartFee(BigDecimal depositAmount, SuitStartFeeRule startRule, SuitRentalUnit rentalUnit) {
        if (depositAmount == null || startRule == null || rentalUnit == null) {
            return 0;
        }

        BigDecimal startingPrice = startRule.getStartingPrice();
        BigDecimal timeoutPrice = startRule.getTimeoutPrice();

        // 获取单位对应的秒数
        long unitSeconds = rentalUnit.getSeconds();

        // 如果预存款小于起步价，按比例计算可用时长
        if (MathUtils.biggerThan(startingPrice, depositAmount)) {
            return depositAmount.multiply(BigDecimal.valueOf(startRule.getStartingTime() * unitSeconds))
                              .divide(startingPrice, 0, BigDecimal.ROUND_DOWN)
                              .longValue();
        }

        // 扣除起步价后的剩余金额
        BigDecimal remainingAmount = depositAmount.subtract(startingPrice);

        // 计算超时时长部分可以使用多少个完整周期
        BigDecimal additionalTimeoutPeriods = remainingAmount.divide(timeoutPrice, 0, BigDecimal.ROUND_DOWN);

        // 计算最后不足一个计费周期的时间（按比例计算）
        BigDecimal remainingAmountForLastPeriod = remainingAmount.remainder(timeoutPrice);
        BigDecimal lastPeriodTime = remainingAmountForLastPeriod
                .multiply(BigDecimal.valueOf(startRule.getTimeoutTime()))
                .divide(timeoutPrice, 0, BigDecimal.ROUND_DOWN);

        // 总时长 = 起步时长 + 完整超时周期时长 + 最后一个不完整周期时长
        return startRule.getStartingTime() * unitSeconds +
               additionalTimeoutPeriods.multiply(BigDecimal.valueOf(startRule.getTimeoutTime() * unitSeconds)).longValue() +
               lastPeriodTime.multiply(BigDecimal.valueOf(unitSeconds)).longValue();
    }

    /**
     * 计算区间计费总可用时长
     * @param depositAmount 预存款
     * @param intervalFeeRules 区间计费规则列表
     * @param rentalUnit 租赁单位
     * @return 总可用时长（秒）
     */
    public static long calcTotalTimeForIntervalFee(BigDecimal depositAmount, List<SuitIntervalFeeRule> intervalFeeRules, SuitRentalUnit rentalUnit) {
        if (depositAmount == null || CollectionUtils.isEmptyElement(intervalFeeRules) || rentalUnit == null) {
            return 0;
        }

        // 按照区间开始时间排序
        intervalFeeRules.sort(Comparator.comparingInt(SuitIntervalFeeRule::getStart));

        BigDecimal amount = depositAmount;  // 剩余金额
        int totalTime = 0; // 总时长

        // 获取单位对应的秒数
        long unitSeconds = rentalUnit.getSeconds();

        for (int i = 0; i < intervalFeeRules.size(); i++) {
            SuitIntervalFeeRule rule = intervalFeeRules.get(i);
            BigDecimal fee = rule.getFee(); // 每个时间段的金额
            int eachUnitTime = rule.getEachUnit();  // 每个时间段的时长
            if (fee.compareTo(BigDecimal.ZERO) <= 0 || eachUnitTime <= 0) {
                continue; // 跳过无效计费规则
            }
            // 计算可以购买几个区间，金额不足也按1个单位计算
            int count = amount.divide(fee, 0, RoundingMode.UP).intValue();

            // 非最后节点，限制最大购买数量
            if (i < intervalFeeRules.size() - 1) {
                int duration =rule.getEnd() - rule.getStart();
                // 计算最大金额
                int maxCount = BigDecimal.valueOf(duration).divide(BigDecimal.valueOf(eachUnitTime), 0, RoundingMode.UP).intValue();
                count = Math.min(count, maxCount);
            }

            // 记入时长，并扣减金额
            totalTime += count * eachUnitTime;
            amount = amount.subtract(fee.multiply(BigDecimal.valueOf(count)));

            // 金额不足则跳出循环
            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
        }

        return totalTime * unitSeconds;
    }

    /**
     * 计算起步价计费所需金额
     * @param seconds 已使用时长（秒）
     * @param rule 起步价计费规则
     * @param rentalUnit 租赁单位
     * @return 所需金额
     */
    public static BigDecimal calcAmountForStartFee(long seconds, SuitStartFeeRule rule, SuitRentalUnit rentalUnit) {
        if (seconds <= 0 || rule == null || rentalUnit == null) {
            return BigDecimal.ZERO;
        }

        // 获取单位对应的秒数
        long unitSeconds = rentalUnit.getSeconds();

        BigDecimal startingPrice = rule.getStartingPrice();    // 起步价（元）
        long startSeconds = rule.getStartingTime() * unitSeconds;  // 起步时长（秒）

        // 如果使用时长小于等于起步时长，返回起步价费用
        if (seconds <= startSeconds) {
            return rule.getStartingPrice();
        }

        // 超出起步的时长
        long extraTime = seconds - startSeconds;    // 超出的时长（秒）
        long outTime = rule.getTimeoutTime() * unitSeconds; // 超出时长周期（秒）
        long extraRange = extraTime / outTime + (extraTime % outTime > 0 ? 1 : 0);  // 超出的周期
        BigDecimal extraFee = MathUtils.mulDecimal(rule.getTimeoutPrice(), BigDecimal.valueOf(extraRange));

        // 总费用 = 起步价 + 超出费用
        return MathUtils.addDecimal(startingPrice, extraFee);
    }

    /**
     * 计算区间计费所需金额
     * @param seconds 已使用时长（秒）
     * @param rules 区间计费规则列表
     * @param rentalUnit 租赁单位
     * @return 所需金额
     */
    public static BigDecimal calcAmountForIntervalFee(long seconds, List<SuitIntervalFeeRule> rules, SuitRentalUnit rentalUnit) {
        if (seconds <= 0 || CollectionUtils.isEmptyElement(rules) || rentalUnit == null) {
            return BigDecimal.ZERO;
        }

        // 获取单位对应的秒数
        long unitSeconds = rentalUnit.getSeconds();

        // 按照区间开始时间排序
        rules.sort(Comparator.comparingInt(SuitIntervalFeeRule::getStart));

        // 总金额
        BigDecimal totalAmount = BigDecimal.ZERO;

        for (int i = 0; i < rules.size(); i++) {
            SuitIntervalFeeRule rule = rules.get(i);
            long end = seconds;
            if (i < rules.size() - 1) {
                end = Math.min(end, rule.getEnd() * unitSeconds);   // 取区间最小的终值
            }

            // 计算价格，并扣减剩余时长
            long duration = end - rule.getStart() * unitSeconds;  // 区间使用时长（秒）
            long eachUnit = rule.getEachUnit() * unitSeconds;  // 间隔（秒）
            long range = duration / eachUnit + (duration % eachUnit > 0 ? 1 : 0);   // 当前区间使用的周期，不满一个周期也按一个周期算
            BigDecimal partAmount = MathUtils.mulDecimal(rule.getFee(), BigDecimal.valueOf(range));
            totalAmount = MathUtils.addDecimal(totalAmount, partAmount);

            // 当前已使用时长大于等于最大时长，跳出循环
            if (end >= seconds) {
                break;
            }
        }

        return totalAmount;
    }
}
