package com.xxfc.platform.order.biz;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.google.common.collect.Lists;
import com.xxfc.platform.order.contant.enumerate.DeductionTypeEnum;
import com.xxfc.platform.order.contant.enumerate.StatisticsStatusEnum;
import com.xxfc.platform.order.entity.OrderRentVehicleReceivedStatistics;
import com.xxfc.platform.order.mapper.OrderRentVehicleReceivedStatisticsMapper;
import com.xxfc.platform.order.pojo.account.OrderAccountBo;
import com.xxfc.platform.order.pojo.account.OrderAccountDeduction;
import com.xxfc.platform.order.pojo.account.OrderAccountDetail;
import com.xxfc.platform.order.pojo.dto.OrderDTO;
import com.xxfc.platform.order.pojo.dto.OrderReceivedStatisticsFindDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 租车订单统计
 *
 * @author libin
 * @email 18178966185@163.com
 * @date 2019-11-08 18:03:42
 */
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OrderRentVehicleReceivedStatisticsBiz extends BaseBiz<OrderRentVehicleReceivedStatisticsMapper, OrderRentVehicleReceivedStatistics> {

    private final BaseOrderBiz baseOrderBiz;
    private final OrderAccountBiz orderAccountBiz;
    private final int PAY_ORDER = 101;

    /**
     * 租车统计查询
     *
     * @param orderReceivedStatisticsFindDTO
     * @return
     */
    public List<OrderRentVehicleReceivedStatistics> selectOrderReceivedStatistics(OrderReceivedStatisticsFindDTO orderReceivedStatisticsFindDTO) {
        return mapper.selectOrderRentVehicleReceivedStatistics(orderReceivedStatisticsFindDTO);
    }

    /**
     * 租车统计处理
     *
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @param companyMap 公司
     */
    public List<OrderRentVehicleReceivedStatistics> orderRentVehicleReceivedStatistics(Date startDate, Date endDate,Map<Integer,String> companyMap) {
        List<OrderRentVehicleReceivedStatistics> orderRentVehicleReceivedStatisticsList = new ArrayList<>();
        List<String> stisticsActiveState = new ArrayList<>();
        //根据租车订单类型 时间查询
        List<OrderDTO> orderDTOS = baseOrderBiz.selectOrdersByTypeAndTime(Arrays.asList(StatisticsStatusEnum.ORDER_RENT_VEHICLE_TYPE),null, startDate, endDate);

      //数据处理 状态组合 按支付状态分组 而后按组合状态
        Map<Boolean, Map<String, List<OrderDTO>>> stateGroupMap = orderDTOS.stream().peek(x -> {
            x.setStateGroup(String.format("%d-%d-%d-%d", x.getCompanyId(),
                    x.getOrderOrigin(),
                    x.getPayWay() == null ? StatisticsStatusEnum.NO_PAY_WAY:x.getPayWay()
                    ,x.getHasPay()));
            stisticsActiveState.add(x.getStateGroup());
        })
                .collect(Collectors.partitioningBy(x -> Objects.nonNull(x.getPayWay()), Collectors.groupingBy(OrderDTO::getStateGroup, Collectors.toList())));

        //订单账目信息
        List<OrderAccountBo> orderAccountBoList = orderAccountBiz.selectByTypeAndDate(StatisticsStatusEnum.ORDER_RENT_VEHICLE_TYPE,startDate, endDate);
        //账目数据处理 状态组合
        Map<String, List<OrderAccountBo>> ordersMap = orderAccountBoList.stream().peek(x -> {
            x.setStateGroup(String.format("%d-%d-%d-%d", x.getCompanyId(),
                    x.getOrderOrigin(),
                    x.getPayWay() == null ? StatisticsStatusEnum.NO_PAY_WAY:x.getPayWay(),
                    x.getHasPay()));
            stisticsActiveState.add(x.getStateGroup());
        }).collect(Collectors.groupingBy(OrderAccountBo::getStateGroup, Collectors.toList()));


        //已经支付单
        Set<Map.Entry<String, List<OrderAccountBo>>> ordersSet = ordersMap.entrySet();
        for (Map.Entry<String, List<OrderAccountBo>> orderEntry : ordersSet) {
            String orderKey = orderEntry.getKey();
            List<OrderAccountBo> orderAccountBos = orderEntry.getValue();
            OrderRentVehicleReceivedStatistics orderRentVehicleReceivedStatistics = StatisticsStatusEnum.wrapStatisticsObject(startDate, orderKey,companyMap,new OrderRentVehicleReceivedStatistics());
            //订单总金额
            BigDecimal totalOrderAmount = BigDecimal.ZERO;
            //订单总押金
            BigDecimal totalDepositAmount = BigDecimal.ZERO;
            //总违约金
            BigDecimal totalLateFeeAmount = BigDecimal.ZERO;
            //订单退款总金额
            BigDecimal totalOrderRefundAmount = BigDecimal.ZERO;
            //订单押金退款总金额
            BigDecimal totalDepositRefundAmount = BigDecimal.ZERO;
            //订单违章总金额
            BigDecimal totalBreakRulesRegulationAmount = BigDecimal.ZERO;
            //订单定损金总金额
            BigDecimal totalLossSpecifiedAmount = BigDecimal.ZERO;
            //不计免赔金额
            BigDecimal damageSafeAmount = BigDecimal.ZERO;
            //不计免赔金额退款
            BigDecimal refundDamageSafeAmount = BigDecimal.ZERO;
            //其它费用
            BigDecimal totalExtendAmount = BigDecimal.ZERO;

            for (OrderAccountBo orderAccountBo : orderAccountBos) {
                OrderAccountDetail accountDetailEntity = orderAccountBo.getAccountDetailEntity();
                if (orderAccountBo.getAccountType() == PAY_ORDER) {
                    totalOrderAmount = totalOrderAmount.add(accountDetailEntity.getOrderAmount());
                    totalDepositAmount = totalDepositAmount.add(accountDetailEntity.getDepositAmount());

                    if (Objects.nonNull(orderAccountBo.getDamageSafe()) && (orderAccountBo.getDamageSafe() == StatisticsStatusEnum.DAMAGE_SAFE)){
                        JSONObject data = orderAccountBo.getData();
                        if (!data.isEmpty()) {
                            Object paramJson = data.get(StatisticsStatusEnum.PARMAM_JSON);
                            JSONObject jsonObject = JSONUtil.parseObj(paramJson);
                            BigDecimal safeAmount = jsonObject.get(StatisticsStatusEnum.NO_DEDUCTIBLE_AMOUNT, BigDecimal.class);
                            damageSafeAmount = damageSafeAmount.add(safeAmount);
                        }
                    }
                } else {
                    totalOrderRefundAmount = totalOrderRefundAmount.add(accountDetailEntity.getOrderAmount());
                    totalDepositRefundAmount = totalDepositRefundAmount.add(accountDetailEntity.getDepositAmount());
                    List<OrderAccountDeduction> deductions = accountDetailEntity.getDeductions();
                    for (OrderAccountDeduction deduction : deductions) {
                       if (DeductionTypeEnum.lateFeeCode.contains(deduction.getType())){
                           totalLateFeeAmount = totalLateFeeAmount.add(deduction.getAmount());
                           if (deduction.getType().equals(DeductionTypeEnum.VIOLATE_CANCEL.getCode())){
                               if (Objects.nonNull(orderAccountBo.getDamageSafe()) && (orderAccountBo.getDamageSafe() == StatisticsStatusEnum.DAMAGE_SAFE)){
                                   JSONObject data = orderAccountBo.getData();
                                   if(!data.isEmpty()) {
                                       Object paramJson = data.get(StatisticsStatusEnum.PARMAM_JSON);
                                       JSONObject jsonObject = JSONUtil.parseObj(paramJson);
                                       BigDecimal safeAmount = jsonObject.get(StatisticsStatusEnum.NO_DEDUCTIBLE_AMOUNT, BigDecimal.class);
                                       refundDamageSafeAmount = refundDamageSafeAmount.add(safeAmount);
                                   }
                               }

                           }
                       }
                       if (DeductionTypeEnum.breakRulesRegulationCode.contains(deduction.getType())){
                           totalBreakRulesRegulationAmount = totalBreakRulesRegulationAmount.add(deduction.getAmount());
                       }

                       if (DeductionTypeEnum.lossSpecifiedCode.contains(deduction.getType())){
                           totalLossSpecifiedAmount = totalLossSpecifiedAmount.add(deduction.getAmount());
                       }

                       if (DeductionTypeEnum.consumerCode.contains(deduction.getType())){
                           BigDecimal extendAmount = deduction.getAmount().subtract(accountDetailEntity.getOriginOrderAmount());
                           totalExtendAmount = totalExtendAmount.add(extendAmount);
                       }
                    }
                }
            }
            orderRentVehicleReceivedStatistics.setNoDeductibleRefundAmount(refundDamageSafeAmount);
            orderRentVehicleReceivedStatistics.setNoDeductibleAmount(damageSafeAmount);
            orderRentVehicleReceivedStatistics.setTotalAmount(totalOrderAmount);
            orderRentVehicleReceivedStatistics.setDepositAmount(totalDepositAmount);
            orderRentVehicleReceivedStatistics.setLateFeeAmount(totalLateFeeAmount);
            orderRentVehicleReceivedStatistics.setOrderRefundAmount(totalOrderRefundAmount);
            orderRentVehicleReceivedStatistics.setDepositRefundAmount(totalDepositRefundAmount);
            orderRentVehicleReceivedStatistics.setBreakRulesRegulationAmount(totalBreakRulesRegulationAmount);
            orderRentVehicleReceivedStatistics.setLossSpecifiedAmount(totalLossSpecifiedAmount);
            Integer totalQuantity =  stateGroupMap ==null?0:stateGroupMap.get(Boolean.TRUE)==null?0:stateGroupMap.get(Boolean.TRUE).get(orderKey)==null?0:stateGroupMap.get(Boolean.TRUE).get(orderKey).size();
            orderRentVehicleReceivedStatistics.setTotalQuantity(totalQuantity);

            orderRentVehicleReceivedStatisticsList.add(orderRentVehicleReceivedStatistics);
        }


        //未支付单
        Map<String, List<OrderDTO>> noPayOrderRentvehicleMap = stateGroupMap == null?Collections.EMPTY_MAP:stateGroupMap.get(Boolean.FALSE)==null?Collections.EMPTY_MAP:stateGroupMap.get(Boolean.FALSE);
        List<OrderRentVehicleReceivedStatistics> noPayOrderRentVehicleStatisticsList = createNoPayOrderRentVehicleStatisticsList(startDate,noPayOrderRentvehicleMap,companyMap);
        orderRentVehicleReceivedStatisticsList.addAll(noPayOrderRentVehicleStatisticsList);


        //创建剩余状态组合的租车统计对象
        List<OrderRentVehicleReceivedStatistics> otherStatisticsStateGroupList = createOtherStatisticsStateGroupList(startDate,stisticsActiveState, companyMap);
        orderRentVehicleReceivedStatisticsList.addAll(otherStatisticsStateGroupList);
        //保存
        insertMemberReceivedStatisticsBatch(orderRentVehicleReceivedStatisticsList);

        return orderRentVehicleReceivedStatisticsList;
    }

    /**
     *
     * @param startDate 时间
     * @param noPayOrdersMap 未支付单map
     * @return
     */
    private List<OrderRentVehicleReceivedStatistics> createNoPayOrderRentVehicleStatisticsList(Date startDate,Map<String, List<OrderDTO>> noPayOrdersMap,Map<Integer,String> companyMap) {
        List<OrderRentVehicleReceivedStatistics> orderRentVehicleReceivedStatisticsList = new ArrayList<>();
        if (noPayOrdersMap==null || noPayOrdersMap.isEmpty()){
            return orderRentVehicleReceivedStatisticsList;
        }
        Set<Map.Entry<String, List<OrderDTO>>> noPayOrderSet = noPayOrdersMap.entrySet();
        for (Map.Entry<String, List<OrderDTO>> noPayOrderEntry : noPayOrderSet) {
            String noPayOrderStateGroup = noPayOrderEntry.getKey();
            List<OrderDTO> noPayOrders = noPayOrderEntry.getValue();
            List<BigDecimal> damageSafeAmountList = new ArrayList<>();
            BigDecimal totalNoPayAmount = noPayOrders.stream().peek(x->{
                //免赔
                if (Objects.nonNull(x.getDamageSafe()) && (x.getDamageSafe() == StatisticsStatusEnum.DAMAGE_SAFE)){
                    JSONObject data = x.getData();
                    if (!data.isEmpty()) {
                        Object paramJson = data.get(StatisticsStatusEnum.PARMAM_JSON);
                        JSONObject jsonObject = JSONUtil.parseObj(paramJson);
                        BigDecimal safeAmount = jsonObject.get(StatisticsStatusEnum.NO_DEDUCTIBLE_AMOUNT, BigDecimal.class);
                        damageSafeAmountList.add(safeAmount);
                    }
                }
            }).map(OrderDTO::getRealAmount).reduce(BigDecimal.ZERO, (x, y) -> x.add(y));
            OrderRentVehicleReceivedStatistics orderRentVehicleReceivedStatistics = StatisticsStatusEnum.wrapStatisticsObject(startDate, noPayOrderStateGroup,companyMap,new OrderRentVehicleReceivedStatistics());

            BigDecimal damageSafeAmount = damageSafeAmountList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
            orderRentVehicleReceivedStatistics.setNoDeductibleAmount(damageSafeAmount);
            BigDecimal depositAmount = noPayOrders.stream().map(OrderDTO::getDeposit).reduce(BigDecimal.ZERO, BigDecimal::add);
            //押金
            orderRentVehicleReceivedStatistics.setDepositAmount(depositAmount);
            orderRentVehicleReceivedStatistics.setNoDeductibleRefundAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatistics.setDepositRefundAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatistics.setLossSpecifiedAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatistics.setBreakRulesRegulationAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatistics.setTotalAmount(totalNoPayAmount.subtract(depositAmount));
            orderRentVehicleReceivedStatistics.setTotalQuantity(noPayOrders.size());
            orderRentVehicleReceivedStatisticsList.add(orderRentVehicleReceivedStatistics);
        }
        return orderRentVehicleReceivedStatisticsList;
    }

    /**
     * 创建剩余状态数据
     *
     * @param startDate               时间
     * @param statisticsStateGroups   状态组合 集合
     * @param companyMap              公司
     * @return
     */
    private List<OrderRentVehicleReceivedStatistics> createOtherStatisticsStateGroupList(Date startDate,
                                                                                         List<String> statisticsStateGroups,
                                                                                         Map<Integer,String> companyMap) {

        List<OrderRentVehicleReceivedStatistics> orderRentVehicleReceivedStatisticsList = new ArrayList<>(statisticsStateGroups.size());
        //获取剩余状态组合
        List<Integer> companyIds = Objects.isNull(companyMap)?Collections.EMPTY_LIST:Lists.newArrayList(companyMap.keySet());
        List<String> otherStatisticsStateGroup = StatisticsStatusEnum.getOtherStatisticsStateGroup(companyIds,statisticsStateGroups);
        //创建租车统计克隆对象
        OrderRentVehicleReceivedStatistics orderRentVehicleReceivedStatistics = new OrderRentVehicleReceivedStatistics();
        //统计对象的生成
        otherStatisticsStateGroup.stream().map(stateGroup -> {
            OrderRentVehicleReceivedStatistics orderRentVehicleReceivedStatisticsClone = StatisticsStatusEnum.wrapStatisticsObject(startDate, stateGroup,companyMap,ObjectUtil.cloneByStream(orderRentVehicleReceivedStatistics));
            orderRentVehicleReceivedStatisticsClone.setDepositAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatisticsClone.setDepositRefundAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatisticsClone.setNoDeductibleAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatisticsClone.setNoDeductibleRefundAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatisticsClone.setLossSpecifiedAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatisticsClone.setBreakRulesRegulationAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatisticsClone.setTotalAmount(BigDecimal.ZERO);
            orderRentVehicleReceivedStatisticsClone.setTotalQuantity(0);
            orderRentVehicleReceivedStatisticsList.add(orderRentVehicleReceivedStatisticsClone);
            return orderRentVehicleReceivedStatisticsClone;
        }).count();

        return orderRentVehicleReceivedStatisticsList;
    }

    /**
     * 批量插入数据  mysql sql语句默认不能超过4M
     *
     * @param orderRentVehicleReceivedStatistics
     */
    public void insertMemberReceivedStatisticsBatch(List<OrderRentVehicleReceivedStatistics> orderRentVehicleReceivedStatistics) {
        orderRentVehicleReceivedStatistics.sort(Comparator.comparing(OrderRentVehicleReceivedStatistics::getCompanyId));
        int orderSize = orderRentVehicleReceivedStatistics.size();
        int sqlAdq = orderSize / StatisticsStatusEnum.DEFAULT_SQL_SIZE;
        int sqlMod = orderSize % StatisticsStatusEnum.DEFAULT_SQL_SIZE;
        sqlAdq = sqlMod == 0 ? sqlAdq : sqlAdq + 1;
        for (int i = 0; i < sqlAdq; i++) {
            int fromIndex = StatisticsStatusEnum.DEFAULT_SQL_SIZE * i;
            int toIndex = StatisticsStatusEnum.DEFAULT_SQL_SIZE * (i + 1);
            toIndex = toIndex > orderSize ? orderSize : toIndex;
            List<OrderRentVehicleReceivedStatistics> orderRentVehicleReceivedStatisticsList = orderRentVehicleReceivedStatistics.subList(fromIndex, toIndex);
            mapper.insertList(orderRentVehicleReceivedStatisticsList);
        }
    }
}