package com.jeesharp.business.modules.mall.application.support;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 金额按权重分摊工具（最后一项吃尾差）。
 *
 * @author jeesharp
 * @since 2026-06-10
 */
public final class MallAmountAllocationSupport {

    private MallAmountAllocationSupport() {
    }

    /**
     * 按权重分摊总额，保证分摊之和等于 total（两位小数）。
     *
     * @param orderedKeys 顺序稳定的键列表
     * @param weights     权重（可为 0，全 0 时均分 0）
     * @param total       待分摊总额
     */
    public static Map<String, BigDecimal> allocate(List<String> orderedKeys, Map<String, BigDecimal> weights,
                                                   BigDecimal total) {
        Map<String, BigDecimal> result = new LinkedHashMap<>();
        if (orderedKeys == null || orderedKeys.isEmpty()) {
            return result;
        }
        BigDecimal safeTotal = total == null ? BigDecimal.ZERO : total.setScale(2, RoundingMode.HALF_UP);
        if (safeTotal.compareTo(BigDecimal.ZERO) <= 0) {
            for (String key : orderedKeys) {
                result.put(key, BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
            }
            return result;
        }
        BigDecimal weightSum = BigDecimal.ZERO;
        for (String key : orderedKeys) {
            BigDecimal w = weights.get(key);
            if (w != null && w.compareTo(BigDecimal.ZERO) > 0) {
                weightSum = weightSum.add(w);
            }
        }
        if (weightSum.compareTo(BigDecimal.ZERO) <= 0) {
            for (String key : orderedKeys) {
                result.put(key, BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
            }
            return result;
        }

        BigDecimal allocated = BigDecimal.ZERO;
        List<String> keys = new ArrayList<>(orderedKeys);
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            BigDecimal part;
            if (i == keys.size() - 1) {
                part = safeTotal.subtract(allocated).setScale(2, RoundingMode.HALF_UP);
            } else {
                BigDecimal w = weights.getOrDefault(key, BigDecimal.ZERO);
                if (w.compareTo(BigDecimal.ZERO) <= 0) {
                    part = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
                } else {
                    part = safeTotal.multiply(w).divide(weightSum, 2, RoundingMode.HALF_UP);
                }
                allocated = allocated.add(part);
            }
            if (part.compareTo(BigDecimal.ZERO) < 0) {
                part = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
            }
            result.put(key, part);
        }
        return result;
    }
}
