package com.bailuntec.job;

import com.alibaba.fastjson.JSON;
import com.bailuntec.domain.entity.*;
import com.bailuntec.domain.example.DcBaseCostFirstFinishExample;
import com.bailuntec.domain.example.DcBaseSkuExample;
import com.bailuntec.domain.example.DcBaseStockExample;
import com.bailuntec.domain.example.DcMidCostFirstExample;
import com.bailuntec.domain.pojo.costfirstfinish.CostFirstFinishData;
import com.bailuntec.domain.pojo.costfirstfinish.CostFirstFinishResult;
import com.bailuntec.mapper.DcBaseCostFirstFinishMapper;
import com.bailuntec.mapper.DcBaseSkuMapper;
import com.bailuntec.mapper.DcBaseStockMapper;
import com.bailuntec.mapper.DcMidCostFirstMapper;
import com.bailuntec.support.PointJob;
import com.bailuntec.utils.ListUtil;
import com.bailuntec.utils.OkHttpUtil;
import com.bailuntec.utils.PropertiesUtil;
import com.bailuntec.utils.SessionUtil;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * </p>
 *
 * @author robbendev
 * @since 2020/9/2 9:45 上午
 */
@Slf4j
public class SyncCostFirstFinishJob extends PointJob {

    private PropertiesUtil propertiesUtil = PropertiesUtil.getInstance("const");

    @Override
    public void executeJob(ShardingContext shardingContext, JobPointLog jobPointLog) {
        log.info("同步完成的调拨单 开始");

        OkHttpClient client = OkHttpUtil.getInstance();

        HashMap<String, String> map = new HashMap<>();
        map.put("startTime", jobPointLog.getStartTime().minusMinutes(3).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        map.put("endTime", jobPointLog.getEndTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        Integer totalPage = 0;
        do {
            map.put("currentPage", jobPointLog.getPageIndex().toString());
            Request request = new Request.Builder()
                    .url(OkHttpUtil.attachHttpGetParams(propertiesUtil.getPropertyAsString("COST_FIRST_FINISH_URL"), map))
                    .get()
                    .addHeader("Content-Type", "application/json")
                    .addHeader("cache-control", "no-cache")
                    .build();
            Response response = null;

            try {
                log.info("调用头程费用接口 starting");
                response = client.newCall(request).execute();

                String result = response.body().string();

                if (StringUtils.isNotBlank(result)) {
                    log.info("序列化头程费用头程费用 starting");
                    CostFirstFinishResult costFirstResult = JSON.parseObject(result, CostFirstFinishResult.class);
                    log.info("序列化头程费用头程费用 end");

                    log.info("头程费用当前请求页数 ：{} \n 头程费用当前总页数 ：{} \n 头程费用当前页码大小 ：{} \n",
                            jobPointLog.getPageIndex(),
                            costFirstResult.getPageCount(),
                            costFirstResult.getData().size());

                    totalPage = costFirstResult.getPageCount();

                    if (ListUtil.isNotEmpty(costFirstResult.getData())) {
                        handleCostFirstData(costFirstResult.getData());
                    }
                } else {
                    log.error("调用头程费接口失败,返回200,param:{}", new Gson().toJson(map));
                }

            } catch (IOException e) {
                log.error("调用头程费接口失败, message:{},param:{}", e.getMessage(), new Gson().toJson(map), e);
            } finally {
                if (response != null) {
                    response.close();
                }
            }
            log.info("调用头程费用接口 end");
            jobPointLog.setPageIndex(jobPointLog.getPageIndex() + 1);

        } while (jobPointLog.getPageIndex() <= totalPage);

        jobPointLog.setPageIndex(1);
        jobPointLog.setStartTime(jobPointLog.getEndTime());
        jobPointLog.setEndTime(jobPointLog.getStartTime().plusDays(jobPointLog.getIntervalTime().longValue()).isAfter(LocalDateTime.now())
                ? LocalDateTime.now()
                : jobPointLog.getStartTime().plusDays(jobPointLog.getIntervalTime().longValue()));
    }

    private void handleCostFirstData(List<CostFirstFinishData> costFirstFinishDataList) {
        try {
            DcBaseCostFirstFinishMapper dcBaseCostFirstFinishMapper = SessionUtil.getSession().getMapper(DcBaseCostFirstFinishMapper.class);
            DcBaseStockMapper dcBaseStockMapper = SessionUtil.getSession().getMapper(DcBaseStockMapper.class);
            DcBaseSkuMapper dcBaseSkuMapper = SessionUtil.getSession().getMapper(DcBaseSkuMapper.class);
            DcMidCostFirstMapper dcMidCostFirstMapper = SessionUtil.getSession().getMapper(DcMidCostFirstMapper.class);

            for (CostFirstFinishData costFirstFinishData : costFirstFinishDataList) {

                costFirstFinishData.setCostDuty(costFirstFinishData.getCostDuty().setScale(3, RoundingMode.HALF_EVEN));
                costFirstFinishData.setCostCustomsClearance(costFirstFinishData.getCostCustomsClearance().setScale(3, RoundingMode.HALF_EVEN));
                costFirstFinishData.setCostGoodsClearance(costFirstFinishData.getCostGoodsClearance().setScale(3, RoundingMode.HALF_EVEN));
                costFirstFinishData.setCostWeight(costFirstFinishData.getCostWeight().setScale(3, RoundingMode.HALF_EVEN));
                costFirstFinishData.setCostFuel(costFirstFinishData.getCostFuel().setScale(3, RoundingMode.HALF_EVEN));
                costFirstFinishData.setCostFirst(costFirstFinishData.getCostFirst().setScale(3, RoundingMode.HALF_EVEN));


                List<DcBaseCostFirstFinish> dcBaseCostFirstFinishList = costFirstFinishData.getSkus().stream()
                        .map(sku -> {
                            try {
                                //查询条件
                                DcBaseCostFirstFinishExample example = DcBaseCostFirstFinishExample.newAndCreateCriteria()
                                        .andBailunSkuEqualTo(sku.getBailunSku())
                                        .andChannelOrderIdEqualTo(costFirstFinishData.getChannelOrderId())
                                        .andBoxIdEqualTo(costFirstFinishData.getBoxId())
                                        .andTransferOrderIdEqualTo(costFirstFinishData.getTransferOrderId())
                                        .andWarehouseCodeEqualTo(costFirstFinishData.getWarehouseCode())
                                        .example();

                                DcBaseCostFirstFinish dcBaseCostFirstFinish = dcBaseCostFirstFinishMapper.selectOneByExample(example);

                                //只新增同步 所以没有才插入一条记录
                                if (dcBaseCostFirstFinish == null) {
                                    log.info("新增同步已完成调拨单");

                                    dcBaseCostFirstFinish = new DcBaseCostFirstFinish();
                                    BeanUtils.copyProperties(dcBaseCostFirstFinish, costFirstFinishData);
                                    dcBaseCostFirstFinish.setBailunSku(sku.getBailunSku());
                                    dcBaseCostFirstFinish.setQuantity(sku.getQuantity());

                                    DcBaseStock dcBaseStock = dcBaseStockMapper.selectOneByExample(DcBaseStockExample.newAndCreateCriteria()
                                            .andWarehouseCodeEqualTo(costFirstFinishData.getWarehouseCode())
                                            .andBailunSkuEqualTo(sku.getBailunSku())
                                            .example());

                                    DcBaseSku dcBaseSku = dcBaseSkuMapper.selectOneByExample(DcBaseSkuExample.newAndCreateCriteria()
                                            .andBailunSkuEqualTo(sku.getBailunSku())
                                            .example());

                                    //瞬时库存
                                    dcBaseCostFirstFinish.setSkuStockQuantity(dcBaseStock.getUsableStock() + dcBaseStock.getOccupyStock());
                                    //sku重量
                                    dcBaseCostFirstFinish.setSkuWeight(dcBaseSku.getWeight());
                                    return dcBaseCostFirstFinish;
                                } else {
                                    dcBaseCostFirstFinish.setOperationTime(costFirstFinishData.getOperationTime());
                                    dcBaseCostFirstFinishMapper.updateByPrimaryKey(dcBaseCostFirstFinish);
                                }
                                return null;
                            } catch (Exception e) {
                                log.error("create dcBaseCostFirstFinish failed :{} ,message:{}", JSON.toJSONString(costFirstFinishData), e.getMessage(), e);
                                return null;
                            }
                        })
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());

                //计算重量比重
                this.calcBaseFirstCostSkuWeightRatio(dcBaseCostFirstFinishList);

                //计算sku平均头程费用
                dcBaseCostFirstFinishList.forEach(dcBaseCostFirstFinish -> {

                    //sku平均头程费用
                    DcMidCostFirst dcMidCostFirst = dcMidCostFirstMapper.selectOneByExample(DcMidCostFirstExample.newAndCreateCriteria()
                            .andBailunSkuEqualTo(dcBaseCostFirstFinish.getBailunSku())
                            .andWarehouseCodeEqualTo(dcBaseCostFirstFinish.getWarehouseCode())
                            .example());

                    // 如果不存在初始值 使用该次调拨单的平均值
                    if (dcMidCostFirst == null) {

                        //新的平均头程费
                        BigDecimal afterAvlSkuCostFirst = dcBaseCostFirstFinish.getSkuCostFirst()
                                .divide(new BigDecimal(dcBaseCostFirstFinish.getQuantity()), 6, BigDecimal.ROUND_DOWN);

                        dcMidCostFirst = new DcMidCostFirst();
                        dcMidCostFirst.setBailunSku(dcBaseCostFirstFinish.getBailunSku());
                        dcMidCostFirst.setWarehouseCode(dcBaseCostFirstFinish.getWarehouseCode());
                        //新平均头程费
                        dcMidCostFirst.setCostFirst(afterAvlSkuCostFirst);
                        //是否每次需要动态计算头程费用 ：需要
                        dcMidCostFirst.setHasCalculation(true);
                        //默认当前库存
                        dcMidCostFirst.setCostCount(dcBaseCostFirstFinish.getSkuStockQuantity());
                        dcMidCostFirstMapper.insertSelective(dcMidCostFirst);


                        //记录日志到dcBaseCostFirstFinish
                        dcBaseCostFirstFinish.setHasCalculation(true);
                        dcBaseCostFirstFinish.setPreAvlSkuCostFirst(BigDecimal.ZERO);
                        dcBaseCostFirstFinish.setAfterAvlSkuCostFirst(afterAvlSkuCostFirst);
                    }

                    //注意这里的规则 ： 如果sku需要动态更新 根据调拨单sku更新sku平均头程费用

                    //rule : case 当前sku库存不为0 ：新的平均头程费 =  （旧的平均头程费用 * max((当前sku库存数量 - 调拨单sku数量),0) + 调拨单sku加权平均头程费用）/ （当前sku库存数量 + 调拨单sku数量）
                    //rule : case 当前sku库存为0 ：新的平均头程费 =  调拨单sku加权平均头程费 /  调拨单sku数量

                    else {
                        //旧的平均头程费用
                        BigDecimal preAvlSkuCostFirst = dcMidCostFirst.getCostFirst();

                        if (dcMidCostFirst.getHasCalculation()) {

                            // 调拨单sku加权平均头程费用 + 旧的平均头程费用 * (当前sku库存数量 - 调拨单sku数量))
                            BigDecimal totalCost = dcBaseCostFirstFinish.getSkuCostFirst()
                                    .add(preAvlSkuCostFirst.multiply(new BigDecimal(Math.max(0, dcBaseCostFirstFinish.getSkuStockQuantity() - dcBaseCostFirstFinish.getQuantity()))));

                            //（当前sku库存数量）
                            BigDecimal totalQuantity = new BigDecimal(dcBaseCostFirstFinish.getSkuStockQuantity());

                            //调拨单sku数量
                            BigDecimal quantity = new BigDecimal(dcBaseCostFirstFinish.getQuantity());

                            //新的平均头程费
                            BigDecimal afterAvlSkuCostFirst;
                            if (totalQuantity.compareTo(quantity) <= 0) {
                                afterAvlSkuCostFirst = dcBaseCostFirstFinish.getSkuCostFirst().divide(quantity, 6, RoundingMode.DOWN);
                            } else {
                                // （调拨单sku加权平均头程费用 + 旧的平均头程费用 * (当前sku库存数量 - 调拨单sku数量))） /
                                afterAvlSkuCostFirst = totalCost.divide(totalQuantity, 6, RoundingMode.DOWN);
                            }

                            dcMidCostFirst.setCostFirst(afterAvlSkuCostFirst);

                            //（当前sku库存数量）
                            dcMidCostFirst.setCostCount(dcBaseCostFirstFinish.getSkuStockQuantity());
                            dcMidCostFirstMapper.updateByPrimaryKey(dcMidCostFirst);

                            //记录日志到dcBaseCostFirstFinish
                            dcBaseCostFirstFinish.setHasCalculation(true);
                            dcBaseCostFirstFinish.setPreAvlSkuCostFirst(preAvlSkuCostFirst);
                            dcBaseCostFirstFinish.setAfterAvlSkuCostFirst(afterAvlSkuCostFirst);
                        }
                        //sku平均头程费用不变
                        else {

                            //记录日志到dcBaseCostFirstFinish
                            dcBaseCostFirstFinish.setHasCalculation(false);
                            dcBaseCostFirstFinish.setPreAvlSkuCostFirst(preAvlSkuCostFirst);
                            dcBaseCostFirstFinish.setAfterAvlSkuCostFirst(preAvlSkuCostFirst);
                        }
                    }

                    try {
                        dcBaseCostFirstFinishMapper.insertSelective(dcBaseCostFirstFinish);
                    } catch (Exception e) {
                        log.error("insert failed obj:{},message:{}", JSON.toJSONString(dcBaseCostFirstFinish), e.getMessage(), e);
                    }
                });
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            SessionUtil.closeSession();
        }
    }

    /**
     * <p>
     * 按照sku重量计算组别内每个订单维度的头程费用权重
     * </p>
     *
     * @param dcBaseCostFirstFinishGroup 基础头程费用组
     */
    private void calcBaseFirstCostSkuWeightRatio(List<DcBaseCostFirstFinish> dcBaseCostFirstFinishGroup) {

        //总重量
        BigDecimal totalWeight = dcBaseCostFirstFinishGroup.stream()
                .map(dcBaseCostFirstFinish -> dcBaseCostFirstFinish.getSkuWeight().multiply(new BigDecimal(dcBaseCostFirstFinish.getQuantity())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);

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

            for (int i = 0; i < dcBaseCostFirstFinishGroup.size(); i++) {
                DcBaseCostFirstFinish dcBaseCostFirstFinish = dcBaseCostFirstFinishGroup.get(i);
                //最后一个元素用减法计算
                if (dcBaseCostFirstFinishGroup.size() - 1 == i) {
                    dcBaseCostFirstFinish.setSkuWeightRatio(leftRatio);
                    dcBaseCostFirstFinish.setSkuCostFirst(dcBaseCostFirstFinish.getCostFirst().multiply(dcBaseCostFirstFinish.getSkuWeightRatio()));
                }
                //前面用除法计算
                else {
                    BigDecimal skuWeightRatio = BigDecimal.ONE.divide(new BigDecimal(dcBaseCostFirstFinishGroup.size()), 6, BigDecimal.ROUND_DOWN);
                    dcBaseCostFirstFinish.setSkuWeightRatio(skuWeightRatio);
                    dcBaseCostFirstFinish.setSkuCostFirst(dcBaseCostFirstFinish.getCostFirst().multiply(dcBaseCostFirstFinish.getSkuWeightRatio()));
                    leftRatio = leftRatio.subtract(skuWeightRatio);
                }
            }
        } else {
            for (int i = 0; i < dcBaseCostFirstFinishGroup.size(); i++) {
                DcBaseCostFirstFinish dcBaseCostFirstFinish = dcBaseCostFirstFinishGroup.get(i);
                //最后一个元素用减法计算
                if (dcBaseCostFirstFinishGroup.size() - 1 == i) {
                    dcBaseCostFirstFinish.setSkuWeightRatio(leftRatio);
                    dcBaseCostFirstFinish.setSkuCostFirst(dcBaseCostFirstFinish.getCostFirst().multiply(dcBaseCostFirstFinish.getSkuWeightRatio()));

                }
                //前面用除法计算
                else {
                    BigDecimal skuWeightRatio = new BigDecimal(dcBaseCostFirstFinish.getQuantity()).multiply(dcBaseCostFirstFinish.getSkuWeight())
                            .divide(totalWeight, 6, BigDecimal.ROUND_DOWN);
                    dcBaseCostFirstFinish.setSkuWeightRatio(skuWeightRatio);
                    dcBaseCostFirstFinish.setSkuCostFirst(dcBaseCostFirstFinish.getCostFirst().multiply(dcBaseCostFirstFinish.getSkuWeightRatio()));

                    leftRatio = leftRatio.subtract(skuWeightRatio);
                }
            }
        }
    }
}
