package com.xxfc.platform.order.biz.inner;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.common.constant.CommonConstants;
import com.xxfc.platform.activity.entity.Coupon;
import com.xxfc.platform.activity.feign.ActivityFeign;
import com.xxfc.platform.app.feign.ConfigFeign;
import com.xxfc.platform.order.biz.BaseOrderBiz;
import com.xxfc.platform.order.biz.OrderAccountBiz;
import com.xxfc.platform.order.biz.OrderItemBiz;
import com.xxfc.platform.order.contant.enumerate.DeductionTypeEnum;
import com.xxfc.platform.order.contant.enumerate.ItemTypeEnum;
import com.xxfc.platform.order.contant.enumerate.OrderStatusEnum;
import com.xxfc.platform.order.contant.enumerate.OrderTypeEnum;
import com.xxfc.platform.order.entity.BaseOrder;
import com.xxfc.platform.order.entity.OrderItem;
import com.xxfc.platform.order.entity.OrderRentVehicleDetail;
import com.xxfc.platform.order.pojo.account.OrderAccountDeduction;
import com.xxfc.platform.order.pojo.account.OrderAccountDetail;
import com.xxfc.platform.order.pojo.calculate.InProgressVO;
import com.xxfc.platform.order.pojo.order.VehicleItemDTO;
import com.xxfc.platform.order.pojo.calculate.OrderRefundPriceVO;
import com.xxfc.platform.order.pojo.order.OrderPageVO;
import com.xxfc.platform.universal.constant.DictionaryKey;
import com.xxfc.platform.universal.entity.Dictionary;
import com.xxfc.platform.universal.feign.ThirdFeign;
import com.xxfc.platform.vehicle.feign.VehicleFeign;
import com.xxfc.platform.vehicle.pojo.dto.VehicleModelCalendarPriceDTO;
import com.xxfc.platform.vehicle.pojo.dto.order.VMCalendarPriceCostDTO;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.github.wxiaoqi.security.common.constant.CommonConstants.SYS_TRUE;
import static com.xxfc.platform.universal.constant.DictionaryKey.*;

/**
 * 订单退款记录表
 *
 * @author zjw
 * @email nishijjo@qq.com
 * @date 2019-06-18 11:08:09
 */
@Slf4j
@Service
public class OrderCalculateBiz {

    @Autowired
    UserFeign userFeign;

    @Autowired
    ThirdFeign thirdFeign;

    @Autowired
    VehicleFeign vehicleFeign;

    @Autowired
    ConfigFeign configFeign;

    @Autowired
    ActivityFeign activityFeign;

    @Autowired
    OrderItemBiz orderItemBiz;

    @Autowired
    OrderAccountBiz orderAccountBiz;

    @Autowired
    BaseOrderBiz baseOrderBiz;

    public InProgressVO inProgressCalculate(BaseOrder baseOrder, VehicleItemDTO vehicleItemDTO, Integer freeDays, Integer useDays, OrderAccountDetail oad, Boolean isCancel) {
        BigDecimal refundAmount = BigDecimal.ZERO;
        BigDecimal consumeAmount = BigDecimal.ZERO;
        BigDecimal topViolateAmount = BigDecimal.ZERO;
        BigDecimal itemChangeAmount = BigDecimal.ZERO;

        //融入日期价格
        List<VehicleModelCalendarPriceDTO> vmcpds = JSONUtil.toList(JSONUtil.parseArray(vehicleItemDTO.getDetail()), VehicleModelCalendarPriceDTO.class);
        List<VMCalendarPriceCostDTO> useAmountList = CollUtil.newArrayList();

        //商品真实价格
        BigDecimal goodsRealAmount = baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount());
        //其他消费金额 = 商品真实价格 - 主要商品真实价格 = （商品价格 - 优惠价格）- 主要商品真实价格
        BigDecimal otherItemRealAmount = goodsRealAmount.subtract(vehicleItemDTO.getRealAmount());

        InProgressVO inProgressVO = new InProgressVO();
        inProgressVO.setUsedDays(useDays);
        inProgressVO.setUsedAmount(vehicleItemDTO.getUsedAmount(useDays));

        //计算：剩余免费天数
        Integer backFreeDays = freeDays - useDays;
        //待返还的免费天数
        List<String> backCouponNos = Lists.newArrayList();
        //剩余天数
        Integer residueDays = vehicleItemDTO.getTotalNum() - useDays;
        //过了出发时间取消订单 ，优先使用免费天数
        if(backFreeDays <= 0) {
            //设置免费天数
            inProgressVO.setUsedfreeDays(freeDays);
            inProgressVO.setUsedFreeDaysAmount(vehicleItemDTO.getFreeAmount(freeDays));

            //消费天数
            Integer consumeDays = useDays - freeDays;
            //如果使用天数 大于 总天数
            if(useDays > vehicleItemDTO.getTotalNum()) {
                    //消费天数
                consumeDays = vehicleItemDTO.getTotalNum() - freeDays;
            }

            //需要扣除订单费用
            //判断是否达到优惠券条件 不符合则返还优惠券
            //计算使用天数的费用
                //融入日期价格
            //consumeAmount = orderItem.getUnitPrice().multiply(new BigDecimal(consumeDays+""));
            for(int i = freeDays; i < (freeDays+ consumeDays); i++) {
                consumeAmount = consumeAmount.add(vmcpds.get(i).getPrice());
            }

            if(StrUtil.isNotBlank(baseOrder.getCouponTickerNos())) {
                List<BigDecimal> couponAmounts = Lists.newArrayList();
                for(String tickerNo : baseOrder.getCouponTickerNos().split(",")) {
                    BigDecimal couponAmount = activityFeign.use(baseOrder.getUserId(), Lists.newArrayList(tickerNo), baseOrder.getNo(), Coupon.CHANNEL_RENT, consumeAmount, ActivityFeign.TYPE_CHECK);
                    if(couponAmount.compareTo(BigDecimal.ZERO) > 0) {
                        //能够使用优惠券，则不返还
                        couponAmounts.add(couponAmount);
                    } else {
                        backCouponNos.add(tickerNo);
                    }
                }

                for(BigDecimal couponAmount : couponAmounts) {
                    consumeAmount = consumeAmount.subtract(couponAmount);
                }
            }

            //设置消费金额
            consumeAmount = consumeAmount.add(otherItemRealAmount);
            inProgressVO.setConsumeAmount(consumeAmount);

            refundAmount = handleConsumeAmount(oad, refundAmount, consumeAmount, goodsRealAmount, inProgressVO);
        }else {
            //设置免费天数
            inProgressVO.setUsedfreeDays(useDays);
            inProgressVO.setUsedFreeDaysAmount(vehicleItemDTO.getFreeAmount(useDays));

            //返回剩余免费天数，返回优惠券，订单款
            //修改bug，不需要提前加， 已在handleConsumeAmount方法中添加
//            refundAmount = refundAmount.add(goodsRealAmount);
//            inProgressVO.setBackFreeDays(backFreeDays);

            //融入日期价格
            Integer trueBackFreeDays = 0;
            for(int i = (freeDays - backFreeDays); i < freeDays; i++) {
                trueBackFreeDays += vmcpds.get(i).getFreeDays();
            }
            inProgressVO.setBackFreeDays(trueBackFreeDays);

            //返回优惠券
            if(StrUtil.isNotBlank(baseOrder.getCouponTickerNos())) {
                //返回所有优惠券
                inProgressVO.setBackCoupons(StrUtil.split(baseOrder.getCouponTickerNos(), ','));
            }

            //设置消费金额 由于返回了优惠券，所以添加优惠券之前减免的金额
            consumeAmount = consumeAmount.add(otherItemRealAmount);
                    //修改bug，消费金额不需要添加优惠券金额
                    //.add(baseOrder.getCouponAmount());

            inProgressVO.setConsumeAmount(consumeAmount);

            refundAmount = handleConsumeAmount(oad, refundAmount, consumeAmount, goodsRealAmount, inProgressVO);

        }

        inProgressVO.setRefundOrderAmount(refundAmount);
        inProgressVO.setBackCoupons(backCouponNos);

        //计算违约金
            //residueDays * 身份价格
        if(residueDays > 0) {
            //设置消耗费用列表
            for(int i = 0; i < useDays; i++) {
                useAmountList.add(BeanUtil.toBean(vmcpds.get(i), VMCalendarPriceCostDTO.class));
            }

            //"{}元/天 x{}天"
            BigDecimal residueAmount = BigDecimal.ZERO;
            for(int i = useDays; i < vehicleItemDTO.getTotalNum(); i++) {
                residueAmount = residueAmount.add(vmcpds.get(i).getPrice());
            }
            String violateDesc = StrUtil.format("{}元", residueAmount.toString());
            if(residueDays >= 2) {
                residueDays = 2;
                residueAmount = BigDecimal.ZERO;
                for(int i = useDays; i < (useDays + residueDays + 1); i++) {
                    residueAmount = residueAmount.add(vmcpds.get(i).getPrice());
                }
                violateDesc += StrUtil.format("（封顶{}元）", residueAmount);
            }
            inProgressVO.setViolateAmount(residueAmount);
            inProgressVO.setViolateDesc(" 提前还车违约金："+ violateDesc);
            OrderAccountDeduction violateDeduction = orderAccountBiz.initDeduction(inProgressVO.getViolateAmount(), violateDesc, DeductionTypeEnum.VIOLATE_ADVANCE, OrderAccountDeduction.ORIGIN_DEPOSIT);
            oad.getDeductions().add(violateDeduction);
        }else if(residueDays < 0 && !isCancel){
            //isCancel 表示是否为取消，取消则不计算延期还车（因为没有出车）
            //设置消耗费用列表
            useAmountList.addAll(Convert.toList(VMCalendarPriceCostDTO.class, vmcpds));
            //如果订单 出发中 或者 已完成 或者定损中
            if(OrderStatusEnum.ORDER_WAIT.getCode().equals(baseOrder.getStatus()) ||
                    OrderStatusEnum.ORDER_FINISH.getCode().equals(baseOrder.getStatus()) ||
                    OrderStatusEnum.ORDER_FIXED_LOSS.getCode().equals(baseOrder.getStatus())) {
                Integer overDays = 0 - residueDays;
                String violateDesc = StrUtil.format(" 延迟{}天", overDays);

                List<VMCalendarPriceCostDTO> overAmountList = orderItemBiz.getOverAmountList(vmcpds.get(vmcpds.size() - 1).getDate(), overDays, vehicleItemDTO.getGoodId(), baseOrder.getUserId());
                BigDecimal overAmount = overAmountList.parallelStream()
                        .map(VMCalendarPriceCostDTO::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add).add(new BigDecimal(2+ ""));

                //超过的天数 价格 * 200%
                inProgressVO.setViolateAmount(overAmount);
                inProgressVO.setViolateDesc(" 延期还车违约金："+ violateDesc);
                OrderAccountDeduction violateDeduction = orderAccountBiz.initDeduction(inProgressVO.getViolateAmount(), violateDesc, DeductionTypeEnum.VIOLATE_DELAY, OrderAccountDeduction.ORIGIN_DEPOSIT);
                oad.getDeductions().add(violateDeduction);
            }
        }
        //设置消耗费用列表
        inProgressVO.setUseAmountList(useAmountList);

        return inProgressVO;
    }

    private BigDecimal handleConsumeAmount(OrderAccountDetail oad, BigDecimal refundAmount, BigDecimal consumeAmount, BigDecimal goodsRealAmount, InProgressVO inProgressVO) {
        if (consumeAmount.compareTo(BigDecimal.ZERO) > 0) {
            //消费金额 大于0 增加额外费用
            OrderAccountDeduction consumeDeduction = orderAccountBiz.initDeduction(consumeAmount, "消费金额", DeductionTypeEnum.CONSUME, OrderAccountDeduction.ORIGIN_ORDER_DEPOSIT);
            oad.getDeductions().add(consumeDeduction);
        }

        //消费金额 小于商品真实的金额 返回钱
        if (consumeAmount.compareTo(goodsRealAmount) <= 0) {
            //设置返回钱
            refundAmount = refundAmount.add(goodsRealAmount.subtract(consumeAmount));
        } else {
            //设置额外扣减（押金里面扣）
            inProgressVO.setExtraAmount(consumeAmount.subtract(goodsRealAmount));
        }
        return refundAmount;
    }

    public InProgressVO inProgressCalculate(BaseOrder baseOrder, Integer useDays) {
        VehicleItemDTO vehicleItemDTO = BeanUtil.toBean(orderItemBiz.selectOne(new OrderItem(){{
            setType(ItemTypeEnum.VEHICLE_MODEL.getCode());
            setOrderId(baseOrder.getId());
        }}), VehicleItemDTO.class);
        Integer freeDays = (null == vehicleItemDTO.getCutNum())?0 :vehicleItemDTO.getCutNum();
        return inProgressCalculate(baseOrder, vehicleItemDTO, freeDays, useDays, new OrderAccountDetail(), Boolean.FALSE);
    }

    public InProgressVO calculateOrderComplete(BaseOrder baseOrder, OrderRentVehicleDetail orvd, OrderAccountDetail oad, VehicleItemDTO vehicleItemDTO, Integer useDays, Boolean isCancel) {
        InProgressVO inProgressVO = inProgressCalculate(baseOrder, vehicleItemDTO, vehicleItemDTO.getCutNum(), useDays, oad, isCancel);

        //定金 - （额外消费金额 + 违约金）
        oad.setDepositAmount(orvd.getDeposit().subtract(inProgressVO.getExtraAmount().add(inProgressVO.getViolateAmount())));
        oad.setOrderAmount(inProgressVO.getRefundOrderAmount());
        oad.setOriginDepositAmount(orvd.getDeposit());
        oad.setOriginOrderAmount(baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount()));
        return inProgressVO;
    }

    /**
     * 计算包含多少天
     * @param startLong
     * @param endLong
     * @return
     */
    public Integer getIncludeDays(Long startLong, Long endLong) {
        Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
        Long hourLong = (60L * 60L * 1000L);
        Long dayLong = hourLong * 24;
        Long bufferLong = Long.valueOf(dictionaryMap.get(APP_ORDER+ "_"+ DictionaryKey.RENT_TIME_BUFFER).getDetail()) * hourLong;

        //计算：使用天数 当前时间 - 开始时间的0时0分0秒
        Long bookTimeLag = endLong - startLong;
        log.info("bookTimeLag {}", new BigDecimal(bookTimeLag + ""));
        log.info("divide {}", new BigDecimal((24 * 60 * 60 * 1000)+ ""));
        Integer bookDays = new BigDecimal(bookTimeLag + "").divide(new BigDecimal(dayLong+ ""), 0, RoundingMode.DOWN).intValue();
        Long excess = bookTimeLag%dayLong;
        if(excess > bufferLong) {
            bookDays += 1;
        }

        if(0 == bookDays) {
            bookDays = 1;
        }
        return bookDays;
    }


    public OrderRefundPriceVO getPriceCalculate (String no, OrderPageVO orderPageVO) {
        //根据no 查订单
        OrderTypeEnum orderTypeEnum = OrderTypeEnum.get(orderPageVO.getType());

        BigDecimal totalRefundAmount = BigDecimal.ZERO;
        BigDecimal totalDeductAmount = BigDecimal.ZERO;
        StringBuilder refundDescBuilder = new StringBuilder("");
        String refundDesc = "";
        InProgressVO inProgressVO = new InProgressVO();
        BigDecimal topAmount = BigDecimal.ZERO;

        switch (orderTypeEnum) {
            case RENT_VEHICLE:

                Long timeLag = orderPageVO.getOrderRentVehicleDetail().getStartTime() - System.currentTimeMillis();

                VehicleItemDTO vehicleItemDTO = BeanUtil.toBean(orderItemBiz.selectOne(new OrderItem(){{
                    setType(ItemTypeEnum.VEHICLE_MODEL.getCode());
                    setOrderId(orderPageVO.getId());
                }}), VehicleItemDTO.class);

                //融入日期价格
                List<VehicleModelCalendarPriceDTO> vmcpds = JSONUtil.toList(JSONUtil.parseArray(vehicleItemDTO.getDetail()), VehicleModelCalendarPriceDTO.class);

                if(timeLag < 0 ) {
                    Integer useDays = getIncludeDays(orderPageVO.getOrderRentVehicleDetail().getStartTime(), DateTime.now().getMillis());
                    OrderAccountDetail oad = new OrderAccountDetail();
                    Integer freeDays = (null == vehicleItemDTO.getCutNum())?0 :vehicleItemDTO.getCutNum();
                    inProgressVO = inProgressCalculate(orderPageVO, vehicleItemDTO, freeDays, useDays, new OrderAccountDetail(), Boolean.FALSE);

                    topAmount = vehicleItemDTO.getTopAmount(useDays);
                    totalDeductAmount = oad.realTotalDeduct();
                    totalRefundAmount = oad.getOrderAmount().add(oad.getDepositAmount());
                    refundDesc = inProgressVO.getViolateDesc();
                }else {

                    String key = RENT_REFUND;

                    BigDecimal deductionAmount = orderAccountBiz.calculateDeduction(vehicleItemDTO.getBuyAmount()
                            , orderPageVO.getOrderRentVehicleDetail().getStartTime() - System.currentTimeMillis()
                            , DictionaryKey.APP_ORDER+ "_"+ key
                            , refundDescBuilder);

                    topAmount = vehicleItemDTO.getTopAmount(0);
                    totalDeductAmount = deductionAmount;
                    totalRefundAmount = orderPageVO.getRealAmount().subtract(deductionAmount);
                    refundDesc = refundDescBuilder.toString();
                }

                break;
            case TOUR:
                //判断是省内还是省外
                String key = TOUR_IN_REFUND;
                if(SYS_TRUE.equals(orderPageVO.getOrderTourDetail().getIsOutside())) {
                    key = TOUR_REFUND;
                }

                OrderItem adultItem = orderItemBiz.selectOne(new OrderItem(){{
                    setType(ItemTypeEnum.TOUR_ADULT.getCode());
                    setOrderId(orderPageVO.getId());
                }});

                OrderItem childItem = orderItemBiz.selectOne(new OrderItem(){{
                    setType(ItemTypeEnum.TOUR_CHILD.getCode());
                    setOrderId(orderPageVO.getId());
                }});

                BigDecimal adultItemAmount = (null == adultItem)? BigDecimal.ZERO: adultItem.getRealAmount();
                BigDecimal childItemAmount = (null == childItem)? BigDecimal.ZERO: childItem.getRealAmount();

                BigDecimal deductionAmount = orderAccountBiz.calculateDeduction(adultItemAmount.add(childItemAmount)
                        , orderPageVO.getOrderTourDetail().getStartTime() - System.currentTimeMillis()
                        , DictionaryKey.APP_ORDER+ "_"+ key
                        , refundDescBuilder);

                topAmount = adultItemAmount.add(childItemAmount);
                totalDeductAmount = deductionAmount;
                totalRefundAmount = orderPageVO.getRealAmount().subtract(deductionAmount);
                refundDesc = refundDescBuilder.toString();
                break;
            default:
                break;
        }

        OrderRefundPriceVO orpv = new OrderRefundPriceVO();
        orpv.setRealAmount(orderPageVO.getRealAmount());
        orpv.setRefundAmount(totalRefundAmount);
        orpv.setCutAmount(totalDeductAmount);
        orpv.setTopAmount(topAmount);
        return orpv;
    }

    public static void main(String[] args) {
        System.out.println(DateTime.now().getMillis());
    }
}