package com.bailuntec.job;

import com.bailuntec.domain.entity.DcBaseCostFirst;
import com.bailuntec.domain.entity.DcBaseSku;
import com.bailuntec.domain.entity.DcMidCostFirst;
import com.bailuntec.domain.entity.DcMidCostFirstOrder;
import com.bailuntec.domain.example.DcBaseSkuExample;
import com.bailuntec.domain.example.DcMidCostFirstExample;
import com.bailuntec.domain.pojo.MidCostFirst;
import com.bailuntec.mapper.DcBaseCostFirstMapper;
import com.bailuntec.mapper.DcBaseSkuMapper;
import com.bailuntec.mapper.DcMidCostFirstMapper;
import com.bailuntec.mapper.DcMidCostFirstOrderMapper;
import com.bailuntec.utils.ListUtil;
import com.bailuntec.utils.SessionUtil;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
public class CalculateCostFirstJob implements SimpleJob {

    @Override
    public void execute(ShardingContext shardingContext) {


        try (SqlSession sqlSession = SessionUtil.getFactory().openSession(true)) {

            DcBaseCostFirstMapper dcBaseCostFirstMapper = sqlSession.getMapper(DcBaseCostFirstMapper.class);
            DcMidCostFirstOrderMapper dcMidCostFirstOrderMapper = sqlSession.getMapper(DcMidCostFirstOrderMapper.class);
            DcBaseSkuMapper dcBaseSkuMapper = sqlSession.getMapper(DcBaseSkuMapper.class);
            DcMidCostFirstMapper dcMidCostFirstMapper = sqlSession.getMapper(DcMidCostFirstMapper.class);

            int currentPage = 1;
            int pageSize = 1000;
            int countChannelOrder = dcBaseCostFirstMapper.countChannelOrder();
            int totalPage = getTotalPage(countChannelOrder, pageSize);

            do {
                log.info("当前第：{}页", currentPage);
                log.info("总共：{}页", totalPage);

                Integer pageStart = (currentPage - 1) * currentPage;
                Integer pageOffset = currentPage;

                List<DcBaseCostFirst> dcBaseCostFirstList = dcBaseCostFirstMapper.dcBaseCostGroupList(pageStart, pageOffset);

                if (ListUtil.isNotEmpty(dcBaseCostFirstList)) {

                    //聚合base头程费sku信息
                    List<DcBaseSku> dcBaseSkuList = dcBaseSkuMapper.selectByExample(DcBaseSkuExample
                            .newAndCreateCriteria()
                            .andBailunSkuIn(dcBaseCostFirstList
                                    .stream()
                                    .map(DcBaseCostFirst::getBailunSku)
                                    .collect(Collectors.toList()))
                            .example());
                    Map<String, DcBaseSku> dcBaseSkuMap = dcBaseSkuList.stream().collect(Collectors.toMap(DcBaseSku::getBailunSku, dcBaseSku -> dcBaseSku));
                    dcBaseCostFirstList.forEach(dcBaseCostFirst -> dcBaseCostFirst.setDcBaseSku(Optional.ofNullable(dcBaseSkuMap.get(dcBaseCostFirst.getBailunSku()))));

                    //头程费用 按订单维度（boxId,channelOrderId ,transferOrderId,warehouseCode）分组
                    Map<String, List<DcBaseCostFirst>> dcBaseCostFirstMap = dcBaseCostFirstList.stream().collect(
                            Collectors.groupingBy(val -> val.getChannelOrderId() + val.getTransferOrderId() + val.getBoxId() + val.getWarehouseCode()));

                    //生成订单维度DcMidCostFirstOrder列表
                    List<DcMidCostFirstOrder> dcMidCostFirstOrderList = dcBaseCostFirstMap.values()
                            .stream()
                            .map(dcBaseCostFirstGroup -> {

                                //计算sku权重
                                this.calcBaseFirstCostSkuWeightRatio(dcBaseCostFirstGroup);
                                return dcBaseCostFirstGroup
                                        .stream()
                                        .map(dcBaseCostFirst -> {

                                            BigDecimal skuCostFirst = dcBaseCostFirst.getCostFirst().multiply(dcBaseCostFirst.getSkuWeightRatio()).setScale(3, RoundingMode.HALF_EVEN);

                                            DcMidCostFirstOrder dcMidCostFirstOrder = new DcMidCostFirstOrder();
                                            dcMidCostFirstOrder.setBailunSku(dcBaseCostFirst.getBailunSku());
                                            dcMidCostFirstOrder.setWarehouseCode(dcBaseCostFirst.getWarehouseCode());
                                            dcMidCostFirstOrder.setChannelOrderId(dcBaseCostFirst.getChannelOrderId());
                                            dcMidCostFirstOrder.setTransferOrderId(dcBaseCostFirst.getTransferOrderId());
                                            dcMidCostFirstOrder.setQuantity(dcBaseCostFirst.getQuantity());
                                            dcMidCostFirstOrder.setBoxId(dcBaseCostFirst.getBoxId());
                                            dcMidCostFirstOrder.setWeightRatio(dcBaseCostFirst.getSkuWeightRatio());
                                            dcMidCostFirstOrder.setCostFirst(skuCostFirst);
                                            dcMidCostFirstOrder.setCompanyId(dcBaseCostFirst.getCompanyId());
                                            dcMidCostFirstOrder.setHasCalculation(true);
                                            return dcMidCostFirstOrder;

                                        }).collect(Collectors.toList());
                            })
                            .flatMap(List::stream)
                            .collect(Collectors.toList());

                    //批量插入更新订单维度
                    dcMidCostFirstOrderMapper.upsertBatch(dcMidCostFirstOrderList);
                }
                currentPage = currentPage + 1;
            }
            while (currentPage <= totalPage);


            int dcMidCostFirstIndex = 1;
            int dcMidCostFirstPageSize = 1000;
            int countSkuWarehouse = dcMidCostFirstOrderMapper.countSkuWarehouse();
            int dcMidCostFirstTotalPage = this.getTotalPage(countSkuWarehouse, dcMidCostFirstPageSize);

            do {
                List<MidCostFirst> midCostFirstList = dcMidCostFirstOrderMapper.listSkuWarehouse((dcMidCostFirstIndex - 1) * dcMidCostFirstPageSize, dcMidCostFirstPageSize);

                List<DcMidCostFirst> dcMidCostFirstList = midCostFirstList.stream().map(midCostFirst -> {
                            DcMidCostFirst dcMidCostFirst = dcMidCostFirstMapper.selectOneByExample(DcMidCostFirstExample.newAndCreateCriteria()
                                    .andBailunSkuEqualTo(midCostFirst.getBailunSku())
                                    .andWarehouseCodeEqualTo(midCostFirst.getWarehouseCode())
                                    .example());
                            if (dcMidCostFirst == null) {
                                dcMidCostFirst = new DcMidCostFirst();
                                dcMidCostFirst.setBailunSku(midCostFirst.getBailunSku());
                                dcMidCostFirst.setWarehouseCode(midCostFirst.getWarehouseCode());
                                dcMidCostFirst.setCompanyId(0);
                                dcMidCostFirst.setHasConfig(false);
                            }

                            dcMidCostFirst.setCostFirst(midCostFirst.getTotalCostFirst().divide(new BigDecimal(midCostFirst.getTotalCount()), 2, BigDecimal.ROUND_HALF_UP));
                            dcMidCostFirst.setCostCount(midCostFirst.getTotalCount());
                            dcMidCostFirst.setHasCalculation(true);

                            return dcMidCostFirst;
                        }
                )
                        .collect(Collectors.toList());

                dcMidCostFirstMapper.upsertBatch(dcMidCostFirstList);

                dcMidCostFirstIndex = dcMidCostFirstIndex + 1;
            } while (dcMidCostFirstIndex < dcMidCostFirstTotalPage);

        } catch (Exception e) {
            throw new RuntimeException("任务失败", e);
        }
    }

    /**
     * <p>
     * 按照sku重量计算组别内每个订单维度的头程费用权重
     * </p>
     *
     * @param dcBaseCostFirstGroup 基础头程费用 按订单维度（boxId,channelOrderId ,transferOrderId,warehouseCode）分组后的组别
     */
    private void calcBaseFirstCostSkuWeightRatio(List<DcBaseCostFirst> dcBaseCostFirstGroup) {

        //总重量
        BigDecimal totalWeight = dcBaseCostFirstGroup.stream().map(dcBaseCostFirst -> dcBaseCostFirst.getDcBaseSku().map(DcBaseSku::getWeight).orElse(BigDecimal.ZERO)
                .multiply(new BigDecimal(dcBaseCostFirst.getQuantity() == null ? 0 : dcBaseCostFirst.getQuantity())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal leftRatio = BigDecimal.ONE;
        if (totalWeight.equals(BigDecimal.ZERO)) {

            for (int i = 0; i < dcBaseCostFirstGroup.size(); i++) {
                DcBaseCostFirst dcBaseCostFirst = dcBaseCostFirstGroup.get(i);
                //最后一个元素用减法计算
                if (dcBaseCostFirstGroup.size() - 1 == i) {
                    dcBaseCostFirst.setSkuWeightRatio(leftRatio);
                }
                //前面用除法计算
                else {
                    BigDecimal skuWeightRatio = BigDecimal.ONE.divide(new BigDecimal(dcBaseCostFirstGroup.size()), 2, BigDecimal.ROUND_HALF_UP);
                    dcBaseCostFirst.setSkuWeightRatio(skuWeightRatio);
                    leftRatio = leftRatio.subtract(skuWeightRatio);
                }
            }
        } else {
            for (int i = 0; i < dcBaseCostFirstGroup.size(); i++) {
                DcBaseCostFirst dcBaseCostFirst = dcBaseCostFirstGroup.get(i);
                //最后一个元素用减法计算
                if (dcBaseCostFirstGroup.size() - 1 == i) {
                    dcBaseCostFirst.setSkuWeightRatio(leftRatio);
                }
                //前面用除法计算
                else {
                    BigDecimal skuWeightRatio = new BigDecimal(dcBaseCostFirst.getQuantity()).multiply(dcBaseCostFirst.getDcBaseSku().map(DcBaseSku::getWeight).orElse(BigDecimal.ZERO))
                            .divide(totalWeight, 2, BigDecimal.ROUND_HALF_UP);
                    dcBaseCostFirst.setSkuWeightRatio(skuWeightRatio);
                    leftRatio = leftRatio.subtract(skuWeightRatio);
                }
            }
        }


    }


    private int getTotalPage(int count, int pageSize) {
        if (count % pageSize == 0) {
            return count / pageSize;
        }
        return count / pageSize + 1;
    }

}
