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);

            costFirstFinishDataList
                    .stream()
                    .filter(costFirstFinishData -> costFirstFinishData.getCostFirst().compareTo(new BigDecimal("8")) > 0)
                    .forEach(costFirstFinishData -> {
                        {
                            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();

                                    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加权平均头程费

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

                                    if (dcMidCostFirst.getHasCalculation()) {

                                        // 调拨单sku加权平均头程费用 * 调拨单sku数量 + 旧的平均头程费用 * (当前sku库存数量 - 调拨单sku数量)
                                        BigDecimal totalCost = dcBaseCostFirstFinish.getSkuCostFirst().multiply(new BigDecimal(dcBaseCostFirstFinish.getQuantity()))
                                                .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();
                                        } 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())
                            .divide(new BigDecimal(dcBaseCostFirstFinish.getQuantity()), 6, BigDecimal.ROUND_DOWN));
                }
                //前面用除法计算
                else {
                    BigDecimal skuWeightRatio = BigDecimal.ONE.divide(new BigDecimal(dcBaseCostFirstFinishGroup.size()), 6, BigDecimal.ROUND_DOWN);
                    dcBaseCostFirstFinish.setSkuWeightRatio(skuWeightRatio);
                    dcBaseCostFirstFinish.setSkuCostFirst(dcBaseCostFirstFinish.getCostFirst().multiply(dcBaseCostFirstFinish.getSkuWeightRatio())
                            .divide(new BigDecimal(dcBaseCostFirstFinish.getQuantity()), 6, BigDecimal.ROUND_DOWN));
                    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()).divide(
                            new BigDecimal(dcBaseCostFirstFinish.getQuantity()), 6, BigDecimal.ROUND_DOWN));

                }
                //前面用除法计算
                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()).divide(
                            new BigDecimal(dcBaseCostFirstFinish.getQuantity()), 6, BigDecimal.ROUND_DOWN));

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