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

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.price.CancelNoStartVO;
import com.xxfc.platform.order.pojo.price.CancelStartedVO;
import com.xxfc.platform.tour.feign.TourFeign;
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.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.weekend.WeekendSqls;

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

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
    OrderRefundBiz orderRefundBiz;

    @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) {
        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());
            }};
            BaseOrder hasUpdateOrder = baseOrderBiz.updateSelectiveByIdReT(updateOrder);
            InProgressVO inProgressVO = null;
            OrderAccountDetail oad = new OrderAccountDetail();

            //触发退款流程
            //判断是否已支付
            if(SYS_TRUE.equals(baseOrder.getHasPay())) {
                //判断订单类型
                if(OrderTypeEnum.RENT_VEHICLE.getCode().equals(baseOrder.getType())) {
                    orvd = orderRentVehicleBiz.selectOne(new OrderRentVehicleDetail(){{
                        setOrderId(baseOrder.getId());
                    }});

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

//                    List<OrderItem> otherOrderItems = orderItemBiz.selectByExample(new Example.Builder(OrderItem.class)
//                        .where(WeekendSqls.<OrderItem>custom().andNotEqualTo(OrderItem::getType, ItemTypeEnum.VEHICLE_MODEL.getCode())).build()
//                    );

                    //原退还押金
                    Integer freeDays = (null == orderItem.getCutNum())?0 :orderItem.getCutNum();
                    BigDecimal freeDayAmount = BigDecimal.ZERO;


                    //如果超过出发时间，不能取消订单
                    //根据时间处理goodsAmount
                    //获取出发时间 到现在 距离时间
                    Long timeLag = orvd.getStartTime() - System.currentTimeMillis();
                    if(timeLag < 0 ) {
                        //开始时间当天时间戳
                        Long beginOfStartDay = DateUtil.beginOfDay(DateUtil.date(orvd.getStartTime())).getTime();
                        //计算：使用天数 当前时间 - 开始时间的0时0分0秒
                        Long useTimeLag = System.currentTimeMillis() - beginOfStartDay;
                        Integer useDays = new BigDecimal(useTimeLag + "").divide(new BigDecimal((24 * 60 * 60 * 1000)+ "")).setScale(0, BigDecimal.ROUND_UP).intValue();
                        inProgressVO = orderCalculateBiz.calculateOrderComplete(baseOrder, orvd, oad, orderItem, useDays);
                        //结合
                        //退款子流程: 订单基础，退款描述，退款金额
                        orderAccountBiz.refundSubProcess(baseOrder, "", baseOrder.getRealAmount().subtract(orvd.getDeposit()), oad.getDepositAmount().add(oad.getOrderAmount()), AccountTypeEnum.OUT_ORDER_FUND.getCode(), RefundStatusEnum.ALL.getCode(), oad);


                        //生成额外的费用明细
                        CancelStartedVO csv = new CancelStartedVO();
                        csv.setConsumeAmount(inProgressVO.getConsumeAmount());
                        csv.setModelAmount(orderItem.getUnitPrice());
                        csv.setUsedAmount(inProgressVO.getUsedAmount());
                        csv.setUsedDayNum(inProgressVO.getUsedDays());
                        csv.setUsedFreeAmount(inProgressVO.getUsedFreeDaysAmount());
                        csv.setHadConpon((StrUtil.isNotBlank(baseOrder.getCouponTickerNos())? Boolean.TRUE: Boolean.FALSE));
                        //如果有扣款项，则生成额外的费用明细
                        if(oad.getDeductions().size() > 0) {
                            for(OrderAccountDeduction deduction : oad.getDeductions()) {
                                if(DeductionTypeEnum.VIOLATE_CANCEL.getCode().equals(deduction.getType())) {
                                    csv.setViolateAmount(deduction.getAmount());
                                    csv.setViolateDesc(deduction.getName());
                                }
                            }
                        }

                        csv.initParamJson();
                        //orderTemplateBiz.handleCostDetailExtend(csv);
                        orvd.handelCostDetailExtend(csv);
                        orderRentVehicleBiz.updateSelectiveByIdRe(orvd);
                    }else {
                        //没到出车时间
                        //判断是否使用免费天数，并且进行扣款
                        if(freeDays > 0) {
                            freeDayAmount = orderItem.getUnitPrice().multiply(new BigDecimal(orvd.getFreeDays()+ ""));

                            //违约金封顶 租车身份价 * 2天
                            BigDecimal topAmount = orderItem.getUnitPrice().multiply(new BigDecimal(2+ ""));
                            if(freeDayAmount.compareTo(topAmount) > 0) {
                                freeDayAmount = topAmount;
                            }
                        }
                        //退款流程
                        orderAccountBiz.rentRefundProcessCancel(hasUpdateOrder, orderItem.getRealAmount(), timeLag, APP_ORDER+ "_"+ RENT_REFUND, orvd.getDeposit(), freeDayAmount, oad);

                        //如果有扣款项，则生成额外的费用明细
                        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());
                                    }};

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

                    //已付款的取消订单发送消息
                    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());
                    }});

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

                    //退款流程
                    orderAccountBiz.rentRefundProcessCancel(hasUpdateOrder, adultItem.getRealAmount().add(childItem.getRealAmount()), timeLag, APP_ORDER+ "_"+ key, BigDecimal.ZERO, BigDecimal.ZERO, oad);

                    //如果有扣款项，则生成额外的费用明细
                    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());
                                }};

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

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

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

                //取消租车预定 和 记录生成额外的费用明细
                //已支付,并且是待出行状态，取消预约
                if(OrderStatusEnum.ORDER_TOSTART.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);
                    }
                }

                //返还优惠券
                if(StrUtil.isNotBlank(baseOrder.getCouponTickerNos())) {
                    List<String> backCoupons = (null == inProgressVO)? inProgressVO.getBackCoupons(): Convert.convert(List.class, baseOrder.getCouponTickerNos().split(","));
                    for(String backCoupon : backCoupons) {
                        activityFeign.cancelUse(backCoupon);
                    }
                }
            }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);
            }

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