package com.ruoyi.bst.suit.utils;

import java.math.BigDecimal;
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;
        }

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

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

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

    /**
     * 计算起步价计费总可用时长
     * @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((a, b) -> Integer.compare(a.getStart(), b.getStart()));

        BigDecimal remainingAmount = depositAmount;
        long totalTime = 0;

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

        for (SuitIntervalFeeRule rule : intervalFeeRules) {
            // 处理最后一个区间的end可能为空的情况
            if ( intervalFeeRules.indexOf(rule) == intervalFeeRules.size() - 1) {
                // 如果是最后一个区间且end为0，可以认为是无限长
                // 这里我们可以计算剩余金额能买多少时间
                BigDecimal eachUnitFee = rule.getFee();
                int eachUnitTime = rule.getEachUnit();

                if (eachUnitFee.compareTo(BigDecimal.ZERO) > 0 && eachUnitTime > 0) {
                    // 计算剩余金额可以购买的完整单位数
                    BigDecimal completeUnits = remainingAmount.divide(eachUnitFee, 0, BigDecimal.ROUND_DOWN);
                    totalTime += completeUnits.multiply(BigDecimal.valueOf(eachUnitTime * unitSeconds)).longValue();

                    // 计算最后不足一个计费单位的时间（按比例计算）
                    BigDecimal remainingAmountForLastUnit = remainingAmount.remainder(eachUnitFee);
                    if (remainingAmountForLastUnit.compareTo(BigDecimal.ZERO) > 0) {
                        totalTime += remainingAmountForLastUnit
                                .multiply(BigDecimal.valueOf(eachUnitTime * unitSeconds))
                                .divide(eachUnitFee, 0, BigDecimal.ROUND_DOWN)
                                .longValue();
                    }
                }

                break; // 处理完最后一个区间后退出循环
            }

            // 计算当前区间的时间范围（单位）
            int end = rule.getEnd();
            int intervalDuration = end - rule.getStart();
            if (intervalDuration <= 0) {
                continue; // 跳过无效区间
            }

            // 计算当前区间内可以购买的完整计费单位数量
            BigDecimal eachUnitFee = rule.getFee();
            int eachUnitTime = rule.getEachUnit();

            if (eachUnitFee.compareTo(BigDecimal.ZERO) <= 0 || eachUnitTime <= 0) {
                continue; // 跳过无效计费规则
            }

            // 计算当前区间内可以购买的完整单位数
            int maxUnitsInInterval = intervalDuration / eachUnitTime;
            BigDecimal maxCostForInterval = eachUnitFee.multiply(BigDecimal.valueOf(maxUnitsInInterval));

            // 如果剩余金额足够支付整个区间
            if (remainingAmount.compareTo(maxCostForInterval) >= 0) {
                totalTime += intervalDuration * unitSeconds;
                remainingAmount = remainingAmount.subtract(maxCostForInterval);
            } else {
                // 计算剩余金额可以购买的完整单位数
                BigDecimal completeUnits = remainingAmount.divide(eachUnitFee, 0, BigDecimal.ROUND_DOWN);
                totalTime += completeUnits.multiply(BigDecimal.valueOf(eachUnitTime * unitSeconds)).longValue();

                // 计算最后不足一个计费单位的时间（按比例计算）
                BigDecimal remainingAmountForLastUnit = remainingAmount.remainder(eachUnitFee);
                if (remainingAmountForLastUnit.compareTo(BigDecimal.ZERO) > 0) {
                    totalTime += remainingAmountForLastUnit
                            .multiply(BigDecimal.valueOf(eachUnitTime * unitSeconds))
                            .divide(eachUnitFee, 0, BigDecimal.ROUND_DOWN)
                            .longValue();
                }

                // 已经用完所有金额，退出循环
                break;
            }
        }

        return totalTime;
    }

    /**
     * 计算已使用时长所需的金额
     * @param suit 套餐
     * @param usedTime 已使用时长（秒）
     * @return 所需金额
     */
    public static BigDecimal calcAmount(SuitVO suit, long usedTime) {
        if (suit == null || usedTime <= 0) {
            return BigDecimal.ZERO;
        }

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

        // 起步价计费
        if (SuitRidingRule.START_FEE.getCode().equals(suit.getRidingRule())) {
            return calcAmountForStartFee(usedTime, suit.getStartRule(), rentalUnit);
        }
        // 区间计费
        else if (SuitRidingRule.INTERVAL_FEE.getCode().equals(suit.getRidingRule())) {
            return calcAmountForIntervalFee(usedTime, suit.getIntervalRule(), rentalUnit);
        }
        return BigDecimal.ZERO;
    }

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

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

        // 将秒转换为计费单位
        BigDecimal usedTimeInUnit = BigDecimal.valueOf(usedTime).divide(BigDecimal.valueOf(unitSeconds), 0, BigDecimal.ROUND_UP);

        // 起步价和起步时长
        BigDecimal startingPrice = startRule.getStartingPrice();
        int startingTime = startRule.getStartingTime();

        // 如果使用时长小于等于起步时长，按比例计算费用
        if (usedTimeInUnit.intValue() <= startingTime) {
            return startingPrice.multiply(usedTimeInUnit).divide(BigDecimal.valueOf(startingTime), 2, BigDecimal.ROUND_UP);
        }

        // 超出起步时长的部分
        BigDecimal extraTime = usedTimeInUnit.subtract(BigDecimal.valueOf(startingTime));

        // 超时费用和超时时长
        BigDecimal timeoutPrice = startRule.getTimeoutPrice();
        int timeoutTime = startRule.getTimeoutTime();

        // 计算完整的超时周期数
        BigDecimal fullTimeoutPeriods = extraTime.divide(BigDecimal.valueOf(timeoutTime), 0, BigDecimal.ROUND_DOWN);

        // 计算最后不足一个超时周期的部分
        BigDecimal remainingTime = extraTime.remainder(BigDecimal.valueOf(timeoutTime));
        BigDecimal lastPeriodFee = BigDecimal.ZERO;
        if (remainingTime.compareTo(BigDecimal.ZERO) > 0) {
            lastPeriodFee = timeoutPrice.multiply(remainingTime).divide(BigDecimal.valueOf(timeoutTime), 2, BigDecimal.ROUND_UP);
        }

        // 总费用 = 起步价 + 完整超时周期费用 + 最后一个不完整周期费用
        return startingPrice
                .add(fullTimeoutPeriods.multiply(timeoutPrice))
                .add(lastPeriodFee);
    }

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

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

        // 将秒转换为计费单位
        int usedTimeInUnit = (int) Math.ceil((double) usedTime / unitSeconds);

        // 按照区间开始时间排序
        intervalFeeRules.sort((a, b) -> Integer.compare(a.getStart(), b.getStart()));

        BigDecimal totalAmount = BigDecimal.ZERO;
        int remainingTime = usedTimeInUnit;

        for (SuitIntervalFeeRule rule : intervalFeeRules) {
            // 如果没有剩余时间需要计费，退出循环
            if (remainingTime <= 0) {
                break;
            }

            // 处理最后一个区间的end可能为0的情况
            boolean isLastInterval = intervalFeeRules.indexOf(rule) == intervalFeeRules.size() - 1;
            if (isLastInterval) {
                // 最后一个区间且end为0，表示无限长
                BigDecimal eachUnitFee = rule.getFee();
                int eachUnitTime = rule.getEachUnit();

                if (eachUnitFee.compareTo(BigDecimal.ZERO) > 0 && eachUnitTime > 0) {
                    // 计算完整单位数
                    int completeUnits = remainingTime / eachUnitTime;
                    totalAmount = totalAmount.add(eachUnitFee.multiply(BigDecimal.valueOf(completeUnits)));

                    // 计算最后不足一个单位的费用
                    int remainingUnitTime = remainingTime % eachUnitTime;
                    if (remainingUnitTime > 0) {
                        totalAmount = totalAmount.add(
                                eachUnitFee.multiply(BigDecimal.valueOf(remainingUnitTime))
                                        .divide(BigDecimal.valueOf(eachUnitTime), 2, BigDecimal.ROUND_UP)
                        );
                    }
                }

                remainingTime = 0;
                break;
            }

            // 计算当前区间的时间范围
            int end = rule.getEnd();
            int intervalDuration = end - rule.getStart();
            if (intervalDuration <= 0) {
                continue; // 跳过无效区间
            }

            // 计算在当前区间内的使用时长
            int timeInCurrentInterval = Math.min(remainingTime, intervalDuration);

            // 计算当前区间内的费用
            BigDecimal eachUnitFee = rule.getFee();
            int eachUnitTime = rule.getEachUnit();

            if (eachUnitFee.compareTo(BigDecimal.ZERO) <= 0 || eachUnitTime <= 0) {
                continue; // 跳过无效计费规则
            }

            // 计算完整单位数
            int completeUnits = timeInCurrentInterval / eachUnitTime;
            totalAmount = totalAmount.add(eachUnitFee.multiply(BigDecimal.valueOf(completeUnits)));

            // 计算最后不足一个单位的费用
            int remainingUnitTime = timeInCurrentInterval % eachUnitTime;
            if (remainingUnitTime > 0) {
                totalAmount = totalAmount.add(
                        eachUnitFee.multiply(BigDecimal.valueOf(remainingUnitTime))
                                .divide(BigDecimal.valueOf(eachUnitTime), 2, BigDecimal.ROUND_UP)
                );
            }

            // 更新剩余需要计费的时间
            remainingTime -= timeInCurrentInterval;
        }

        return totalAmount;
    }
}
