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 com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.admin.feign.dto.AppUserDTO;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.util.process.ResultCode;
import com.xxfc.platform.activity.feign.ActivityFeign;
import com.xxfc.platform.order.biz.*;
import com.xxfc.platform.order.contant.enumerate.*;
import com.xxfc.platform.order.entity.*;
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.mq.OrderMQDTO;
import com.xxfc.platform.order.pojo.order.VehicleItemDTO;
import com.xxfc.platform.order.pojo.price.CancelNoStartVO;
import com.xxfc.platform.order.pojo.price.CancelStartedVO;
import com.xxfc.platform.order.pojo.price.CostDetailExtend;
import com.xxfc.platform.tour.feign.TourFeign;
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.common.RestResponse;
import com.xxfc.platform.vehicle.feign.VehicleFeign;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
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.HashMap;
import java.util.LinkedList;
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 OrderCancelBiz {

    @Autowired
    BaseOrderBiz baseOrderBiz;

    @Autowired
    OrderItemBiz orderItemBiz;

    @Autowired
    OrderMemberDetailBiz orderMemberDetailBiz;

    @Autowired
    OrderTourDetailBiz orderTourDetailBiz;

    @Autowired
    OrderRentVehicleBiz orderRentVehicleBiz;

    @Autowired
    OrderMsgBiz orderMsgBiz;

    @Autowired
    OrderCalculateBiz orderCalculateBiz;

    @Autowired
    OrderAccountBiz orderAccountBiz;

    @Autowired
    OrderTemplateBiz orderTemplateBiz;

    @Autowired
    UserFeign userFeign;

    @Autowired
    VehicleFeign vehicleFeign;

    @Autowired
    TourFeign tourFeign;

    @Autowired
    ThirdFeign thirdFeign;

    @Autowired
    ActivityFeign activityFeign;

    private static Map<Integer, List<Integer>> cancelAble;
    static {
        cancelAble = new HashMap<Integer, List<Integer>>();
        cancelAble.put(OrderTypeEnum.RENT_VEHICLE.getCode(), new LinkedList<Integer>(){{
            add(OrderStatusEnum.ORDER_UNPAY.getCode());
            add(OrderStatusEnum.ORDER_TOSTART.getCode());
        }});
        cancelAble.put(OrderTypeEnum.TOUR.getCode(), new LinkedList<Integer>(){{
            add(OrderStatusEnum.ORDER_UNPAY.getCode());
            add(OrderStatusEnum.ORDER_TOSTART.getCode());
        }});
        cancelAble.put(OrderTypeEnum.MEMBER.getCode(), new LinkedList<Integer>(){{
            add(OrderStatusEnum.ORDER_UNPAY.getCode());
        }});
    }

    /**
     * 取消订单
     * @param baseOrder
     */
    @Transactional
    public void cancel(BaseOrder baseOrder, BigDecimal changeViolateAmount) {
        OrderRentVehicleDetail orvd = new OrderRentVehicleDetail();
        OrderTourDetail otd = new OrderTourDetail();
        OrderMemberDetail omd = new OrderMemberDetail();
        if(cancelAble.get(baseOrder.getType()).contains(baseOrder.getStatus())) {
            BaseOrder updateOrder = new BaseOrder(){{
                setId(baseOrder.getId());
                setStatus(OrderStatusEnum.ORDER_CANCEL.getCode());
                setCancelReason(baseOrder.getCancelReason());
                setVersion(baseOrder.getVersion());
            }};
            BeanUtils.copyProperties(baseOrderBiz.updateSelectiveByIdReT(updateOrder), baseOrder);;
            InProgressVO inProgressVO = null;
            OrderAccountDetail oad = new OrderAccountDetail();

            //触发退款流程
            //判断是否已支付
            if(SYS_TRUE.equals(baseOrder.getHasPay())) {

                //缓冲取消时间(一个小时)
                Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
                Long autoCancelTime = Long.valueOf(dictionaryMap.get(APP_ORDER+ "_"+ DictionaryKey.CANCEL_TIME_BUFFER).getDetail());
                autoCancelTime = autoCancelTime * 60L * 1000L;

                //判断是否支付超过缓冲取消时间(一个小时)
                Long orderCrtLag = System.currentTimeMillis() - baseOrder.getPayTime();
                Boolean overCancelBuffer = (orderCrtLag > autoCancelTime)? Boolean.TRUE: Boolean.FALSE;

                //判断订单类型
                if(OrderTypeEnum.RENT_VEHICLE.getCode().equals(baseOrder.getType())) {
                    orvd = orderRentVehicleBiz.selectOne(new OrderRentVehicleDetail(){{
                        setOrderId(baseOrder.getId());
                    }});

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

                    //如果超过出发时间，不能取消订单
                    //根据时间处理goodsAmount
                    //获取出发时间 到现在 距离时间
                    Long timeLag = orvd.getStartTime() - System.currentTimeMillis();

                    if(timeLag < 0 && overCancelBuffer) {
                        Integer useDays = orderCalculateBiz.getIncludeDays(orvd.getStartTime(), System.currentTimeMillis());
                        if(useDays > orvd.obtainRealDayNum()) {
                            useDays = orvd.obtainRealDayNum();
                        }
                        inProgressVO = orderCalculateBiz.calculateOrderComplete(baseOrder, orvd, oad, vehicleItemDTO, useDays, Boolean.TRUE);

                        //判断是否修改违约金
                        if(null != changeViolateAmount) {
                            oad.changeCancelViolate(changeViolateAmount);
                        }

                        //结合
                        //退款子流程: 订单基础，退款描述，退款金额
                        orderAccountBiz.refundSubProcess(baseOrder, "", baseOrder.getRealAmount(), oad.getDepositAmount().add(oad.getOrderAmount()), AccountTypeEnum.OUT_ORDER_FUND.getCode(), RefundStatusEnum.ALL.getCode(), oad);


                        //生成额外的费用明细
                        CancelStartedVO csv = new CancelStartedVO();
                        csv.setType(CostDetailExtend.CANCEL_STARTED);
                        csv.setConsumeAmount(inProgressVO.getConsumeAmount());
                        csv.setCouponAmount(inProgressVO.getCouponAmount());
                        csv.setCouponDesc(inProgressVO.getCouponDesc());
                        csv.setModelAmount(vehicleItemDTO.getUnitPrice());
                        csv.setUsedAmount(inProgressVO.getUsedAmount());
                        csv.setUsedDayNum(inProgressVO.getUsedDays());
                        csv.setUsedfreeDayNum(inProgressVO.getUsedfreeDays());
                        csv.setUsedFreeAmount(inProgressVO.getUsedFreeDaysAmount());
                        csv.setHadConpon((StrUtil.isNotBlank(baseOrder.getCouponTickerNos())? Boolean.TRUE: Boolean.FALSE));

                        csv.setViolateAmount(inProgressVO.getViolateAmount());
                        csv.setViolateDesc(inProgressVO.getViolateDesc());
                        csv.setViolateAmountList(inProgressVO.getViolateAmountList());

                        csv.setAbleUsedDayNum(inProgressVO.getUseAmountList().size());
                        csv.setSameUseUnitPriceFromList(inProgressVO.getUseAmountList());
                        csv.setSameOverUnitPriceFromList(inProgressVO.getOverAmountList());
                        csv.setUseAmountList(inProgressVO.getUseAmountList());
                        csv.setOverAmountList(inProgressVO.getOverAmountList());

                        //如果有扣款项，则生成额外的费用明细
                        // inProgressVO已经包含 ViolateAmount ViolateDesc
//                        if(oad.getDeductions().size() > 0) {
//                            for(OrderAccountDeduction deduction : oad.getDeductions()) {
//                                if(DeductionTypeEnum.VIOLATE_ADVANCE.getCode().equals(deduction.getType())) {
//                                    csv.setViolateAmount(deduction.getAmount());
//                                    csv.setViolateDesc(deduction.getName());
//                                }
//                            }
//                        }

                        //设置订单数据
                        baseOrder.setViolateAmount(csv.getViolateAmount());
                        baseOrder.setExtraAmount(inProgressVO.getExtraAmount());

                        //最后设置DeductionList
                        csv.setDeductionList(oad.getDeductions());
                        csv.initParamJson();
                        //orderTemplateBiz.handleCostDetailExtend(csv);
                        orvd.handelCostDetailExtend(csv);
                        orderRentVehicleBiz.updateSelectiveByIdRe(orvd);

                        //根据商品消费金额 设置changeAmount
                        vehicleItemDTO.setChangeAmount(inProgressVO.getConsumeAmount().subtract(vehicleItemDTO.getRealAmount()));
                        orderItemBiz.updateSelectiveByIdRe(vehicleItemDTO);
                    }else {
//                      //没到出车时间

                        //判断是否修改违约金
                        if(null != changeViolateAmount) {
                            oad.changeCancelViolate(changeViolateAmount);
                        }

                        //退款流程
                            //违约金封顶 租车身份价 * 2天
                        orderAccountBiz.rentRefundProcessCancel(baseOrder, BigDecimal.ZERO, timeLag, APP_ORDER+ "_"+ RENT_REFUND, orvd.getDeposit(), vehicleItemDTO.getBuyAmount(), oad, vehicleItemDTO.getTopAmount(0), null, changeViolateAmount, (!overCancelBuffer));

                        //设置订单数据
                        //baseOrder.setDamagesAmount(csv.getDamagesAmount());
                        //baseOrder.setExtraAmount(inProgressVO.getExtraAmount());
                        baseOrder.setBackCoupon(baseOrder.getCouponTickerNos());

                        //如果有扣款项，则生成额外的费用明细
                        if(oad.getDeductions().size() > 0) {
                            for(OrderAccountDeduction deduction : oad.getDeductions()) {
                                if(DeductionTypeEnum.VIOLATE_CANCEL.getCode().equals(deduction.getType())) {
                                    CancelNoStartVO cnsv = new CancelNoStartVO(){{
                                        setViolateAmount(deduction.getAmount());
                                        setViolateDesc(deduction.getName());

                                        //设置违约金日期费用列表
                                        setViolateAmountList(vehicleItemDTO.getVehicleDetail());
                                        setSameViolateUnitPriceFromList(vehicleItemDTO.getVehicleDetail());
                                    }};

                                    //设置订单数据
                                    baseOrder.setViolateAmount(cnsv.getViolateAmount());

                                    cnsv.initParamJson();
                                    //orderTemplateBiz.handleCostDetailExtend(cnsv);
                                    orvd.handelCostDetailExtend(cnsv);
                                    orderRentVehicleBiz.updateSelectiveByIdRe(orvd);
                                    break;
                                }
                            }
                        }
                    }

                    //已付款的取消订单发送消息
                    try {
                        AppUserDTO appUserDTO = userFeign.userDetailById(baseOrder.getUserId()).getData();
                        //处理后台用户提醒短信的发送
                        orderMsgBiz.handelMsgCancel(orvd, otd, omd, baseOrder, appUserDTO);
                    }catch (Exception e) {
                        log.error(e.getMessage(), e);
                    }

                }else if (OrderTypeEnum.TOUR.getCode().equals(baseOrder.getType())) {

                    otd = orderTourDetailBiz.selectOne(new OrderTourDetail(){{
                        setOrderId(baseOrder.getId());
                    }});

                    Long timeLag = otd.getStartTime() - System.currentTimeMillis();

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

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

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

                    //判断是省内还是省外
                    String key = TOUR_IN_REFUND;
                    if(SYS_TRUE.equals(otd.getIsOutside())) {
                        key = TOUR_REFUND;
                    }

                    //退款流程
                    orderAccountBiz.rentRefundProcessCancel(baseOrder, adultItemAmount.add(childItemAmount), timeLag, APP_ORDER+ "_"+ key, BigDecimal.ZERO, BigDecimal.ZERO, oad, adultItemAmount.add(childItemAmount), changeViolateAmount, null, (!overCancelBuffer));

                    //如果有扣款项，则生成额外的费用明细
                    if(oad.getDeductions().size() > 0) {
                        for(OrderAccountDeduction deduction : oad.getDeductions()) {
                            if(DeductionTypeEnum.VIOLATE_CANCEL.getCode().equals(deduction.getType())) {
                                CancelNoStartVO cnsv = new CancelNoStartVO(){{
                                    setViolateAmount(deduction.getAmount());
                                    setViolateDesc(deduction.getName());
                                }};

                                //设置订单数据
                                baseOrder.setViolateAmount(cnsv.getViolateAmount());

                                //orderTemplateBiz.handleCostDetailExtend(cnsv);
                                otd.handelCostDetailExtend(cnsv);
                                orderTourDetailBiz.updateSelectiveByIdRe(otd);
                            }
                        }
                    }

                    //站点总人数减少
                    tourFeign.updateTourGoodPersonNum(otd.getVerificationId(), TourFeign.TOTAL_PERSON, (otd.getTotalNumber() * -1));

                    //已付款的取消订单发送消息
                    try {
                        AppUserDTO appUserDTO = userFeign.userDetailById(baseOrder.getUserId()).getData();
                        //处理后台用户提醒短信的发送
                        orderMsgBiz.handelMsgCancel(orvd, otd, omd, baseOrder, appUserDTO);
                    }catch (Exception e) {
                        log.error(e.getMessage(), e);
                    }
                }
            }

            //处理取消流程
            if(OrderTypeEnum.RENT_VEHICLE.getCode().equals(baseOrder.getType())) {
                orvd = orderRentVehicleBiz.selectOne(new OrderRentVehicleDetail(){{
                    setOrderId(baseOrder.getId());
                }});

                //取消租车预定 和 记录生成额外的费用明细
                //已支付,并且是待出行状态，取消预约
                if(OrderStatusEnum.ORDER_CANCEL.getCode().equals(baseOrder.getStatus()) && SYS_TRUE.equals(baseOrder.getHasPay())){
                    vehicleFeign.rentUnbookVehicle(orvd.getBookRecordId());

                }else {
                    //未支付，拒绝之前的预约
                    RestResponse<Integer> restResponse = vehicleFeign.rentRejectVehicleBooking(orvd.getBookRecordId());
                }

                //取消租车免费天数使用
                if(null != orvd.getFreeDays() && orvd.getFreeDays() > 0) {
                    Integer freeDays = (null == inProgressVO) ? orvd.getFreeDays(): inProgressVO.getBackFreeDays();
                    int result = userFeign.memberDays(baseOrder.getUserId(), freeDays, UserFeign.MEMBER_DAYS_WITHDRAW);
                    if(result < 0) {
                        throw new BaseException(ResultCode.FAILED_CODE);
                    }
                    orvd.setBackFreeDays(freeDays);
                    orderRentVehicleBiz.updateSelectiveById(orvd);
                }

            }else if(OrderTypeEnum.TOUR.getCode().equals(baseOrder.getType())) {
                otd = orderTourDetailBiz.selectOne(new OrderTourDetail(){{
                    setOrderId(baseOrder.getId());
                }});

                //增加库存
                tourFeign.stock(otd.getSpePriceId(), otd.getTotalNumber(), TourFeign.STOCK_PLUS);
            }

            //返还优惠券
            if(StrUtil.isNotBlank(baseOrder.getCouponTickerNos())) {
                List<String> backCoupons = (null == inProgressVO)? Convert.convert(List.class, baseOrder.getCouponTickerNos().split(",")): inProgressVO.getBackCoupons();
                //设置订单参数
                baseOrder.setBackCoupon(CollUtil.join(backCoupons, ","));

                for(String backCoupon : backCoupons) {
                    activityFeign.cancelUse(backCoupon);
                }
            }

            //更新baseOrder
            BeanUtil.copyProperties(baseOrderBiz.updateSelectiveByIdReT(baseOrder), baseOrder);

            //发送队列消息
            baseOrderBiz.sendOrderMq(orvd, otd, omd, baseOrder, OrderMQDTO.ORDER_CANCEL);
        }else {
            throw new BaseException(ResultCode.FAILED_CODE);
        }
    }

//    public static void main(String[] args) {
//        Integer aaa = new BigDecimal(140119562.00 + "").divide(new BigDecimal((24 * 60 * 60 * 1000)+ ""), 0, RoundingMode.UP).intValue();
//        System.out.println(aaa);
//    }
}