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.feign.UserFeign;
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.xxfc.platform.order.biz.inner.OrderMsgBiz;
import com.xxfc.platform.order.contant.enumerate.*;
import com.xxfc.platform.order.entity.*;
import com.xxfc.platform.order.pojo.DedDetailDTO;
import com.xxfc.platform.order.pojo.mq.OrderMQDTO;
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.universal.vo.OrderRefundVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.jexl2.MapContext;
import org.mockito.internal.util.collections.Sets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.xxfc.platform.order.mapper.OrderRefundMapper;
import com.github.wxiaoqi.security.common.biz.BaseBiz;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.APP_ORDER;

/**
 * 订单退款记录表
 *
 * @author zjw
 * @email nishijjo@qq.com
 * @date 2019-06-18 11:08:09
 */
@Service
@Slf4j
public class OrderRefundBiz extends BaseBiz<OrderRefundMapper,OrderRefund> {
    @Autowired
    ThirdFeign thirdFeign;

    @Autowired
    BaseOrderBiz baseOrderBiz;

    @Autowired
    OrderRentVehicleBiz orderRentVehicleBiz;

    @Autowired
    OrderVehicleCrosstownBiz crosstownBiz;

    @Autowired
    OrderViolationBiz orderViolationBiz;

    @Autowired
    OrderMsgBiz orderMsgBiz;

    @Autowired
    UserFeign userFeign;

    @Autowired
    OrderDepositRefundRecordBiz orderDepositRefundRecordBiz;

    /**
     * 退还部分押金
     * @param orderMQDTO
     */
    public void refundPartDeposit(OrderMQDTO orderMQDTO){
        Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
        BigDecimal illegalReserve = new BigDecimal(dictionaryMap.get(APP_ORDER+ "_"+ DictionaryKey.ILLEGAL_RESERVE).getDetail());

        //未退还， 进行挂起保留违章预备金 的退还
        if(RefundStatusEnum.NONE.getCode().equals(orderMQDTO.getRefundStatus())) {
            Integer crosstownTypeEnum;
            DepositRefundStatus depositRefundRecordStatus;
            //判断是否定损过
            if(SYS_TRUE.equals(orderMQDTO.getOrderRentVehicleDetail().getFixedLossStatus())) {
                crosstownTypeEnum = CrosstownTypeEnum.FIXED_LOSS.getCode();
                depositRefundRecordStatus = DepositRefundStatus.FIXLOSSREFUNDARRIVAL;
            }else {
                crosstownTypeEnum = CrosstownTypeEnum.ARRIVE.getCode();
                depositRefundRecordStatus = DepositRefundStatus.REFUNDARRIVAL;
            }
            OrderVehicleCrosstown crosstown = crosstownBiz.selectOne(new OrderVehicleCrosstown(){{
                setOrderId(orderMQDTO.getId());
                setType(crosstownTypeEnum);
            }});

            if(null == crosstown) {
                throw new BaseException(ResultCode.PARAM_ILLEGAL_CODE, Sets.newSet("退款第一部分押金失败，获取不了还车/定损记录，订单号："+ orderMQDTO.getId()));
            }

            if(null == crosstown.getRestDeposit()
                    || crosstown.getRestDeposit().subtract(illegalReserve).compareTo(BigDecimal.ZERO) < 0 ){
                throw new BaseException(ResultCode.PARAM_ILLEGAL_CODE, Sets.newSet("退第一笔押金剩余金额异常，异常记录为："+ crosstown.getId()));
            }

            //还车扣除款 剩余的 钱，再减去违章预备金
            BigDecimal refundAmont = crosstown.getRestDeposit().subtract(illegalReserve);
            BigDecimal originalRefundAmount = crosstown.getRestDeposit().add(crosstown.getDeductionCost()).subtract(illegalReserve);
            String refundDesc = "退还押金："+ refundAmont.toString()+ "(已扣除 违章预备金："+ illegalReserve.toString();
            try{
                if(null != crosstown.getDedDetail()) {
                    List<DedDetailDTO> dddList = JSONUtil.toList(JSONUtil.parseArray(crosstown.getDedDetail()), DedDetailDTO.class);
                    for(DedDetailDTO ddd : dddList) {
                        refundDesc += ", "+ ddd.getDeductions()+ ":"+ ddd.getCost();
                    }
                }
            }catch (Exception e) {
                log.error("crosstown.getDedDetail() crosstown id :"+crosstown.getId() +" 转换失败");
            }
            refundDesc += ")";
            refundTrigger(orderMQDTO, orderMQDTO.getOrderRentVehicleDetail(), illegalReserve, originalRefundAmount, refundAmont, refundDesc, RefundStatusEnum.RESIDUE_ILLEGAL.getCode(), RefundTypeEnum.PART_DEPOSIT);

//            DepositRefundRecord depositRefundRecord = orderDepositRefundRecordBiz.findByCrossIdAndStatus(crosstown.getId(), depositRefundRecordStatus);
//            depositRefundRecord.setIscomplete(Boolean.TRUE);
//            orderDepositRefundRecordBiz.updateSelectiveById(depositRefundRecord);
            orderDepositRefundRecordBiz.completeRecordStatus(crosstown.getId(), depositRefundRecordStatus);
            orderMsgBiz.handelMsgDeposit(orderMQDTO.getOrderRentVehicleDetail(), orderMQDTO, userFeign.userDetailById(orderMQDTO.getUserId()).getData());
        }
    }

    public void refundTrigger(BaseOrder baseOrder, OrderRentVehicleDetail orvd, BigDecimal residueAmount, BigDecimal originalRefundAmount, BigDecimal refundAmont, String refundDesc, Integer refundStatus, RefundTypeEnum refundTypeEnum) {
        //退款子流程: 订单基础，退款描述，退款金额
        refundSubProcess(baseOrder, refundDesc, originalRefundAmount, refundAmont, refundTypeEnum.getCode(), refundStatus);
        //设置剩余没有返还的钱
        orderRentVehicleBiz.updateSelectiveById(new OrderRentVehicleDetail(){{
            setId(orvd.getId());
            setReturnPayResidue(residueAmount);
        }});
    }

    /**
     * 租车（包括旅游）退款流程
     * @param baseOrder
     * @param timeLag 与开始时间的时间差
     * @param dicParentKey
     */
    public BigDecimal rentRefundProcess(BaseOrder baseOrder, Long timeLag, String dicParentKey) {
        //计算退款金额
            //商品价格 - 优惠券减免的价格
        BigDecimal originalRefundAmount = BigDecimal.ZERO.add(baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount()));
        StringBuilder refundDescBuilder = new StringBuilder("取消订单退款:");
        BigDecimal refundGoodsAmount = calculateRefund(originalRefundAmount, timeLag, dicParentKey, refundDescBuilder);
        refundGoodsAmount = refundGoodsAmount.setScale(2, RoundingMode.HALF_UP);

        //退款金额
        BigDecimal refundAmount = refundGoodsAmount;

        //退款子流程: 订单基础，退款描述，退款金额
        refundSubProcess(baseOrder, refundDescBuilder.toString(), originalRefundAmount, refundAmount, RefundTypeEnum.ORDER_FUND.getCode(), RefundStatusEnum.ALL.getCode());
        return refundAmount;
    }

    /**
     * 租车押金退款流程
     * @param baseOrder
     * @param depositAmount
     * @param timeLag 与开始时间的时间差
     * @param dicParentKey
     * @param originalDeductAmount
     */
    public BigDecimal rentRefundDepositProcess(BaseOrder baseOrder, BigDecimal depositAmount, Long timeLag, String dicParentKey, BigDecimal originalDeductAmount) {
        // 1、押金 + 租金(规则扣除)
        BigDecimal originalRefundAmount = BigDecimal.ZERO.add(depositAmount);
        BigDecimal refundAmount = BigDecimal.ZERO.add(depositAmount);
        StringBuilder refundDescBuilder = new StringBuilder("取消订单押金退款:");
        if(null != originalDeductAmount && BigDecimal.ZERO.compareTo(originalDeductAmount) < 0) {
            //通过原扣除款 计算剩余款
            BigDecimal residueAmount = calculateRefund(originalDeductAmount, timeLag, dicParentKey, refundDescBuilder);
            residueAmount = residueAmount.setScale(2, RoundingMode.HALF_UP);

            //退款金额 ：押金 - (原扣除款 - 剩余款)   即： 押金 - （免费天数对应的钱 - 剩余款）
            refundAmount = originalRefundAmount.subtract(originalDeductAmount.subtract(residueAmount));
        }

        //触发押金退款
        refundSubProcess(baseOrder, refundDescBuilder.toString(), originalRefundAmount, refundAmount, RefundTypeEnum.DEPOSIT.getCode(), RefundStatusEnum.ALL.getCode());
        return refundAmount;
    }

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

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

        Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
        Set<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());
                refundDescBuilder = refundDescBuilder.insert(0, names[1]+ ",");
                break;
            }
        }

        return refundGoodsAmount;
    }

    /**
     * 退款子流程
     * @param baseOrder
     * @param refundDesc
     * @param refundAmount
     * @param refundType
     * @param refundStatus
     */
    public void refundSubProcess(BaseOrder baseOrder, String refundDesc, BigDecimal originalRefundAmount, BigDecimal refundAmount, Integer refundType, Integer refundStatus) {
        String refundTradeNo = null;
        //0 小于 退款金额
        if(BigDecimal.ZERO.compareTo(refundAmount) < 0) {
            OrderRefundVo orv = new OrderRefundVo();
            orv.setAmount(baseOrder.getRealAmount().multiply(new BigDecimal("100")).intValue());
            orv.setOrderNo(baseOrder.getNo());
            orv.setRefundDesc(refundDesc+ refundAmount.toString());
            orv.setRefundAmount(refundAmount.multiply(new BigDecimal("100")).intValue());
            ObjectRestResponse<String> result = thirdFeign.refund(orv);
            refundTradeNo = result.getData();
            if(null == refundTradeNo) {
                log.error("退款没有refundTradeNo，订单号为："+ baseOrder.getNo()+ ", 微服务调用结果为"+ JSONUtil.toJsonStr(result));
            }
        }

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

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

    /**
     * 记录订单退款记录
     * @param orderId
     * @param refundDesc
     * @param refundAmount
     * @param refundTradeNo
     */
    private Integer addOrderRefund(Integer orderId, String refundDesc, BigDecimal originalRefundAmount, 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.ORDER_FUND.getCode());
        }};
        orderRefund.setOriginalRefundAmount(originalRefundAmount);
        orderRefund.setDeductAmount(originalRefundAmount.subtract(refundAmount));
        orderRefund.setRefundAmount(refundAmount);
        orderRefund.setRefundDesc(refundDesc);
        orderRefund.setRefundStatus(refundStatus);
        orderRefund.setRefundType(refundType);
        insertSelective(orderRefund);

        return refundStatus;
    }
}