package com.mvc.forwarmana;

import com.mvc.constant.BaseConstant;
import com.mvc.constant.ReviewResultType;
import com.mvc.entity.TAiread;
import com.mvc.entity.TaskReview;
import com.mvc.model.TAireadModel;
import com.mvc.model.forwarding.DicForwarderModel;
import com.mvc.model.forwarding.TForwarderStationModel;
import com.mvc.service.aiiden.TaskReviewService;
import com.mvc.service.copy.TAireadService;
import com.mvc.service.forwarding.DicForwarderService;
import com.mvc.service.forwarding.TForwarderStationService;
import com.mvc.service.iotcard.IotCardService;
import com.mvc.utils.DateUtil;
import com.mvc.utils.NotifyUtil;
import com.mvc.utils.NumberUtil;
import com.mvc.utils.StoreBlockingQueue;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class ForwarderThread implements Runnable {
    private Logger log = LoggerFactory.getLogger(ForwarderThread.class);

    //private static List<DicForwarderModel> mDicForwarderModels;

    public static Map<Integer, DicForwarderModel> mDicForwarderModelMap;

    @Resource
    private NotifyUtil notifyUtil;

    @Resource
    private DicForwarderService dicForwarderService;

    @Resource
    private TForwarderStationService forwarderStationService;

    @Resource
    private HdForwarder hdForwarder;

    @Resource
    private CYForwarder cyForwarder;

    @Resource
    private CPForwarder cpForwarder;

    @Resource
    private FTForwarder ftForwarder;

    @Resource
    private IotCardService iotCardService;

    @Resource
    private WbtyForwarder wbtyForwarder;

    @Resource
    private CpszyForwarder cpszyForwarder;

    @Resource
    private TAireadService aireadService;

    @Resource
    private TaskReviewService taskReviewService;

    //需要实时转发的服务器id
    private final List<Integer> REALTIME_FORWARDER_ID = new ArrayList<>(List.of(2));
    private static Map<String, TForwarderStationModel> mRealtimeStationMap = null;
    private final int CY_FORWARDER_ID = 2;//朝阳平台转发服务器ID

    //虚拟化处理的数据源开始日期
    private Date mCYVirtualStartDate = DateUtil.parseTimeStr("20240812000000", DateUtil.PATTERN_DATA_UNSIGNED);
    private static Map<String, List<CYVirtualData>> mCYVirtualDataUtil;//朝阳24小时数据池<StationCode,List<CYVirtualData>>

    //@Resource
    //private AlarmNoticeMana alarmNoticeMana;

    @Resource
    private ZHYServerThermalMonitor zhyServerThermalMonitor;

    @Override
    public void run() {
        List<DicForwarderModel> dicForwarderModels = dicForwarderService.findAllDicForwarder();
        mDicForwarderModelMap = dicForwarderModels.stream().collect(java.util.stream.Collectors.toMap(DicForwarderModel::getId, v -> v));
        while (true) {
            try {
                TAireadModel aireadModel = StoreBlockingQueue.forwarderQueue.take();
                //使用深复制对象,防止后续操作影响入库的数据 2024-01-03 guozhao
                TAireadModel copyModel = SerializationUtils.clone(aireadModel);

                //处理自来水公司的邮件提醒
                notifyUtil.sendEMail(copyModel);

                //获取物联网卡信息 2024-02-21 guozhao
                iotCardService.saveCardInfoByAiReadModel(copyModel);

                //传入智慧院机房温度监测模块 2025-03-25 guozhao
                zhyServerThermalMonitor.handleData(copyModel);

                //部分水务局要求多条 2024-08-06 guozhao
                //改为补充数据,每4小时一发 2024-08-19 guozhao
                ////realtimeForwarding(copyModel);

                Thread.sleep(20);
            } catch (Exception e) {
                //e.printStackTrace();
                //log.error("ForwarderThread", e);
                continue;
            }
        }
    }

    /**
     * 处理需要转发的数据
     */
    public void dealForwardData() {
        List<TForwarderStationModel> forwarderStationModels = forwarderStationService.findAllTForwarderStation(new TForwarderStationModel());

        //使用审核数据进行转发
        Date yesterday = DateUtil.getOtherDateByCurrentDate(new Date(), -1);
        //Date yesterday = DateUtil.getOtherDateByCurrentDate(new Date(), -1);
        Map<String, TaskReview> reviewMap = taskReviewService.findTaskReviewMapByDate(yesterday);

        Date sampleTime = DateUtil.getZeroTimeOfDay(new Date());//采样时间是转发时的当日0点
        Date rptTime = DateUtil.getOtherDateByMinute(sampleTime, 1);//发报时间推后1分钟

        for (TForwarderStationModel model : forwarderStationModels) {
            if (!model.getUseFlag().equals(BaseConstant.VALID_FLAG)) {//未启用
                continue;
            }

            if (!reviewMap.containsKey(model.getStationCode())) {//审核记录中没有
                continue;
            }
            TaskReview taskReview = reviewMap.get(model.getStationCode());
            if (!taskReview.getFinishedFlag().equals(ReviewResultType.IDENT_TRUE.getCode()) &&
                    !taskReview.getFinishedFlag().equals(ReviewResultType.REVIEW_REVISE.getCode())) {//未审核或无法辨别
                continue;
            }

            sendForwardData(model, taskReview, sampleTime, rptTime);
        }
    }

    /**
     * 发送需要转发的数据
     *
     * @param forwarderStationModel
     * @param taskReview
     * @param sampleTime
     * @param rptTime
     */
    private void sendForwardData(TForwarderStationModel forwarderStationModel, TaskReview taskReview, Date sampleTime, Date rptTime) {
        if (!mDicForwarderModelMap.containsKey(forwarderStationModel.getForwarderId())) {//没有匹配到服务器列表
            return;
        }
        DicForwarderModel dicForwarderModel = mDicForwarderModelMap.get(forwarderStationModel.getForwarderId());

        //单独处理此类数据 2024-08-06 guozhao
        if (REALTIME_FORWARDER_ID.contains(forwarderStationModel.getForwarderId())) {
            return;
        }

        TAireadModel aireadModel = aireadService.getTaireadByAireadId(taskReview.getAireadId());
        if (aireadModel == null) {
            return;
        }
        if (DateUtil.daysBetween(aireadModel.getSampleTime(), sampleTime) > 30) {
            log.warn("{}:{}超30天未收到数据,不转发", dicForwarderModel.getServerName(), aireadModel.getStationCode());
            return;
        }

        //改造读数记录
        aireadModel.setSampleTime(sampleTime);
        aireadModel.setRptTime(rptTime);
        aireadModel.setRawValue(taskReview.getReviewValue());
        aireadModel.setIdentValue(taskReview.getReviewValue());
        aireadModel.setResultValue(taskReview.getReviewValue());//正常情况应该已经相等了

        String rawMsg = null;

        if (StringUtils.isNotEmpty(forwarderStationModel.getTargetStationCode())) {//目标平台站码不一致,用目标的站码
            aireadModel.setStationCode(forwarderStationModel.getTargetStationCode());//在此修改,不再需要后面每一个类都判断
        }
        log.warn("{}:{}", dicForwarderModel.getServerName(), aireadModel.getStationCode());
        switch (forwarderStationModel.getForwarderId()) {
            case 1://海淀水务局
                rawMsg = hdForwarder.generateMessage(aireadModel, dicForwarderModel);
                break;
            case 2://朝阳水务局
                rawMsg = cyForwarder.generateMessage(aireadModel, dicForwarderModel);
                break;
            case 3://昌平水务局
                rawMsg = cpForwarder.generateMessage(aireadModel, dicForwarderModel);
                break;
            case 4://丰台水务局
                ftForwarder.reportdata(dicForwarderModel, aireadModel);
                break;
            case 5://昌平水资源科
                cpszyForwarder.insertDayData(dicForwarderModel, aireadModel);
                break;
            case 21://万邦拓源
                wbtyForwarder.commitData(dicForwarderModel, aireadModel);
                break;
            default:
                break;
        }

        //TCP或UPD时才需要通过生成报文的方式 2024-06-12 guozhao
        if (dicForwarderModel.getTransportProtocol() < 3) {
            if (StringUtils.isNotEmpty(rawMsg)) {
                SendMessage(dicForwarderModel, rawMsg);
            } else {
                log.warn("生成转发数据失败:" + aireadModel.getStationCode() + "readid:" + aireadModel.getId());
            }
        }
    }

    //发送消息
    private void SendMessage(DicForwarderModel forwarderModel, String rawMsg) {
        if (StringUtils.isEmpty(forwarderModel.getServerIp()) || forwarderModel.getServerPort() == null) return;
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new ReadTimeoutHandler(3, TimeUnit.SECONDS)); // 5秒的读取超时
                            pipeline.addLast(new ClientHandler());
                        }
                    });
            Channel channel = bootstrap.connect(forwarderModel.getServerIp(), forwarderModel.getServerPort()).sync().channel();
            // 向服务器发送数据(加了一些延迟)
            Thread.sleep(50);//防止发送过快
            log.warn("ForwarderThread:" + forwarderModel.getServerIp() + ":" + forwarderModel.getServerPort() + ":" + rawMsg);
            byte[] bytes = NumberUtil.hexStringToBytes(rawMsg);
            channel.writeAndFlush(Unpooled.wrappedBuffer(bytes));
            channel.closeFuture().sync();
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error(ex.getMessage());
        } finally {
            group.shutdownGracefully();
        }
    }

    //消息回执
    private class ClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
            // 处理来自服务器的字节数组消息
            byte[] receivedBytes = new byte[msg.readableBytes()];
            msg.readBytes(receivedBytes);
            String message = NumberUtil.bytesToHexStr(receivedBytes);
            log.warn("ForwarderThread:收到回执:" + message);
        }

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof ReadTimeoutException) {
                //读取超时，关闭连接
                log.warn("ForwarderThread:等待回执超时,通道关闭:" + ctx.channel().localAddress());
                ctx.close();
            }
        }
    }

    /**
     * 实时数据转发
     *
     * @param aireadModel
     */
    private void realtimeForwarding(TAireadModel aireadModel) {
        try {
            Map<String, TForwarderStationModel> stationMap = getRealtimeStationMap();
            if (!stationMap.containsKey(aireadModel.getStationCode())) {
                return;
            }

            TForwarderStationModel forwarderStationModel = stationMap.get(aireadModel.getStationCode());
            if (aireadModel.getResultValue() == null) {
                return;
            }
            if (!mDicForwarderModelMap.containsKey(forwarderStationModel.getForwarderId())) {
                return;
            }

            DicForwarderModel dicForwarderModel = mDicForwarderModelMap.get(forwarderStationModel.getForwarderId());

            if (StringUtils.isNotEmpty(forwarderStationModel.getTargetStationCode())) {
                aireadModel.setStationCode(forwarderStationModel.getTargetStationCode());
            }

            String rawMsg = null;

            log.warn("实时转发{}:{}", dicForwarderModel.getServerName(), aireadModel.getStationCode());
            switch (forwarderStationModel.getForwarderId()) {
                case 2://朝阳水务局
                    rawMsg = cyForwarder.generateMessage(aireadModel, dicForwarderModel);
                    break;
                default:
                    break;
            }

            //TCP或UPD时才需要通过生成报文的方式 2024-06-12 guozhao
            if (dicForwarderModel.getTransportProtocol() < 3) {
                if (StringUtils.isNotEmpty(rawMsg)) {
                    SendMessage(dicForwarderModel, rawMsg);
                    //log.info("模拟发送："+rawMsg);
                } else {
                    log.warn("生成转发数据失败:" + aireadModel.getStationCode() + "readid:" + aireadModel.getId());
                }
            }
        } catch (Exception ex) {
            log.error("实时转发失败:", ex);
        }
    }

    /**
     * 获取需要实时转发的站点集合
     *
     * @return
     */
    private Map<String, TForwarderStationModel> getRealtimeStationMap() {
        if (mRealtimeStationMap != null) {
            return mRealtimeStationMap;
        }

        Map<String, TForwarderStationModel> map = new HashMap<>();
        TForwarderStationModel queryModel = new TForwarderStationModel();
        for (int id : REALTIME_FORWARDER_ID) {
            queryModel.setForwarderId(id);
            List<TForwarderStationModel> list = forwarderStationService.findAllTForwarderStation(queryModel);
            if (!list.isEmpty()) {
                map.putAll(list.stream()
                        .collect(Collectors.toMap(
                                TForwarderStationModel::getStationCode, // 键映射函数
                                v -> v, // 值映射函数
                                (existing, replacement) -> existing // 合并函数：保留第一个值
                        )));
            }
        }
        mRealtimeStationMap = map;
        return mRealtimeStationMap;
    }

    public void dealCyForwardData() {
        if (!mDicForwarderModelMap.containsKey(CY_FORWARDER_ID)) {
            return;
        }
        Date now = new Date();

        TForwarderStationModel queryModel = new TForwarderStationModel();
        queryModel.setForwarderId(CY_FORWARDER_ID);
        queryModel.setUseFlag(BaseConstant.VALID_FLAG);
        List<TForwarderStationModel> forwarderStationModels = forwarderStationService.findAllTForwarderStation(queryModel);

        //1.生成日数据池
        if (mCYVirtualDataUtil == null) {
            generateCYVirtualDataMap(forwarderStationModels);
        }
        int hour = DateUtil.getHours(now);

        Date endTime = DateUtil.getZeroTimeOfDay(now);
        Date startTime = DateUtil.getOtherDateByCurrentDate(endTime, -1);

        //0点较为特殊,应该使用前天21,22,23和昨天0点的4条数据
        if (hour == 0) {
            endTime = DateUtil.getOtherDateByCurrentDate(endTime, -1);
            startTime = DateUtil.getOtherDateByCurrentDate(endTime, -1);
        }

        DayOfWeek dayOfWeek = DateUtil.getDayOfWeek(startTime);

        log.info("朝阳数据补充开始[{}]-[{}]", startTime, endTime);

        //2.遍历站点列表
        for (TForwarderStationModel model : forwarderStationModels) {
            log.info("朝阳数据补充[{}]", model.getStationCode());
            TAiread airead0;
            TAiread airead1;

            List<TAiread> aireads = aireadService.findAllTaireadByStationCodeAndTime(model.getStationCode(), startTime, endTime);

            if (aireads.size() < 2) {
                log.info("朝阳数据补充[{}]:数据少于2条", model.getStationCode());

                //**正常情况1天1条数据,如果丢失一条会导致连续2天无法补充数据,因此判断此表是否7天都未走过字,是则直接使用历史值进行补充
                Date startTime2 = DateUtil.getOtherDateByCurrentDate(startTime, -7);
                aireads = aireadService.findAllTaireadByStationCodeAndTime(model.getStationCode(), startTime2, endTime);

                if(aireads.size()<2){//历史数据也少于2条 2024-10-12 guozhao
                    continue;
                }

                airead0 = aireads.get(0);
                airead1 = aireads.get(aireads.size() - 1);

                if (aireads.size() < 2
                        || DateUtil.daysBetween(airead0.getSampleTime(), airead1.getSampleTime()) < 7
                        || airead0.getResultValue() == null
                        || airead1.getResultValue() == null
                        || airead0.getResultValue().compareTo(airead1.getResultValue()) != 0
                ) {
                    continue;
                }else {
                    log.info("朝阳数据补充[{}]:数据少于2条,但7天未走字,使用前值", model.getStationCode());
                }
            }

            airead0 = aireads.get(0);
            airead1 = aireads.get(aireads.size() - 1);

            if(StringUtils.isEmpty(airead0.getReviewResultFlag())||ReviewResultType.UNRECOGNIZABLE.getCode().equals(airead0.getReviewResultFlag())){
                log.info("朝阳数据补充[{}]:第一条数据未审核或无法辨别,跳过", model.getStationCode());
                continue;
            }
            if(StringUtils.isEmpty(airead1.getReviewResultFlag())||ReviewResultType.UNRECOGNIZABLE.getCode().equals(airead1.getReviewResultFlag())){
                log.info("朝阳数据补充[{}]:当日数据未审核或无法辨别,跳过", model.getStationCode());
                continue;
            }

            //两条数据时间间隔小于12小时，跳过
            if (DateUtil.getMinsbetweenTime(airead0.getSampleTime(), airead1.getSampleTime()) < 12 * 60) {
                log.info("朝阳数据补充[{}]:两条数据时间间隔小于12小时", model.getStationCode());
                continue;
            }

            //有读数无效或用量为负数，跳过
            if (airead0.getResultValue() == null || airead1.getResultValue() == null ||
                    airead1.getResultValue().subtract(airead0.getResultValue()).compareTo(BigDecimal.ZERO) < 0) {
                log.info("朝阳数据补充[{}]:数据无效或用量为负数", model.getStationCode());
                continue;
            }

            //下面这个判断条件有些严格,暂时注释
                /*if(airead0.getSampleTime().compareTo(startTime)!=0||airead1.getSampleTime().compareTo(endTime)!=0){
                    continue;
                }*/

            CYVirtualData cyVirtualData = null;

            List<CYVirtualData> cyVirtualDataList = null;
            if (mCYVirtualDataUtil.containsKey(model.getStationCode())) {
                cyVirtualDataList = mCYVirtualDataUtil.get(model.getStationCode());
            } else {//如果没有此站点的用水数据,取第一个
                cyVirtualDataList = mCYVirtualDataUtil.entrySet().iterator().next().getValue();
            }

            Optional<CYVirtualData> optional = cyVirtualDataList.stream().filter(s -> s.getDayOfWeek().equals(dayOfWeek)).findFirst();
            cyVirtualData = optional.isPresent() ? optional.get() : cyVirtualDataList.get(0);

            //日用量
            BigDecimal dayUsage = airead1.getResultValue().subtract(airead0.getResultValue());

            TAireadModel tempModel = new TAireadModel();
            BeanUtils.copyProperties(airead0, tempModel);

            hour = hour == 0 ? 24 : hour;

            //每次都计算24次,比较简单
            for (int i = 1; i < 24; i++) {
                //通过时用量占比计算出每小时用量并累计
                BigDecimal hourUsage = dayUsage.multiply(cyVirtualData.getHourlyUsageRatio().get(i - 1));
                //防止因为计算中多出的小数点造成表的尾数单位变化//不能一直抹小数点,一般用量很小,每次都抹掉,损失太多
                //hourUsage = hourUsage.setScale(0, BigDecimal.ROUND_DOWN);

                tempModel.setResultValue(tempModel.getResultValue().add(hourUsage));
                tempModel.setSampleTime(DateUtil.getOtherDateByHour(endTime, i));

                //符合本次发送时间段
                //(从4小时发送一次改为12小时,不然可能会用上未审核的数据) 2025-02-27 guozhao
                if (i > hour - 12 && i <= hour) {
                    tempModel.setRptTime(now);
                    TAireadModel forwarderData = tempModel;
                    int tempDecimalDigits = tempModel.getDecimalDigits() == null ? 0 : tempModel.getDecimalDigits();
                    forwarderData.setResultValue(forwarderData.getResultValue().setScale(tempDecimalDigits, BigDecimal.ROUND_DOWN));
                    realtimeForwarding(tempModel);
                    log.info("朝阳数据补充[{}]:{} | {}", model.getStationCode(), tempModel.getSampleTime(), tempModel.getResultValue().setScale(tempModel.getDecimalDigits(), BigDecimal.ROUND_DOWN));
                } else if (i > hour) {
                    break;
                }
            }
            //0点直接使用实际数据
            if (hour == 24) {
                BeanUtils.copyProperties(airead1, tempModel);
                tempModel.setSampleTime(DateUtil.getZeroTimeOfDay(now));
                tempModel.setRptTime(now);
                realtimeForwarding(tempModel);
                log.info("朝阳数据补充[{}]:{} | {}", model.getStationCode(), tempModel.getSampleTime(), tempModel.getResultValue());
            }

        }
    }

    /**
     * 生成朝阳虚拟数据池
     *
     * @param list
     */
    private void generateCYVirtualDataMap(List<TForwarderStationModel> list) {
        Date endTime = DateUtil.getOtherDateByCurrentDate(mCYVirtualStartDate, 14);

        Map<String, List<CYVirtualData>> cyVirtualDataMap = new HashMap<>();

        for (TForwarderStationModel model : list) {
            List<TAiread> aireads = aireadService.findAllTaireadByStationCodeAndTime(model.getStationCode(), mCYVirtualStartDate, endTime);

            //转为map方便根据时间获取数据
            Map<Date, TAiread> aireadMap = aireads.stream()
                    .collect(Collectors.toMap(
                            TAiread::getSampleTime, // Key extractor
                            tairead -> tairead, // Value mapper
                            (existingValue, newValue) -> existingValue // Merge function
                    ));

            List<CYVirtualData> cyVirtualDataList = new ArrayList<>();
            List<BigDecimal> hourlyUsageList = new ArrayList<>();

            TAiread airead0;
            TAiread airead1;

            CYVirtualData data = new CYVirtualData();
            Date curDate = mCYVirtualStartDate;
            Date nextDate;//下一个小时

            while (curDate.compareTo(endTime) <= 0) {
                //循环第一次或日期发生变化
                if (data.getLocalDate() == null || !data.getLocalDate().equals(DateUtil.dateToLocalDate(curDate))) {
                    //日期发生改变了,将当日数据添加到list中
                    if (data.getLocalDate() != null) {
                        List<BigDecimal> hourlyUsageRatio = calcHourlyUsageRatio(hourlyUsageList);
                        data.setHourlyUsageRatio(hourlyUsageRatio);

                        //过滤
                        boolean allZeroes = hourlyUsageList.stream().allMatch(n -> n.equals(BigDecimal.ZERO));
                        if (!allZeroes) {
                            data.setHourlyUsage(new ArrayList<>(hourlyUsageList));
                            cyVirtualDataList.add(data);
                        }
                        hourlyUsageList.clear();
                    }
                    data = new CYVirtualData();
                    data.setDayOfWeek(DateUtil.getDayOfWeek(curDate));
                    data.setLocalDate(DateUtil.dateToLocalDate(curDate));
                }

                nextDate = DateUtil.getOtherDateByHour(curDate, 1);

                if (aireadMap.containsKey(curDate) && aireadMap.containsKey(nextDate)) {
                    airead0 = aireadMap.get(curDate);
                    airead1 = aireadMap.get(nextDate);
                    if (airead0.getResultValue() != null && airead1.getResultValue() != null) {
                        hourlyUsageList.add(airead1.getResultValue().subtract(airead0.getResultValue()).abs());
                    } else {//有无效读数值,则用量定为0
                        hourlyUsageList.add(BigDecimal.ZERO);
                    }
                } else {//没有此小时数据,用量定为0
                    hourlyUsageList.add(BigDecimal.ZERO);
                }

                curDate = nextDate;
            }

            if (cyVirtualDataList.size() > 0) {
                cyVirtualDataMap.put(model.getStationCode(), cyVirtualDataList);
            }
        }
        mCYVirtualDataUtil = cyVirtualDataMap;
    }

    /**
     * 计算小时用量比例
     *
     * @param values
     * @return
     */
    private List<BigDecimal> calcHourlyUsageRatio(List<BigDecimal> values) {
        BigDecimal sum = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        if (sum.compareTo(BigDecimal.ZERO) == 0) {
            return values.stream().map(v -> BigDecimal.ZERO).collect(Collectors.toList());
        } else {
            return values.stream().map(v -> v.divide(sum, 5, RoundingMode.HALF_DOWN)).collect(Collectors.toList());
        }
    }
}
