package com.liquidnet.service.platform.service.refund;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.base.Joiner;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.UserPathDto;
import com.liquidnet.service.candy.param.BackCouponParam;
import com.liquidnet.service.feign.stone.api.FeignStoneIntegralClient;
import com.liquidnet.service.kylin.constant.KylinTableStatusConst;
import com.liquidnet.service.kylin.dto.param.RefundCallbackParam;
import com.liquidnet.service.kylin.dto.vo.mongo.*;
import com.liquidnet.service.kylin.dto.vo.returns.KylinOrderRefundsVo;
import com.liquidnet.service.kylin.entity.*;
import com.liquidnet.service.kylin.mapper.*;
import com.liquidnet.service.platform.utils.DataUtils;
import com.liquidnet.service.platform.utils.MongoVoUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单退款表 服务实现类 处理数据 退款后状态的变化
 * </p>
 *
 * @author jiaangxiulong
 * @since 2021-05-26
 */
@Slf4j
@Service
public class KylinRefundsStatusServiceImpl {

    @Autowired
    private KylinOrderTicketsMapper kylinOrderTicketsMapper;

    @Autowired
    private KylinOrderTicketStatusMapper kylinOrderTicketStatusMapper;

    @Autowired
    private KylinOrderRefundsMapper kylinOrderRefundsMapper;

    @Autowired
    private KylinOrderTicketEntitiesMapper kylinOrderTicketEntitiesMapper;

    @Autowired
    private KylinOrderRefundsEntitiesMapper kylinOrderRefundsEntitiesMapper;

    @Autowired
    private KylinOrderTicketRelationsMapper kylinOrderTicketRelationsMapper;

    @Autowired
    MongoTemplate mongoTemplate;

    @Autowired
    private MongoConverter mongoConverter;

    @Autowired
    private DataUtils dataUtils;

    @Autowired
    private MongoVoUtils mongoVoUtils;

    @Autowired
    private FeignStoneIntegralClient feignStoneIntegralClient;

    @Value("${liquidnet.service.candy.url}")
    private String candyUrl;

    public boolean orderTicketRefunded(RefundCallbackParam refundCallbackParam, KylinOrderRefunds refundInfo) {
        List<KylinOrderRefundEntities> refundEntities = kylinOrderRefundsEntitiesMapper.selectList(
                Wrappers.lambdaQuery(KylinOrderRefundEntities.class).eq(KylinOrderRefundEntities::getOrderRefundsId, refundInfo.getOrderRefundsId())
        );
        List<String> orderTicketEntitiesIdsArr = refundEntities.stream().map(KylinOrderTicketEntities -> KylinOrderTicketEntities.getOrderTicketEntitiesId()).collect(Collectors.toList());
        String orderTicketsId = refundInfo.getOrderTicketsId();

        KylinOrderTicketRelations orderRelations = kylinOrderTicketRelationsMapper.selectOne(
                Wrappers.lambdaQuery(KylinOrderTicketRelations.class).eq(KylinOrderTicketRelations::getOrderId, orderTicketsId)
        );

        // 更新数据
        // 订单状态表
        KylinOrderTickets orderInfo = kylinOrderTicketsMapper.selectOne(
                Wrappers.lambdaQuery(KylinOrderTickets.class).eq(KylinOrderTickets::getOrderTicketsId, orderTicketsId)
        );
        KylinOrderTicketStatus orderStatusTable = KylinOrderTicketStatus.getNew();
        int newStatus = 0;
        int compareToRes = refundCallbackParam.getRefundPrice().add(refundInfo.getPriceCharges()).add(orderInfo.getPriceRefund()).add(orderInfo.getRefundPriceCharges()).compareTo(orderInfo.getPriceActual());
        if (compareToRes == 0) {
            newStatus = KylinTableStatusConst.ORDER_STATUS4;
        } else if (compareToRes < 0) {
            newStatus = KylinTableStatusConst.ORDER_STATUS6;
        } else if (compareToRes > 0) {
            log.info("refundCallback: 退款金额已大于实际支付金额，编号{}", refundInfo.getOrderTicketsId());
            newStatus = KylinTableStatusConst.ORDER_STATUS4;
        }
        orderStatusTable.setStatus(newStatus);
        orderStatusTable.setUpdatedAt(LocalDateTime.now());
        int update1 = kylinOrderTicketStatusMapper.update(
                orderStatusTable,
                Wrappers.lambdaUpdate(KylinOrderTicketStatus.class).eq(KylinOrderTicketStatus::getOrderId, orderTicketsId)
        );

        // 入场人
        BigDecimal priceActual = orderInfo.getPriceActual();
        BigDecimal priceExpress = orderInfo.getPriceExpress();
        int allEntitiesCount = kylinOrderTicketEntitiesMapper.selectCount(// 总入场人数量 排出未付款的 用来计算单入场人的价格
                Wrappers.lambdaQuery(KylinOrderTicketEntities.class)
                        .eq(KylinOrderTicketEntities::getOrderId, orderTicketsId)
                        .ne(KylinOrderTicketEntities::getIsPayment, KylinTableStatusConst.ENTITIES_IS_PAYMENT0)
        );
        BigDecimal onePrice = priceActual.subtract(priceExpress).divide(BigDecimal.valueOf(allEntitiesCount));//单价
        int refundNumber = 0;
        for (String entitiesId : orderTicketEntitiesIdsArr) {
            KylinOrderTicketEntities EntitiesInfo = kylinOrderTicketEntitiesMapper.selectOne(//已退完成的
                    Wrappers.lambdaQuery(KylinOrderTicketEntities.class)
                            .eq(KylinOrderTicketEntities::getOrderTicketEntitiesId, entitiesId)
            );
            BigDecimal refundedPrice = EntitiesInfo.getRefundPrice();

            KylinOrderRefundEntities refundEntitiesInfo = kylinOrderRefundsEntitiesMapper.selectOne(
                    Wrappers.lambdaQuery(KylinOrderRefundEntities.class)
                            .eq(KylinOrderRefundEntities::getOrderRefundsId, refundInfo.getOrderRefundsId())
                            .eq(KylinOrderRefundEntities::getOrderTicketEntitiesId, entitiesId)
            );

            KylinOrderTicketEntities entitiesTable = KylinOrderTicketEntities.getNew();
            BigDecimal priceNew = refundEntitiesInfo.getRefundPrice().add(refundedPrice);
            int isPayment = 0;
            if (priceNew.compareTo(onePrice) >= 0) {
                isPayment = KylinTableStatusConst.ENTITIES_IS_PAYMENT3;
                refundNumber++;
            } else {
                isPayment = KylinTableStatusConst.ENTITIES_IS_PAYMENT4;
            }
            entitiesTable.setIsPayment(isPayment);
            entitiesTable.setUpdatedAt(LocalDateTime.now());
            if (priceNew.compareTo(onePrice) > 0) {
                priceNew = onePrice;
            }
            entitiesTable.setRefundPrice(priceNew);
            Integer[] entitiesTableIsPayment = {KylinTableStatusConst.ENTITIES_IS_PAYMENT2, KylinTableStatusConst.ENTITIES_IS_PAYMENT4};
            kylinOrderTicketEntitiesMapper.update(
                    entitiesTable,
                    Wrappers.lambdaUpdate(KylinOrderTicketEntities.class)
                            .eq(KylinOrderTicketEntities::getOrderTicketEntitiesId, entitiesId)
                            .in(KylinOrderTicketEntities::getIsPayment, entitiesTableIsPayment)
            );

            HashMap<String, Object> EntitiesVo = new HashMap<>();
            EntitiesVo.put("updatedAt", DateUtil.getNowTime());
            EntitiesVo.put("refundPrice", priceNew);
            EntitiesVo.put("isPayment", isPayment);
            BasicDBObject EntitiesVov = new BasicDBObject("$set", mongoConverter.convertToMongoType(EntitiesVo));
            UpdateResult updateResult = mongoTemplate.getCollection(KylinOrderTicketEntitiesVo.class.getSimpleName()).updateOne(
                    Query.query(Criteria.where("orderTicketEntitiesId").is(entitiesId)).getQueryObject(),
                    EntitiesVov
            );

            dataUtils.delOrderTicketEntitiesRedis(entitiesId);
        }

        // 订单表
        BigDecimal price = orderInfo.getPriceRefund().add(refundCallbackParam.getRefundPrice());
        if (compareToRes > 0) {
            price = orderInfo.getPriceActual();
        }
        Integer num = orderInfo.getRefundNumber() + refundNumber;
        if (num > orderInfo.getNumber()) {
            num = orderInfo.getNumber();
        }
        KylinOrderTickets update = KylinOrderTickets.getNew();
        update.setRefundNumber(num);
        update.setPriceRefund(price);
        update.setUpdatedAt(LocalDateTime.now());
        update.setRefundPriceCharges(orderInfo.getRefundPriceCharges().add(refundInfo.getPriceCharges()));
        int update2 = kylinOrderTicketsMapper.update(
                update,
                Wrappers.lambdaUpdate(KylinOrderTickets.class).eq(KylinOrderTickets::getOrderTicketsId, orderTicketsId)
        );

        HashMap<String, Object> orderVo = new HashMap<>();
        orderVo.put("updatedAt", DateUtil.getNowTime());
        orderVo.put("priceRefund", price);
        orderVo.put("refundPriceCharges", orderInfo.getRefundPriceCharges().add(refundInfo.getPriceCharges()));
        orderVo.put("status", newStatus);
        orderVo.put("refundNumber", num);
        BasicDBObject orderVov = new BasicDBObject("$set", mongoConverter.convertToMongoType(orderVo));
        UpdateResult orderUpdateResult = mongoTemplate.getCollection(KylinOrderTicketVo.class.getSimpleName()).updateOne(
                Query.query(Criteria.where("orderTicketsId").is(orderTicketsId)).getQueryObject(),
                orderVov
        );
        dataUtils.delOrderTicketRedis(orderTicketsId);
        mongoVoUtils.resetOrderListVo(orderInfo.getUserId(), 2, orderTicketsId, null);

        // 退款单完成
        KylinOrderRefunds kylinOrderRefunds = KylinOrderRefunds.getNew();
        kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_REFUNDED);
        kylinOrderRefunds.setRefundCode(refundCallbackParam.getRefundCode());
        kylinOrderRefunds.setRefundType(refundCallbackParam.getRefundType());
        kylinOrderRefunds.setRefundId(refundCallbackParam.getRefundId());
        kylinOrderRefunds.setRefundAt(refundCallbackParam.getRefundAt());
        kylinOrderRefunds.setUpdatedAt(LocalDateTime.now());
        int update3 = kylinOrderRefundsMapper.update(
                kylinOrderRefunds,
                Wrappers.lambdaUpdate(KylinOrderRefunds.class).eq(KylinOrderRefunds::getOrderRefundsId, refundInfo.getOrderRefundsId())
        );
        // 修改缓存
        KylinOrderRefundsVo kylinOrderRefundsVo = KylinOrderRefundsVo.getNew();
        BeanUtils.copyProperties(kylinOrderRefunds, kylinOrderRefundsVo);
        BasicDBObject object = new BasicDBObject("$set", mongoConverter.convertToMongoType(kylinOrderRefundsVo));
        UpdateResult updateResult = mongoTemplate.getCollection(KylinOrderRefundsVo.class.getSimpleName()).updateOne(
                Query.query(Criteria.where("orderRefundsId").is(refundInfo.getOrderRefundsId())).getQueryObject(),
                object
        );
        dataUtils.delOrderRefundVo(refundInfo.getOrderRefundsId());
        dataUtils.delOrderRefundVoByOrderId(refundInfo.getOrderTicketsId());

        log.info("refundCallback数据库更新res: [update1={},update2={},update3={}]", update1, update2, update3);

        if(null != refundInfo.getRefundCode() && !refundInfo.getRefundCode().isEmpty()) {
            log.info("refundCallback已退过不处理库存等: [refundAt={}, orderRefundCode={}, refundCode={}]", refundInfo.getRefundAt(), refundInfo.getOrderRefundCode(), refundInfo.getRefundCode());
            return true;
        }

        if (refundInfo.getType() != KylinTableStatusConst.ORDER_REFUND_TYPE_AUTO && update3 > 0) {
            // 退还库存
            for (String entitiesId : orderTicketEntitiesIdsArr) {
                KylinOrderTicketEntities entitiesInfo = kylinOrderTicketEntitiesMapper.selectOne(
                        Wrappers.lambdaQuery(KylinOrderTicketEntities.class).eq(KylinOrderTicketEntities::getOrderTicketEntitiesId, entitiesId)
                );
                if (entitiesInfo.getIsPayment() == KylinTableStatusConst.ENTITIES_IS_PAYMENT3) {
                    int surplusGeneral = dataUtils.changeSurplusGeneral(entitiesInfo.getTicketId(), 1);
                    log.info("refundCallback回滚库存res: [surplusGeneral={},ticketId={},orderRefundCode={}]", surplusGeneral, entitiesInfo.getTicketId(), refundCallbackParam.getOrderRefundCode());
                    log.info(UserPathDto.setData("changeBuyInfo", "UserId=" + orderInfo.getUserId() + "idCard=" + entitiesInfo.getEnterIdCode() + " PerformanceId=" + orderRelations.getPerformanceId() + " TicketId=" + entitiesInfo.getTicketId(), "info"));
                    dataUtils.changeBuyInfo(orderInfo.getUserId(), entitiesInfo.getEnterIdCode(), orderRelations.getPerformanceId(), entitiesInfo.getTicketId(), -1);
                }
            }
            // 退所有优惠券 提前购优惠券不退
            if (newStatus == KylinTableStatusConst.ORDER_STATUS4) {
                KylinPerformanceVo performanceVo = dataUtils.getPerformanceVo(orderRelations.getPerformanceId());
                if (performanceVo.getIsRefundVoucher() > 0) {
                    ArrayList<KylinOrderCoupons> orderCoupon = dataUtils.getOrderCoupon(orderTicketsId);
                    if (!CollectionUtil.isEmpty(orderCoupon)) {
                        BackCouponParam param = BackCouponParam.getNew();
                        List<String> uCouponIds = orderCoupon.stream().filter(r -> r.getCouponType() != 101).map(KylinOrderCoupons -> KylinOrderCoupons.getCouponCode()).collect(Collectors.toList());
                        String uCouponIdsStr = Joiner.on(",").join(uCouponIds);
                        param.setuCouponIds(uCouponIdsStr);
                        param.setUid(orderInfo.getUserId());

                        ArrayList<BackCouponParam> params = new ArrayList();
                        params.add(param);
                        String jsonString = JSON.toJSONString(params);
                        log.info("订单退款回调orderTicketRefunded-退所有优惠券：[orderTicketsId={}, uCouponIdsStr={}, uid={}, candyUrl={}]",
                                orderTicketsId, uCouponIdsStr, orderInfo.getUserId(), candyUrl);
                        try {
                            String returnData = HttpUtil.postRaw(candyUrl + "/candy-coupon/useBack", jsonString, null);
                            log.info("订单退款回调退券结果：[returnData={}]", returnData);
                        } catch (Exception e) {
                            log.info("订单退款回调退券异常：[errorMsg={}, e={}]", e.getMessage(), e);
                        }
                    }
                }
            }
            // 退积分
            try {
                ResponseDto<String> de2111 = feignStoneIntegralClient.de2111(orderInfo.getUserId(), refundCallbackParam.getRefundPrice().setScale(0, BigDecimal.ROUND_DOWN).intValue(), "演出订单退款:".concat(orderInfo.getPerformanceTitle()).concat(":").concat(orderInfo.getOrderCode().substring(orderInfo.getOrderCode().length() - 10)));
                log.info("订单退款回调退积分结果：[getCode={}, getData={}]", de2111.getCode(), de2111.getData());
            } catch (Exception e) {
                log.info("订单退款回调退积分异常：[errorMsg={}, e={}]", e.getMessage(), e);
            }
        }

        return true;
    }

}
