package com.liquidnet.client.admin.zhengzai.kylin.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.liquidnet.client.admin.common.utils.ShiroUtils;
import com.liquidnet.client.admin.common.utils.StringUtils;
import com.liquidnet.client.admin.zhengzai.kylin.utils.DataUtils;
import com.liquidnet.commons.lang.util.HttpUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.JsonUtils;
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.vo.mongo.KylinOrderTicketEntitiesVo;
import com.liquidnet.service.kylin.dto.vo.mongo.KylinOrderTicketVo;
import com.liquidnet.service.kylin.entity.KylinOrderRefunds;
import com.liquidnet.service.kylin.entity.KylinOrderTicketEntities;
import com.liquidnet.service.kylin.entity.KylinOrderTicketStatus;
import com.liquidnet.service.kylin.entity.KylinOrderTickets;
import com.liquidnet.service.kylin.mapper.KylinOrderRefundsMapper;
import com.liquidnet.service.kylin.mapper.KylinOrderTicketEntitiesMapper;
import com.liquidnet.service.kylin.mapper.KylinOrderTicketStatusMapper;
import com.liquidnet.service.kylin.mapper.KylinOrderTicketsMapper;
import com.mongodb.BasicDBObject;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.ReturnDocument;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
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.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
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.List;
import java.util.stream.Collectors;

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

    @Value("${liquidnet.url-refund.apply}")
    private String applyUrl;
    @Value("${liquidnet.url-refund.notify}")
    private String notifyUrl;

    @Autowired
    private KylinOrderTicketsMapper kylinOrderTicketsMapper;

    @Autowired
    private KylinOrderTicketStatusMapper kylinOrderTicketStatusMapper;

    @Autowired
    private KylinOrderRefundsMapper kylinOrderRefundsMapper;

    @Autowired
    private KylinOrderTicketEntitiesMapper kylinOrderTicketEntitiesMapper;

    @Autowired
    MongoTemplate mongoTemplate;

    @Autowired
    private DataUtils dataUtils;

    public Boolean orderTicketRefunding(
            RefundApplyParam refundApplyParam, KylinOrderTickets orderInfo, String orderTicketsId,
            double RefundPriceExpress, double priceExpress,
            int ticketEntityCount, List<String> ticketEntityIds
    ) {
        // 处理数据
        String authId = ShiroUtils.getUserId().toString();
        String authName = ShiroUtils.getLoginName();
        String reason = refundApplyParam.getReason();
        String orderRefundBatchesId = refundApplyParam.getOrderRefundBatchesId();
        double priceActual = orderInfo.getPriceActual().doubleValue();
        // 本次退款总金额
        // 总入场人数量 排出未付款的 用来计算单入场人的价格
        int allEntitiesCount = kylinOrderTicketEntitiesMapper.selectCount(
                new QueryWrapper<KylinOrderTicketEntities>()
                        .eq("order_id", orderTicketsId)
                        .ne("is_payment", KylinTableStatusConst.ENTITIES_IS_PAYMENT0)
        );
        double refundTotalPrice = RefundPriceExpress + ((priceActual - priceExpress) / allEntitiesCount * ticketEntityCount);

        // 更新数据
        // 订单状态表
        KylinOrderTicketStatus orderStatusTable = new KylinOrderTicketStatus();
        // TODO: 2021/5/27 事物 and 部分退款
        orderStatusTable.setStatus(KylinTableStatusConst.ORDER_STATUS3);
        kylinOrderTicketStatusMapper.update(orderStatusTable, new UpdateWrapper<KylinOrderTicketStatus>()
                .eq("order_id", orderTicketsId));

        KylinOrderTicketVo kylinOrderTicketVo = new KylinOrderTicketVo();
        kylinOrderTicketVo.setStatus(KylinTableStatusConst.ORDER_STATUS3);
        BasicDBObject orderObject = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(kylinOrderTicketVo)));
        Document orderDoc = mongoTemplate.getCollection(KylinOrderTicketVo.class.getSimpleName()).findOneAndUpdate(
                Query.query(Criteria.where("orderTicketsId").is(orderTicketsId)).getQueryObject(),
                orderObject,
                new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
        );
        dataUtils.delOrderTicketRedis(orderTicketsId);


        // 入场人
        for (String v : ticketEntityIds) {
            KylinOrderTicketEntities entitiesTable = new KylinOrderTicketEntities();
            // TODO: 2021/5/27 事物 and 部分退款
            entitiesTable.setIsPayment(KylinTableStatusConst.ENTITIES_IS_PAYMENT2);
            kylinOrderTicketEntitiesMapper.update(entitiesTable, new UpdateWrapper<KylinOrderTicketEntities>()
                    .eq("order_ticket_entities_id", v));

            KylinOrderTicketEntitiesVo kylinOrderTicketEntitiesVo = new KylinOrderTicketEntitiesVo();
            kylinOrderTicketEntitiesVo.setIsPayment(KylinTableStatusConst.ENTITIES_IS_PAYMENT2);
            BasicDBObject entitiesObject = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(kylinOrderTicketEntitiesVo)));
            Document entitiesDoc = mongoTemplate.getCollection(KylinOrderTicketEntitiesVo.class.getSimpleName()).findOneAndUpdate(
                    Query.query(Criteria.where("orderTicketEntitiesId").is(v)).getQueryObject(),
                    entitiesObject,
                    new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
            );

            dataUtils.delOrderTicketEntitiesRedis(v);
        }

        // 退款明细
        KylinOrderRefunds kylinOrderRefunds = new KylinOrderRefunds();
        String orderRefundsId = IDGenerator.nextSnowId().toString();
        kylinOrderRefunds.setOrderRefundsId(orderRefundsId);
        kylinOrderRefunds.setOrderTicketsId(orderTicketsId);
        kylinOrderRefunds.setOrderRefundBatchesId(orderRefundBatchesId);

        Integer refundCount = kylinOrderRefundsMapper.selectCount(
                new QueryWrapper<KylinOrderRefunds>()
                        .eq("order_tickets_id", orderTicketsId)
        );

        String orderRefundCode = orderInfo.getOrderCode();
        String codeNum = StringUtils.leftPad(String.valueOf(refundCount), 3, "0");
        kylinOrderRefunds.setOrderRefundCode(orderRefundCode.concat(codeNum));
        kylinOrderRefunds.setPrice(BigDecimal.valueOf(refundTotalPrice));
        kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_APPLY);
        kylinOrderRefunds.setType(0);
        kylinOrderRefunds.setApplicantId(authId);
        kylinOrderRefunds.setApplicantName(authName);
        kylinOrderRefunds.setApplicantAt(LocalDateTime.now());
        kylinOrderRefunds.setReason(reason);
        // TODO: 2021/5/27 判断tyoe
        if (RefundPriceExpress > 0) {
            kylinOrderRefunds.setRefundType("all");
        } else {
            kylinOrderRefunds.setRefundType("ticket");
        }
        kylinOrderRefunds.setOrderTicketEntitiesIds(StringUtils.join(ticketEntityIds, ','));
        kylinOrderRefunds.setCreatedAt(LocalDateTime.now());
        int rows = kylinOrderRefundsMapper.insert(kylinOrderRefunds);

        return true;
    }

    public boolean orderTicketRefundCancel(List<KylinOrderRefunds> refundList) {
        for (KylinOrderRefunds refundInfo : refundList) {
            String orderTicketEntitiesIds = refundInfo.getOrderTicketEntitiesIds();
            String[] orderTicketEntitiesIdsArr = orderTicketEntitiesIds.split(",");
            String orderTicketsId = refundInfo.getOrderTicketsId();
            String orderRefundsId = refundInfo.getOrderRefundsId();

            // 更新数据
            // 订单状态表
            KylinOrderTicketStatus orderStatusTable = new KylinOrderTicketStatus();
            // TODO: 2021/5/27 事物 and 部分退款
            orderStatusTable.setStatus(KylinTableStatusConst.ORDER_STATUS1);
            kylinOrderTicketStatusMapper.update(orderStatusTable, new UpdateWrapper<KylinOrderTicketStatus>()
                    .eq("order_id", orderTicketsId));

            KylinOrderTicketVo kylinOrderTicketVo = new KylinOrderTicketVo();
            kylinOrderTicketVo.setStatus(KylinTableStatusConst.ORDER_STATUS1);
            BasicDBObject orderObject = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(kylinOrderTicketVo)));
            Document orderDoc = mongoTemplate.getCollection(KylinOrderTicketVo.class.getSimpleName()).findOneAndUpdate(
                    Query.query(Criteria.where("orderTicketsId").is(orderTicketsId)).getQueryObject(),
                    orderObject,
                    new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
            );
            dataUtils.delOrderTicketRedis(orderTicketsId);


            // 入场人
            for (String entitiesId : orderTicketEntitiesIdsArr) {
                KylinOrderTicketEntities entitiesTable = new KylinOrderTicketEntities();
                entitiesTable.setIsPayment(KylinTableStatusConst.ENTITIES_IS_PAYMENT1);
                kylinOrderTicketEntitiesMapper.update(entitiesTable, new UpdateWrapper<KylinOrderTicketEntities>()
                        .eq("order_ticket_entities_id", entitiesId)
                        .eq("is_payment", KylinTableStatusConst.ENTITIES_IS_PAYMENT2)
                );

                KylinOrderTicketEntitiesVo kylinOrderTicketEntitiesVo = new KylinOrderTicketEntitiesVo();
                kylinOrderTicketEntitiesVo.setIsPayment(KylinTableStatusConst.ENTITIES_IS_PAYMENT1);
                BasicDBObject entitiesObject = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(kylinOrderTicketEntitiesVo)));
                Document entitiesDoc = mongoTemplate.getCollection(KylinOrderTicketEntitiesVo.class.getSimpleName()).findOneAndUpdate(
                        Query.query(Criteria.where("orderTicketEntitiesId").is(entitiesId)).getQueryObject(),
                        entitiesObject,
                        new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
                );

                dataUtils.delOrderTicketEntitiesRedis(entitiesId);
            }

            // 退款细节取消
            KylinOrderRefunds kylinOrderRefunds = new KylinOrderRefunds();
            kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_CANCEL);
            kylinOrderRefunds.setUpdatedAt(LocalDateTime.now());
            kylinOrderRefundsMapper.update(kylinOrderRefunds, new UpdateWrapper<KylinOrderRefunds>()
                    .eq("order_refunds_id", orderRefundsId));

        }
        return true;
    }

    public boolean orderRefundChangeStatus(List<KylinOrderRefunds> refundList, String type, String reject, String refuse) {
        String authId = ShiroUtils.getUserId().toString();
        String authName = ShiroUtils.getLoginName();
        KylinOrderRefunds kylinOrderRefunds = new KylinOrderRefunds();

        switch (type) {
            case "reapply":
                kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_APPLY);
                kylinOrderRefunds.setApplicantId(authId);
                kylinOrderRefunds.setApplicantName(authName);
                kylinOrderRefunds.setApplicantAt(LocalDateTime.now());
                break;
            case "approved":
                kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_APPROVED);
                kylinOrderRefunds.setAuditorId(authId);
                kylinOrderRefunds.setAuditorName(authName);
                kylinOrderRefunds.setAuditorAt(LocalDateTime.now());
                kylinOrderRefunds.setReject(reject);
                break;
            case "Reject":
                kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_REJECT);
                kylinOrderRefunds.setAuditorId(authId);
                kylinOrderRefunds.setAuditorName(authName);
                kylinOrderRefunds.setAuditorAt(LocalDateTime.now());
                kylinOrderRefunds.setReject(reject);
                break;
            case "refuse":
                kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_REFUSE);
                kylinOrderRefunds.setExecutorId(authId);
                kylinOrderRefunds.setExecutorName(authName);
                kylinOrderRefunds.setExecutorAt(LocalDateTime.now());
                kylinOrderRefunds.setRefuse(refuse);
                break;
        }

        List<String> orderRefundIds = refundList.stream().map(
                KylinOrderRefunds -> KylinOrderRefunds.getOrderRefundsId()).collect(Collectors.toList()
        );
        kylinOrderRefundsMapper.update(
                kylinOrderRefunds,
                new UpdateWrapper<KylinOrderRefunds>().in("order_refunds_id", orderRefundIds)
        );

        return true;
    }

    public boolean orderTicketRefundUnfilled(List<KylinOrderRefunds> refundList, String refuse) {
        String authId = ShiroUtils.getUserId().toString();
        String authName = ShiroUtils.getLoginName();

        KylinOrderRefunds kylinOrderRefunds = new KylinOrderRefunds();
        kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_UNFILLED);
        kylinOrderRefunds.setExecutorId(authId);
        kylinOrderRefunds.setExecutorName(authName);
        kylinOrderRefunds.setExecutorAt(LocalDateTime.now());
        kylinOrderRefunds.setRefuse(refuse);
//        List<String> refundIds = refundList.stream().map(KylinOrderRefunds -> KylinOrderRefunds.getOrderRefundsId()).collect(Collectors.toList());

        for (KylinOrderRefunds refund : refundList) {
            KylinOrderTickets oderInfo = kylinOrderTicketsMapper.selectOne(
                    new QueryWrapper<KylinOrderTickets>()
                            .eq("order_tickets_id", refund.getOrderTicketsId())
            );
            MultiValueMap<String, String> params = new LinkedMultiValueMap();
            params.add("code", oderInfo.getPayCode());
            params.add("order_refund_code", refund.getOrderRefundCode());
            params.add("price", refund.getPrice().toString());
            params.add("reason", refund.getReason());
            params.add("notify_url", notifyUrl);

            String sign = StringUtils.Ksort(params);
            sign = sign.concat("&key=").concat("R7tXY9smPQPG9Ku5yI0u6sfnlckmk04V");
//            sign = MD5.getStrMD5(sign);
            sign = sign.toUpperCase();
            params.add("sign", sign);


            // start
            // TODO: 2021/5/31 待整理
            String postResult = null;
            postResult = HttpUtil.post(applyUrl, params);
            JsonNode postResultNew = JsonUtils.fromJson(postResult, JsonNode.class);
            // 请求提审接口结果
            if (postResultNew.get("message").toString() != "OK") {
                KylinOrderRefunds kylinOrderRefundsErr = new KylinOrderRefunds();
                kylinOrderRefundsErr.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_ERROR);
                kylinOrderRefundsErr.setRefundError("通知退款中心失败");
                kylinOrderRefundsErr.setRefundAt(LocalDateTime.now());
                kylinOrderRefundsMapper.update(
                        kylinOrderRefundsErr,
                        new UpdateWrapper<KylinOrderRefunds>().eq("order_refunds_id", refund.getOrderRefundsId())
                );
            } else {
                kylinOrderRefundsMapper.update(
                        kylinOrderRefunds,
                        new UpdateWrapper<KylinOrderRefunds>().in("order_refunds_id", refund.getOrderRefundsId())
                );
            }
        }

        return true;
    }

    public boolean orderTicketRefunded(RefundCallbackParam refundCallbackParam, KylinOrderRefunds refundInfo) {
        String orderTicketEntitiesIds = refundInfo.getOrderTicketEntitiesIds();
        String[] orderTicketEntitiesIdsArr = orderTicketEntitiesIds.split(",");
        Integer EntitiesIdsCount = orderTicketEntitiesIdsArr.length;
        String orderTicketsId = refundInfo.getOrderTicketsId();

        // 更新数据
        // 订单状态表
        KylinOrderTickets orderInfo = kylinOrderTicketsMapper.selectOne(
                new QueryWrapper<KylinOrderTickets>().eq("order_tickets_id", orderTicketsId)
        );
        KylinOrderTicketStatus orderStatusTable = new KylinOrderTicketStatus();
        int newStatus = 0;
        if (EntitiesIdsCount + orderInfo.getRefundNumber() == orderInfo.getNumber()) {
            newStatus = KylinTableStatusConst.ORDER_STATUS4;
        } else {
            newStatus = KylinTableStatusConst.ORDER_STATUS6;
        }
        orderStatusTable.setStatus(newStatus);
        kylinOrderTicketStatusMapper.update(
                orderStatusTable, new UpdateWrapper<KylinOrderTicketStatus>()
                        .eq("order_id", orderTicketsId)
        );

        // 订单表
        double price = orderInfo.getPriceRefund().doubleValue() + refundInfo.getPrice().doubleValue();
        Integer num = orderInfo.getRefundNumber() + EntitiesIdsCount;
        KylinOrderTickets update = new KylinOrderTickets();
        update.setRefundNumber(num);
        update.setPriceRefund(BigDecimal.valueOf(price));
        kylinOrderTicketsMapper.update(
                update, new UpdateWrapper<KylinOrderTickets>()
                        .eq("order_tickets_id", orderTicketsId)
        );
        KylinOrderTicketVo kylinOrderTicketVoOrder = new KylinOrderTicketVo();
        kylinOrderTicketVoOrder.setStatus(newStatus);
        kylinOrderTicketVoOrder.setRefundNumber(num);
        kylinOrderTicketVoOrder.setPriceRefund(BigDecimal.valueOf(price));
        BasicDBObject orderEntitiesObject = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(kylinOrderTicketVoOrder)));
        Document docOrder = mongoTemplate.getCollection(KylinOrderTicketVo.class.getSimpleName()).findOneAndUpdate(
                Query.query(Criteria.where("orderTicketsId").is(orderTicketsId)).getQueryObject(),
                orderEntitiesObject,
                new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
        );
        dataUtils.delOrderTicketRedis(orderTicketsId);

        // 入场人
        for (String entitiesId : orderTicketEntitiesIdsArr) {
            KylinOrderTicketEntities entitiesTable = new KylinOrderTicketEntities();
            // TODO: 2021/5/27 事物 and 部分退款
            entitiesTable.setIsPayment(KylinTableStatusConst.ENTITIES_IS_PAYMENT3);
            kylinOrderTicketEntitiesMapper.update(entitiesTable, new UpdateWrapper<KylinOrderTicketEntities>()
                    .eq("order_ticket_entities_id", entitiesId)
                    .eq("is_payment", KylinTableStatusConst.ENTITIES_IS_PAYMENT2)
            );

            KylinOrderTicketEntitiesVo kylinOrderTicketEntitiesVo = new KylinOrderTicketEntitiesVo();
            kylinOrderTicketEntitiesVo.setIsPayment(KylinTableStatusConst.ENTITIES_IS_PAYMENT3);
            BasicDBObject entitiesObject = new BasicDBObject("$set", JSON.parse(JsonUtils.toJson(kylinOrderTicketEntitiesVo)));
            Document entitiesDoc = mongoTemplate.getCollection(KylinOrderTicketEntitiesVo.class.getSimpleName()).findOneAndUpdate(
                    Query.query(Criteria.where("orderTicketEntitiesId").is(entitiesId)).getQueryObject(),
                    entitiesObject,
                    new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
            );

            dataUtils.delOrderTicketEntitiesRedis(entitiesId);
        }

        // 退款单完成
        KylinOrderRefunds kylinOrderRefunds = new KylinOrderRefunds();
        kylinOrderRefunds.setStatus(KylinTableStatusConst.ORDER_REFUND_STATUS_REFUNDED);
        kylinOrderRefunds.setRefundCode(refundCallbackParam.getRefund_code());
        kylinOrderRefunds.setRefundType(refundCallbackParam.getRefund_type());
        kylinOrderRefunds.setRefundId(refundCallbackParam.getRefund_id());
        kylinOrderRefunds.setRefundAt(refundCallbackParam.getRefund_at());
        kylinOrderRefunds.setRefundError(refundCallbackParam.getRefund_error());

        kylinOrderRefundsMapper.update(
                kylinOrderRefunds,
                new UpdateWrapper<KylinOrderRefunds>().in("order_refunds_id", refundInfo.getOrderRefundsId())
        );

        if (refundInfo.getType() == KylinTableStatusConst.ORDER_REFUND_TYPE_APPLY) {
            // 退还库存
            for (String entitiesId : orderTicketEntitiesIdsArr) {
                dataUtils.changeSurplusGeneral(entitiesId, 1);
            }
        }

        return true;
    }
}
