package com.xxfc.platform.order.biz;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.github.wxiaoqi.security.admin.dto.UserMemberDTO;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.admin.feign.dto.AppUserDTO;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.util.IntervalUtil;
import com.github.wxiaoqi.security.common.util.process.ResultCode;
import com.google.common.collect.Lists;
import com.xxfc.platform.activity.feign.ActivityFeign;
import com.xxfc.platform.order.biz.inner.OrderMsgBiz;
import com.xxfc.platform.order.contant.enumerate.OrderStatusEnum;
import com.xxfc.platform.order.contant.enumerate.OrderTypeEnum;
import com.xxfc.platform.order.contant.enumerate.RefundStatusEnum;
import com.xxfc.platform.order.contant.enumerate.RefundTypeEnum;
import com.xxfc.platform.order.entity.*;
import com.xxfc.platform.order.mapper.BaseOrderMapper;
import com.xxfc.platform.order.pojo.mq.OrderMQDTO;
import com.xxfc.platform.order.pojo.order.OrderListVo;
import com.xxfc.platform.order.pojo.order.OrderPageVO;
import com.xxfc.platform.order.pojo.order.OrderVehicleCrosstownDto;
import com.xxfc.platform.tour.feign.TourFeign;
import com.xxfc.platform.universal.feign.MQSenderFeign;
import com.xxfc.platform.universal.feign.ThirdFeign;
import com.xxfc.platform.universal.vo.OrderRefundVo;
import com.xxfc.platform.vehicle.common.RestResponse;
import com.xxfc.platform.vehicle.entity.Vehicle;
import com.xxfc.platform.vehicle.entity.VehicleUserLicense;
import com.xxfc.platform.vehicle.feign.VehicleFeign;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.jexl2.MapContext;
import org.apache.commons.lang3.StringUtils;
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 static com.github.wxiaoqi.security.common.constant.CommonConstants.SYS_FALSE;
import static com.github.wxiaoqi.security.common.constant.CommonConstants.SYS_TRUE;
import static com.xxfc.platform.universal.constant.DictionaryKey.*;
import static com.github.wxiaoqi.security.common.config.rabbit.RabbitConstant.*;

/**
 * 
 *
 * @author zjw
 * @email nishijjo@qq.com
 * @date 2019-05-15 21:30:33
 */
@Service
@Slf4j
public class BaseOrderBiz extends BaseBiz<BaseOrderMapper,BaseOrder> {

    @Autowired
    OrderMemberDetailBiz orderMemberDetailBiz;

    @Autowired
    OrderTourDetailBiz orderTourDetailBiz;

    @Autowired
    OrderRentVehicleBiz orderRentVehicleBiz;

    @Autowired
    OrderVehicalCrosstownBiz orderVehicalCrosstownBiz;

    @Autowired
    OrderUserLicenseBiz orderUserLicenseBiz;

    @Autowired
    OrderRefundBiz orderRefundBiz;

    @Autowired
    VehicleFeign vehicleFeign;

    @Autowired
    ThirdFeign thirdFeign;

    @Autowired
    TourFeign tourFeign;

    @Autowired
    UserFeign userFeign;

    @Autowired
    MQSenderFeign mqSenderFeign;

    @Autowired
    OrderMsgBiz orderMsgBiz;

    @Autowired
    ActivityFeign activityFeign;

    public List<OrderPageVO> pageByParm(Map<String, Object> paramMap){
        return mapper.pageByParm(paramMap);
    }

    public List<OrderListVo> listOrder(Map<String, Object> paramMap){

        return mapper.listOrder(paramMap);
    }
    public List<OrderPageVO> getRentVehicle (Map<String, Object> paramMap) {
        return mapper.getRentVehicle(paramMap);
    }
    public List<OrderPageVO> getTourList (Map<String, Object> paramMap) {
        return mapper.getTourList(paramMap);
    }
    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 no
     * @return
     */
    public ObjectRestResponse getOrderDetail(String no) {
        OrderPageVO orderPageVo = mapper.getOrderDetail(no);
        if(orderPageVo == null) {
            return ObjectRestResponse.createDefaultFail();
        }

        if(orderPageVo.getStatus() == 4) { //未交车
           List<VehicleUserLicense> vehicleUserLicenses = Lists.newArrayList();

            boolean flag = getTodyTime(orderPageVo.getOrderRentVehicleDetail().getStartTime());
            if(!flag) {
                return ObjectRestResponse.createFailedResult(3501, "今日不是交车日期");
            }
           String myDriverIds = orderPageVo.getOrderRentVehicleDetail().getMyDriverIds();
           if(StringUtils.isNotBlank(myDriverIds)) {
               if(myDriverIds.contains(",")) {
                   String[] ids = myDriverIds.split(",");
                   try{
                       //目前暂定一个驾驶人，后期需要更改再此更改
                       RestResponse<VehicleUserLicense> restResponse = vehicleFeign.getOne(Integer.parseInt(ids[0]));
                       log.info(restResponse.getMessage());
                       if(restResponse.getData() != null) {
                           vehicleUserLicenses.add(restResponse.getData());
                       }
                   }catch (Exception e) {
                       e.printStackTrace();
                   }
               } else {
                   try {
                       vehicleUserLicenses.add(vehicleFeign.getOne(Integer.parseInt(myDriverIds.trim())).getData());
                   }catch (Exception e){
                       e.printStackTrace();
                   }
               }
           }
           orderPageVo.setVehicleUserLicenses(vehicleUserLicenses);
       } else if(orderPageVo.getStatus() >= 5) {//出行中 已交车
            OrderVehicleCrosstownDto orderVehicleCrosstownDto = new OrderVehicleCrosstownDto();
            orderVehicleCrosstownDto.setOrderId(orderPageVo.getId());
            orderVehicleCrosstownDto.setOrderNo(orderPageVo.getNo());
            List<OrderVehicleCrosstown> orderVehicleCrosstowns = orderVehicalCrosstownBiz.selectByOrderId(orderVehicleCrosstownDto);
            if(orderVehicleCrosstowns != null && orderVehicleCrosstowns.size() > 0) {
                List<OrderUserLicense> orderUserLicenses = orderUserLicenseBiz.selectByIds(orderVehicleCrosstowns.get(0).getUserLicenseId()).getData();
                if(orderUserLicenses != null && orderUserLicenses.size() > 0) {
                    orderVehicleCrosstownDto.setLicenseIdCard(orderUserLicenses.get(0).getLicenseIdCard());
                    orderVehicleCrosstownDto.setLicenseImg(orderUserLicenses.get(0).getLicenseImg());
                    orderVehicleCrosstownDto.setLicenseName(orderUserLicenses.get(0).getLicenseName());
                    orderVehicleCrosstownDto.setLicensePhone(orderUserLicenses.get(0).getLicensePhone());
                }

            }
            orderPageVo.setOrderVehicleCrosstownDto(orderVehicleCrosstownDto);
        }
        RestResponse<Vehicle> restResponse = vehicleFeign.findById(orderPageVo.getOrderRentVehicleDetail().getVehicleId());
        if(restResponse.getData() != null) {
            orderPageVo.setVehicalNumberPlat(restResponse.getData().getNumberPlate());
        }
        return ObjectRestResponse.succ(orderPageVo);
    }

    /**
     * 取消订单
     * @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 = this.updateSelectiveByIdReT(updateOrder);

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

                    //如果超过出发时间，不能取消订单
                    //根据时间处理goodsAmount
                    //获取出发时间 到现在 距离时间
                    Long timeLag = orvd.getStartTime() - System.currentTimeMillis();
                    if(timeLag < 0) {
                        throw new BaseException(ResultCode.FAILED_CODE, new HashSet<String>(){{
                            add("已超过出发时间，不能取消订单");
                        }});
                    }

                    //退款流程
                    rentRefundProcess(hasUpdateOrder, orvd.getDeposit(), timeLag, APP_ORDER+ "_"+ RENT_REFUND);

                    //已付款的取消订单发送消息
                    try {
                        AppUserDTO appUserDTO = userFeign.userDetailById(baseOrder.getUserId()).getData();
                        //处理后台用户提醒短信的发送
                        orderMsgBiz.handelBgUserMsg4Pay(orvd, baseOrder, appUserDTO, OrderMsgBiz.RENT_CANCEL);
                    }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();

                    //判断是省内还是省外
                    String key = TOUR_IN_REFUND;
                    if(SYS_TRUE.equals(otd.getIsOutside())) {
                        key = TOUR_REFUND;
                    }
                        //退款流程
                    rentRefundProcess(hasUpdateOrder, timeLag, APP_ORDER+ "_"+ key);
                }
            }

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

                //取消租车预定
                    //已支付,并且是待出行状态，取消预约
                if(OrderStatusEnum.ORDER_TOSTART.equals(baseOrder.getType()) && SYS_TRUE.equals(baseOrder.getHasPay())){
                    vehicleFeign.unbookVehicle(orvd.getBookRecordId());
                }else {
                    //未支付，拒绝之前的预约
                    RestResponse<Integer> restResponse = vehicleFeign.rentRejectVehicleBooking(orvd.getBookRecordId());
                }

                //取消租车免费天数使用
                if(null != orvd.getFreeDays() && orvd.getFreeDays() > 0) {
                    int result = userFeign.memberDays(baseOrder.getUserId(), orvd.getFreeDays(), UserFeign.MEMBER_DAYS_WITHDRAW);
                    if(result < 0) {
                        throw new BaseException(ResultCode.FAILED_CODE);
                    }
                }
            }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);
            }

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

    /**
     * 租车退款流程(不含押金的通用方法)
     * @param baseOrder
     * @param startTime
     * @param dicParentKey
     */
    private void rentRefundProcess(BaseOrder baseOrder, Long startTime, String dicParentKey){
        rentRefundProcess(baseOrder, BigDecimal.ZERO, startTime, dicParentKey);
    }

    /**
     * 租车（包括旅游）退款流程(含押金)
     * @param baseOrder
     * @param depositAmount
     * @param timeLag 与开始时间的时间差
     * @param dicParentKey
     */
    private void rentRefundProcess(BaseOrder baseOrder, BigDecimal depositAmount, Long timeLag, String dicParentKey) {
        //计算退款金额
            // 1、押金 + 租金(规则扣除)
        //BigDecimal refundGoodsAmount = baseOrder.getGoodsAmount();
        String refundDesc = "取消订单退款:";
        if(null == depositAmount) {
            depositAmount = BigDecimal.ZERO;
        }
        //商品价格 - 优惠券减免的价格
        BigDecimal refundGoodsAmount = calculateRefund(baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount()), timeLag, dicParentKey, refundDesc);

        //退款金额
        BigDecimal refundAmount = depositAmount.add(refundGoodsAmount);
        //退款子流程: 订单基础，退款描述，退款金额
        refundSubProcess(baseOrder, refundDesc, refundAmount, RefundTypeEnum.RentVehicle.getCode(), RefundStatusEnum.ALL.getCode());
    }

    public BigDecimal calculateRefund(BigDecimal goodsAmount, Long timeLag, String dicParentKey, String refundDesc) {
        BigDecimal refundGoodsAmount = goodsAmount;

        //根据时间处理goodsAmount
        //获取出发时间 到现在 距离时间
        Integer hourLag = Long.valueOf(timeLag/(1000L * 60L * 60L)).intValue();

        Map<String, com.xxfc.platform.universal.entity.Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
        Set<com.xxfc.platform.universal.entity.Dictionary> rentRefunds = dictionaryMap.get(dicParentKey).getChildrens();

        for(com.xxfc.platform.universal.entity.Dictionary dic : rentRefunds) {
            if(StrUtil.isBlank(dic.getName())) {
                continue;
            }
            String[] names = dic.getName().split("\\|");
            if(names.length < 2) {
                continue;
            }

            //符合范围
            if(IntervalUtil.staticIsInTheInterval(hourLag.toString(), names[0])){
                refundGoodsAmount = new BigDecimal((IntervalUtil.evaluate(dic.getDetail(), new MapContext(){{
                    //ga : goodsAmount
                    set("ga", goodsAmount);
                }})).toString());
                refundDesc = names[1]+ ","+ refundDesc;
                break;
            }
        }

        return refundGoodsAmount;
    }

    /**
     * 退款子流程
     * @param baseOrder
     * @param refundDesc
     * @param refundAmount
     * @param refundType
     * @param refundStatus
     */
    public void refundSubProcess(BaseOrder baseOrder, String refundDesc, BigDecimal refundAmount, Integer refundType, Integer refundStatus) {
        OrderRefundVo orv = new OrderRefundVo(){{
            setAmount(baseOrder.getRealAmount().multiply(new BigDecimal("100")).intValue());
            setOrderNo(baseOrder.getNo());
        }};
        orv.setRefundDesc(refundDesc+ refundAmount.toString());
        orv.setRefundAmount(refundAmount.multiply(new BigDecimal("100")).intValue());
        String refundTradeNo = thirdFeign.refund(orv).getData();

        //记录订单退款记录
        Integer flag = addOrderRefund(baseOrder.getId(), refundDesc, refundAmount, refundTradeNo, refundType);

        //更新订单的退款状态和退款时间
        if(SYS_TRUE.equals(flag) && null != refundStatus) {
            updateSelectiveByIdReT(new BaseOrder(){{
                setId(baseOrder.getId());
                setRefundStatus(refundStatus);
                setRefundTime(System.currentTimeMillis());
                setVersion(baseOrder.getVersion());
            }});
        }
    }

    /**
     * 记录订单退款记录
     * @param orderId
     * @param refundDesc
     * @param refundAmount
     * @param refundTradeNo
     */
    private Integer addOrderRefund(Integer orderId, String refundDesc, BigDecimal refundAmount, String refundTradeNo, Integer refundType) {
        //如果返回的流水为空，则当做失败
        Integer refundStatus = SYS_TRUE;
        if(StrUtil.isBlank(refundTradeNo)) {
            refundStatus = SYS_FALSE;
        }

        //创建订单退款记录
        OrderRefund orderRefund = new OrderRefund(){{
            setOrderId(orderId);
            setRefundTime(System.currentTimeMillis());
            setTradeNo(refundTradeNo);
            setRefundType(RefundTypeEnum.RentVehicle.getCode());
        }};
        orderRefund.setRefundAmount(refundAmount);
        orderRefund.setRefundDesc(refundDesc);
        orderRefund.setRefundStatus(refundStatus);
        orderRefund.setRefundType(refundType);
        orderRefundBiz.insertSelective(orderRefund);

        return refundStatus;
    }

    /**
     * 支付回调处理
     * @param orderNo
     * @param tradeNo
     */
    @Transactional
    public void payNotifyHandle(String orderNo, String tradeNo, Integer type) {
        OrderRentVehicleDetail orvd = new OrderRentVehicleDetail();
        OrderTourDetail otd = new OrderTourDetail();
        OrderMemberDetail omd = new OrderMemberDetail();
        BaseOrder baseOrder = this.selectOne(new BaseOrder() {{
            setNo(orderNo);
        }});

        if (OrderStatusEnum.ORDER_UNPAY.getCode().equals(baseOrder.getStatus()) && baseOrder.getHasPay().equals(SYS_FALSE)) {
            BaseOrder updateOrder = new BaseOrder() {{
                setId(baseOrder.getId());
                setOutTradeNo(tradeNo);
                setHasPay(SYS_TRUE);
                setPayTime(System.currentTimeMillis());
                setPayOrigin(type);
                setVersion(baseOrder.getVersion());
            }};

            //如果是会员订单，则触发会员效益
            if(OrderTypeEnum.MEMBER.getCode().equals(baseOrder.getType())) {
                //直接设置订单完成
                updateOrder.setStatus(OrderStatusEnum.ORDER_FINISH.getCode());
                omd = orderMemberDetailBiz.selectOne(new OrderMemberDetail(){{
                    setOrderId(baseOrder.getId());
                }});

                //触发会员效益
                UserMemberDTO userMemberDTO = new UserMemberDTO() {{
                    setUserId(baseOrder.getUserId());
                    setIsBind(ISBIND_BIND);
                }};
                userMemberDTO.setMemberLevel(omd.getMemberLevel());
                userMemberDTO.setRentFreeDays(omd.getRentFreeNum());
                userMemberDTO.setTotalNumber(omd.getRentFreeNum());
                userMemberDTO.setDiscount(omd.getRebate());

                ObjectRestResponse orr = userFeign.buyMember(userMemberDTO);
                log.info("orr.getStatus() : " + orr.getStatus() );
            }else if(OrderTypeEnum.RENT_VEHICLE.getCode().equals(baseOrder.getType())) {
                updateOrder.setStatus(OrderStatusEnum.ORDER_TOSTART.getCode());
                orvd = orderRentVehicleBiz.selectOne(new OrderRentVehicleDetail(){{
                    setOrderId(baseOrder.getId());
                }});
                //车辆预定审核通过

                //确认免费天数
                if(orvd.getFreeDays() > 0) {
                    userFeign.memberDays(baseOrder.getUserId(), orvd.getFreeDays(), UserFeign.MEMBER_DAYS_CONFIRM);
                }

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

                //站点总人数添加
                 tourFeign.updateTourGoodPersonNum(otd.getVerificationId(), TourFeign.TOTAL_PERSON, otd.getTotalNumber());
            }
            try {
                this.updateSelectiveByIdRe(updateOrder);
            }finally {
                AppUserDTO appUserDTO = userFeign.userDetailById(baseOrder.getUserId()).getData();

                //处理App用户提醒短信的发送
                orderMsgBiz.handelAppUserMsg(orvd, omd, baseOrder, appUserDTO);

                //处理后台用户提醒短信的发送
                orderMsgBiz.handelBgUserMsg4Pay(orvd, baseOrder, appUserDTO, OrderMsgBiz.RENT_PAY);
                sendOrderMq(orvd, otd, omd, baseOrder, OrderMQDTO.ORDER_PAY);
                if(OrderTypeEnum.MEMBER.getCode().equals(baseOrder.getType())) {
                    sendOrderMq(orvd, otd, omd, baseOrder, OrderMQDTO.ORDER_FINISH);
                }
            }
        } else {
            log.error(" order has payed , orderNo:{}, tradeNo:{} ", orderNo, tradeNo);
        }
    }

    public void sendOrderMq(OrderRentVehicleDetail orvd, OrderTourDetail otd, OrderMemberDetail omd, BaseOrder baseOrder, Integer sign) {
        //发送队列消息
        OrderMQDTO orderMQDTO = BeanUtil.toBean(baseOrder, OrderMQDTO.class);
        orderMQDTO.setOrderRentVehicleDetail(orvd);
        orderMQDTO.setOrderTourDetail(otd);
        orderMQDTO.setOrderMemberDetail(omd);
        sendQueue(orderMQDTO, sign);
    }

    /**
     * 更新（不成功抛异常）
     * @param baseOrder
     * @return
     */
    public BaseOrder updateSelectiveByIdReT(BaseOrder baseOrder) {
        if(updateSelectiveByIdRe(baseOrder) > 0) {
            return selectById(baseOrder.getId());
        }else {
            throw new BaseException(ResultCode.DB_OPERATION_FAIL_CODE);
        }
    }

    public boolean getTodyTime(Long time) {
        Long startTime = getDayStart();
        if(time > startTime && time < startTime + 24 * 60 * 60 * 1000 - 1) {
            return true;
        }
        return false;
    }

    public static Long getDayStart() {
        long current = System.currentTimeMillis();
        long zero = current/(1000*3600*24)*(1000*3600*24) - TimeZone.getDefault().getRawOffset();
        return zero;
    }

    private void sendQueue(OrderMQDTO orderMQDTO, Integer sign) {
        try {
            orderMQDTO.setSign(sign);
            switch (sign) {
                case 2:
                    mqSenderFeign.sendMessage(ORDER_TOPIC, KEY_ORDER_CANCEL, JSONUtil.toJsonStr(orderMQDTO));
                    break;
                case 4:
                    mqSenderFeign.sendMessage(ORDER_TOPIC, KEY_ORDER_PAY, JSONUtil.toJsonStr(orderMQDTO));
                    break;
                case 6:
                    mqSenderFeign.sendMessage(ORDER_TOPIC, KEY_ORDER_FINLISH, JSONUtil.toJsonStr(orderMQDTO));
                    break;
                default:
                    break;
            }
        }catch (Exception e){
            log.error(e.getMessage(), e);
        }
    }

}