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

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.HttpUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.kylin.constant.KylinTableStatusConst;
import com.liquidnet.service.kylin.dto.param.RefundApplyParam;
import com.liquidnet.service.kylin.dto.param.RefundCallbackParam;
import com.liquidnet.service.kylin.dto.param.RefundSearchParam;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinOrderTicketVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinPerformanceVo;
import com.liquidnet.service.kylin.dto.vo.returns.KylinOrderRefundsVo;
import com.liquidnet.service.kylin.entity.KylinOrderRefunds;
import com.liquidnet.service.kylin.entity.KylinOrderTickets;
import com.liquidnet.service.kylin.mapper.KylinOrderRefundsMapper;
import com.liquidnet.service.kylin.mapper.KylinOrderTicketStatusMapper;
import com.liquidnet.service.kylin.mapper.KylinOrderTicketsMapper;
import com.liquidnet.service.kylin.service.IKylinOrderRefundsService;
import com.liquidnet.service.platform.utils.DataUtils;
import com.liquidnet.service.platform.utils.GroupBuyPlatformHelper;
import com.mongodb.BasicDBObject;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
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.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 订单退款表 服务实现类 处理逻辑判断
 * </p>
 *
 * @author jiangxiulong
 * @since 2021-05-26
 */
@Slf4j
@Service
public class OrderRefundsCallbackServiceImpl extends ServiceImpl<KylinOrderRefundsMapper, KylinOrderRefunds> implements IKylinOrderRefundsService {

    @Value("${liquidnet.service.dragon.urls.refundApply}")
    private String refundApply;
    @Value("${liquidnet.service.dragon.urls.refundResult}")
    private String refundResult;
    @Value("${liquidnet.service.platform.urls.ticketRefundNotify}")
    private String refundNotify;


    @Autowired
    private KylinRefundsStatusServiceImpl kylinRefundsStatusServiceImpl;

    @Autowired
    private KylinOrderRefundsMapper kylinOrderRefundsMapper;
    @Autowired
    private KylinOrderTicketsMapper kylinOrderTicketsMapper;
    @Autowired
    private KylinOrderTicketStatusMapper kylinOrderTicketStatusMapper;

    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    private MongoConverter mongoConverter;
    @Autowired
    private DataUtils dataUtils;
    @Autowired
    private GroupBuyPlatformHelper groupBuyPlatformHelper;

    public String refundCallback(RefundCallbackParam refundCallbackParam) {
        log.info("refundCallback订单退款回调参数: [RefundCallbackParam={}]", refundCallbackParam);
        KylinOrderRefunds refundInfo = kylinOrderRefundsMapper.selectOne(
                Wrappers.lambdaQuery(KylinOrderRefunds.class).eq(KylinOrderRefunds::getOrderRefundCode, refundCallbackParam.getOrderRefundCode())
        );

        if (refundInfo == null) {
            log.info("refundCallback: 退款订单查询失败，编号{}", refundCallbackParam.getOrderRefundCode());
            return "fail";
        }
        if (refundInfo.getStatus() == KylinTableStatusConst.ORDER_REFUND_STATUS_CANCEL || refundInfo.getStatus() == KylinTableStatusConst.ORDER_REFUND_STATUS_REJECT) {
            log.info("refundCallback: 退款订单已取消，编号{}", refundCallbackParam.getOrderRefundCode());
            return "fail";
        }
        if (refundInfo.getStatus() == KylinTableStatusConst.ORDER_REFUND_STATUS_REFUNDED) {
            log.info("refundCallback: 退款订单已完成，编号{}", refundCallbackParam.getOrderRefundCode());
            return "success";
        }

        /*KylinOrderTicketStatus kylinOrderTicketStatus = kylinOrderTicketStatusMapper.selectOne(
                Wrappers.lambdaQuery(KylinOrderTicketStatus.class).eq(KylinOrderTicketStatus::getOrderId, refundInfo.getOrderTicketsId())
        );
        if (kylinOrderTicketStatus.getStatus() == KylinTableStatusConst.ORDER_STATUS4) {
            log.info("refundCallback: 票订单已完成退款，编号{}", refundInfo.getOrderTicketsId());
            return "success";
        }*/

        Integer status = refundCallbackParam.getStatus();
        if (1 == status) { // 退款成功
            boolean res = kylinRefundsStatusServiceImpl.orderTicketRefunded(refundCallbackParam, refundInfo);
            if (res) {
                try {
                    groupBuyPlatformHelper.onTicketRefunded(refundInfo.getOrderTicketsId());
                } catch (Exception e) {
                    log.error("拼团退款回写异常 orderTicketsId={}", refundInfo.getOrderTicketsId(), e);
                }
                return "success";
            } else {
                return "fail";
            }
        }
        if (0 == status) { // 退款失败
            KylinOrderRefunds kylinOrderRefunds = new KylinOrderRefunds();
            kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_ERROR);
            kylinOrderRefunds.setRefundCode(refundCallbackParam.getRefundCode());
            kylinOrderRefunds.setRefundAt(refundCallbackParam.getRefundAt());
            kylinOrderRefunds.setRefundError(refundCallbackParam.getRefundError());
            kylinOrderRefunds.setUpdatedAt(LocalDateTime.now());
            kylinOrderRefundsMapper.update(
                    kylinOrderRefunds,
                    Wrappers.lambdaUpdate(KylinOrderRefunds.class).eq(KylinOrderRefunds::getOrderRefundsId, refundInfo.getOrderRefundsId())
            );
            // 修改缓存
            KylinOrderRefundsVo kylinOrderRefundsVo = new KylinOrderRefundsVo();
            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());
        }
        return "success";
    }

    public void automaticRefund(String orderRefundsId) {
        try {
            KylinOrderRefunds refund = kylinOrderRefundsMapper.selectOne(
                    Wrappers.lambdaQuery(KylinOrderRefunds.class)
                            .eq(KylinOrderRefunds::getOrderRefundsId, orderRefundsId)
            );
            log.info("自动退款refund={}",refund.toString());
            KylinOrderTickets oderInfo = kylinOrderTicketsMapper.selectOne(
                    new QueryWrapper<KylinOrderTickets>()
                            .eq("order_tickets_id", refund.getOrderTicketsId())
            );
            BigDecimal refundPrice = refund.getPrice().add(refund.getPriceExpress());
            MultiValueMap<String, String> params = new LinkedMultiValueMap();
            params.add("code", oderInfo.getPayCode());
            params.add("notifyUrl", refundNotify);
            params.add("orderCode", oderInfo.getOrderCode());
            params.add("orderRefundCode", refund.getOrderRefundCode());
            params.add("paymentId", oderInfo.getPaymentId());
            params.add("paymentType", oderInfo.getPaymentType());
            params.add("price", String.valueOf(refundPrice));
            params.add("priceTotal", String.valueOf(oderInfo.getPriceActual()));
            params.add("reason", "按需退款");
            MultiValueMap<String, String> headers = new LinkedMultiValueMap();
            headers.add("Accept", "application/json;charset=UTF-8");
            log.info("调用自动退款参数" + JsonUtils.toJson(params));
            String postResult = HttpUtil.post(refundApply, params, headers);
            log.info("调用自动退款返参res" + postResult);
            HashMap hashMapResult = JsonUtils.fromJson(postResult, HashMap.class);
            Boolean success = (Boolean) hashMapResult.get("success");
            if (!success) {
                String message = (String) hashMapResult.get("message");
                log.info("自动退款pay返回失败" + message);
                KylinOrderRefunds kylinOrderRefundsFail = new KylinOrderRefunds();
                kylinOrderRefundsFail.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_ERROR);
                kylinOrderRefundsFail.setRefundError(message);
                kylinOrderRefundsFail.setUpdatedAt(LocalDateTime.now());
                kylinOrderRefundsMapper.update(
                        kylinOrderRefundsFail,
                        new UpdateWrapper<KylinOrderRefunds>().eq("order_refunds_id", refund.getOrderRefundsId())
                );
                // 修改缓存
                KylinOrderRefundsVo kylinOrderRefundsVoFail = new KylinOrderRefundsVo();
                BeanUtils.copyProperties(kylinOrderRefundsFail, kylinOrderRefundsVoFail);
                BasicDBObject objectFail = new BasicDBObject("$set", mongoConverter.convertToMongoType(kylinOrderRefundsVoFail));
                UpdateResult updateFailResult = mongoTemplate.getCollection(KylinOrderRefundsVo.class.getSimpleName()).updateOne(
                        Query.query(Criteria.where("orderRefundsId").is(refund.getOrderRefundsId())).getQueryObject(),
                        objectFail
                );
            }
        } catch (Exception e) {
            log.error("自动退款请求pay失败e" + e.getMessage());
        }
    }
    public  void  failRefund(){
        List<KylinOrderRefunds> kylinOrderRefunds = kylinOrderRefundsMapper.selectList(
                Wrappers.lambdaQuery(KylinOrderRefunds.class)
                        .eq(KylinOrderRefunds::getStatus, KylinTableStatusConst.ORDER_REFUND_STATUS_ERROR));
        log.info("自动处理退款失败开始");
        if(!CollectionUtil.isEmpty(kylinOrderRefunds)){
            for (KylinOrderRefunds kylinOrderRefund : kylinOrderRefunds) {
                     String refundId = kylinOrderRefund.getOrderRefundsId();
                     log.info("自动处理退款失败RefundId={}",refundId);
                     this.automaticRefund(refundId);
            }
        }
    }

    @Override
    public String getOrderRefundCode(String orderRefundCode, int type) {
        return null;
    }

    public ResponseDto<String> alipayActiveCallback() {
        LocalDateTime startTime = LocalDateTime.now();
        LocalDateTime newTime = startTime.minusHours(2);
        List<KylinOrderRefunds> kylinOrderRefunds = kylinOrderRefundsMapper.selectList(
                Wrappers.lambdaQuery(KylinOrderRefunds.class)
                        .eq(KylinOrderRefunds::getStatus, KylinTableStatusConst.ORDER_REFUND_STATUS_UNFILLED)
                        .lt(KylinOrderRefunds::getExecutorAt, newTime) // 默认减去8小时的
        );
        for (KylinOrderRefunds refundInfo : kylinOrderRefunds) {
            KylinOrderTickets orderInfo = kylinOrderTicketsMapper.selectOne(
                    Wrappers.lambdaQuery(KylinOrderTickets.class)
                            .eq(KylinOrderTickets::getOrderTicketsId, refundInfo.getOrderTicketsId())
            );
            if (null != orderInfo.getPaymentType() && (orderInfo.getPaymentType().equals("WAPALIPAY") || orderInfo.getPaymentType().equals("APPALIPAY"))) {
                log.info("\n支付宝退款主动查询处理结果：\n[{}] ", orderInfo.getOrderTicketsId());
                MultiValueMap<String, String> params = new LinkedMultiValueMap();
                params.add("callBackUrl", refundNotify);
                params.add("orderCode", orderInfo.getOrderCode());
                params.add("orderRefundCode", refundInfo.getOrderRefundCode());
                params.add("paymentId", orderInfo.getPaymentId());

                log.info("\n支付宝退款主动查询参数：\n[{}] ", params.toString());
                // 请求dragon
                try {
                    String postResult = HttpUtil.post(refundResult, params);
                    log.info("\n支付宝退款主动查询处理结果：\n[{}] " + postResult);
                } catch (Exception e) {
                    log.info("\n支付宝退款主动查询失败：[errorMsg=[{}], [orderRefundsId=[{}]", e.getMessage(), refundInfo.getOrderRefundsId());
                }

            }
        }
        return ResponseDto.success("success");
    }

    public ResponseDto<String> overtimeRefund() {
        List<KylinOrderRefunds> refundList = kylinOrderRefundsMapper.selectList(
                Wrappers.lambdaQuery(KylinOrderRefunds.class)
                        .eq(KylinOrderRefunds::getType, KylinTableStatusConst.ORDER_REFUND_TYPE_AUTO)
                        .in(KylinOrderRefunds::getStatus, Arrays.asList(
                                KylinTableStatusConst.ORDER_REFUND_STATUS_APPLY,
                                KylinTableStatusConst.ORDER_REFUND_STATUS_APPROVED
                        ))
        );
        for (KylinOrderRefunds refundInfo : refundList) {
            String refundId = refundInfo.getOrderRefundsId();
            String orderId = refundInfo.getOrderTicketsId();
            LocalDateTime nowTime = LocalDateTime.now();
            KylinOrderRefunds kylinOrderRefunds = KylinOrderRefunds.getNew();
            // 运营审核
            // kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_APPROVED);
            kylinOrderRefunds.setAuditorId("system");
            kylinOrderRefunds.setAuditorName("system");
            kylinOrderRefunds.setAuditorAt(nowTime);
            kylinOrderRefunds.setReject("系统审核通过");
            // 财务审核
            kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_UNFILLED);
            kylinOrderRefunds.setExecutorId("system");
            kylinOrderRefunds.setExecutorName("system");
            kylinOrderRefunds.setExecutorAt(nowTime);
            kylinOrderRefunds.setRefuse("系统审核通过");
            kylinOrderRefunds.setUpdatedAt(nowTime);
            // 数据库
            kylinOrderRefundsMapper.update(
                    kylinOrderRefunds,
                    Wrappers.lambdaUpdate(KylinOrderRefunds.class).eq(KylinOrderRefunds::getOrderRefundsId, refundId)
            );
            // 缓存
            KylinOrderRefundsVo kylinOrderRefundsVo = KylinOrderRefundsVo.getNew();
            BeanUtils.copyProperties(kylinOrderRefunds, kylinOrderRefundsVo);
            /*UpdateResult result = mongoTemplate.updateFirst(
                    Query.query(Criteria.where("orderRefundsId").is(refundId)),
                    Update.fromDocument(Document.parse(JsonUtils.toJson(kylinOrderRefundsVo))),
                    KylinOrderRefundsVo.class, KylinOrderRefundsVo.class.getSimpleName()
            );*/
            BasicDBObject objectUpdate = new BasicDBObject("$set", mongoConverter.convertToMongoType(kylinOrderRefundsVo));
            mongoTemplate.getCollection(KylinOrderRefundsVo.class.getSimpleName()).updateOne(
                    Query.query(Criteria.where("orderRefundsId").is(refundId)).getQueryObject(),
                    objectUpdate
            );

            dataUtils.delOrderRefundVo(refundId);
            dataUtils.delOrderRefundVoByOrderId(orderId);


            KylinOrderTickets oderInfo = kylinOrderTicketsMapper.selectOne(
                    Wrappers.lambdaQuery(KylinOrderTickets.class)
                            .eq(KylinOrderTickets::getOrderTicketsId, orderId)
            );
            BigDecimal refundPrice = refundInfo.getPrice().add(refundInfo.getPriceExpress());
            MultiValueMap<String, String> params = new LinkedMultiValueMap();
            params.add("code", oderInfo.getPayCode());
            params.add("notifyUrl", refundNotify);
            params.add("orderCode", oderInfo.getOrderCode());
            params.add("orderRefundCode", refundInfo.getOrderRefundCode());
            params.add("paymentId", oderInfo.getPaymentId());
            params.add("paymentType", oderInfo.getPaymentType());
            params.add("price", String.valueOf(refundPrice));
            params.add("priceTotal", String.valueOf(oderInfo.getPriceActual()));
            params.add("reason", "按需退款");

            MultiValueMap<String, String> headers = new LinkedMultiValueMap();
            headers.add("Accept", "application/json;charset=UTF-8");
            log.info("退款参数" + JsonUtils.toJson(params));
            // 请求pay
            String postResult = null;
            try {
                postResult = HttpUtil.post(refundApply, params, headers);
                log.info("退款res" + postResult);
                HashMap hashMapResult = JsonUtils.fromJson(postResult, HashMap.class);
                Boolean success = (Boolean) hashMapResult.get("success");
                if (!success) {
                    String message = (String) hashMapResult.get("message");
                    log.info("退款pay返回失败" + message);

                    KylinOrderRefunds kylinOrderRefundsFail = new KylinOrderRefunds();
                    kylinOrderRefundsFail.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_ERROR);
                    kylinOrderRefundsFail.setRefundError(message);
                    kylinOrderRefundsFail.setUpdatedAt(LocalDateTime.now());
                    kylinOrderRefundsMapper.update(
                            kylinOrderRefundsFail,
                            Wrappers.lambdaUpdate(KylinOrderRefunds.class).eq(KylinOrderRefunds::getOrderRefundsId, refundId)
                    );
                    // 修改缓存
                    KylinOrderRefundsVo kylinOrderRefundsVoFail = new KylinOrderRefundsVo();
                    BeanUtils.copyProperties(kylinOrderRefundsFail, kylinOrderRefundsVoFail);
                    /*BasicDBObject objectFail = new BasicDBObject("$set", mongoConverter.convertToMongoType(kylinOrderRefundsVoFail));
                    UpdateResult updateFailResult = mongoTemplate.getCollection(KylinOrderRefundsVo.class.getSimpleName()).updateOne(
                            Query.query(Criteria.where("orderRefundsId").is(refundId)).getQueryObject(),
                            objectFail
                    );*/
                    UpdateResult failResult = mongoTemplate.updateFirst(
                            Query.query(Criteria.where("orderRefundsId").is(refundId)),
                            Update.fromDocument(Document.parse(JsonUtils.toJson(kylinOrderRefundsVoFail))),
                            KylinOrderRefundsVo.class, KylinOrderRefundsVo.class.getSimpleName()
                    );
                    dataUtils.delOrderRefundVo(refundId);
                    dataUtils.delOrderRefundVoByOrderId(orderId);

                    continue;
                }
                // 同步票务平台 和 大麦 order申请时候就做了
            } catch (Exception e) {
                log.info("退款请求pay失败e" + e.getMessage());
                continue;
            }
        }
        return ResponseDto.success("success");
    }
}
