package com.ruoyi.app.flashdelivery.service;

import com.ruoyi.app.flashdelivery.dto.FlashDeliveryPriceBreakdown;
import com.ruoyi.system.domain.flash.FlashDeliveryPricing;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;

/** 按闪送时段运价计算整数新台币金额。 */
@Component
public class FlashDeliveryPricingCalculator {
    public Long calculate(FlashDeliveryPricing pricing, int distanceMeters) {
        return calculateBreakdown(pricing, distanceMeters, "NORMAL", 0L).getAmount();
    }

    public FlashDeliveryPriceBreakdown calculateBreakdown(FlashDeliveryPricing pricing, int distanceMeters) {
        return calculateBreakdown(pricing, distanceMeters, "NORMAL", 0L);
    }

    public FlashDeliveryPriceBreakdown calculateBreakdown(FlashDeliveryPricing pricing, int distanceMeters,
                                                           String deliveryType, Long tipAmount) {
        if (pricing == null || distanceMeters <= 0
                || pricing.getStartingDistance() == null || pricing.getStartingDistance().signum() <= 0
                || pricing.getStartingFare() == null || pricing.getStartingFare() <= 0
                || pricing.getDistance() == null || pricing.getDistance().signum() <= 0
                || pricing.getFreight() == null || pricing.getFreight() <= 0
                || pricing.getUrgentRate() == null || pricing.getUrgentRate().signum() < 0
                || pricing.getMinimumUrgentFee() == null || pricing.getMinimumUrgentFee() < 0
                || !("NORMAL".equals(deliveryType) || "URGENT".equals(deliveryType))
                || tipAmount == null || tipAmount < 0) {
            throw new IllegalArgumentException("invalid pricing or distance");
        }

        BigDecimal routeDistance = BigDecimal.valueOf(distanceMeters, 3);
        BigDecimal extraDistance = routeDistance.subtract(pricing.getStartingDistance()).max(BigDecimal.ZERO);
        BigDecimal chargeableDistance;
        if (extraDistance.compareTo(new BigDecimal("0.50")) < 0) {
            chargeableDistance = BigDecimal.ZERO;
        } else if (extraDistance.compareTo(BigDecimal.ONE) < 0) {
            chargeableDistance = BigDecimal.ONE;
        } else {
            chargeableDistance = extraDistance;
        }

        BigDecimal unitPrice = BigDecimal.valueOf(pricing.getFreight())
                .divide(pricing.getDistance(), 8, RoundingMode.HALF_UP);
        long distanceFee = chargeableDistance.multiply(unitPrice)
                .setScale(0, RoundingMode.HALF_UP)
                .longValueExact();

        FlashDeliveryPriceBreakdown result = new FlashDeliveryPriceBreakdown();
        result.setBillableDistance(chargeableDistance.setScale(2, RoundingMode.HALF_UP));
        result.setDistanceFee(distanceFee);
        long baseDeliveryFee = Math.addExact(pricing.getStartingFare(), distanceFee);
        long urgentFee = "URGENT".equals(deliveryType)
                ? Math.max(pricing.getMinimumUrgentFee(), BigDecimal.valueOf(baseDeliveryFee)
                .multiply(pricing.getUrgentRate())
                .divide(BigDecimal.valueOf(100), 0, RoundingMode.HALF_UP)
                .longValueExact())
                : 0L;
        result.setBaseDeliveryFee(baseDeliveryFee);
        result.setUrgentFee(urgentFee);
        result.setTipAmount(tipAmount);
        result.setAmount(Math.addExact(Math.addExact(baseDeliveryFee, urgentFee), tipAmount));
        return result;
    }
}
