package com.liquidnet.service.order.service.impl;


import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.type.TypeReference;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.constant.NftAccStatusEnum;
import com.liquidnet.service.goblin.dto.GoblinUserNftAccInfoVo;
import com.liquidnet.service.goblin.dto.manage.AddressVo;
import com.liquidnet.service.goblin.dto.manage.GoblinOrderSkuParam;
import com.liquidnet.service.goblin.dto.manage.GoblinOrderStoreParam;
import com.liquidnet.service.goblin.dto.manage.MixOrderParam;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinNftOrder;
import com.liquidnet.service.goblin.entity.GoblinOrderAttr;
import com.liquidnet.service.goblin.entity.GoblinOrderSku;
import com.liquidnet.service.goblin.entity.GoblinStoreOrder;
import com.liquidnet.service.goblin.param.GoblinNftOrderPayCallbackParam;
import com.liquidnet.service.goblin.param.GoblinOrderPreParam;
import com.liquidnet.service.goblin.param.SyncOrderParam;
import com.liquidnet.service.goblin.service.IMixOrderService;
import com.liquidnet.service.order.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.tomcat.jni.Local;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

import static com.liquidnet.commons.lang.util.DateUtil.DTF_YMD_HMS;
import static com.liquidnet.commons.lang.util.DateUtil.getNowTime;


@Service
@Slf4j
public class MixOrderServiceImpl implements IMixOrderService {

    @Autowired
    GoblinRedisUtils redisUtils;
    @Autowired
    GoblinMongoUtils mongoUtils;
    @Autowired
    GoblinOrderUtils orderUtils;
    @Autowired
    GoblinNftOrderUtils nftOrderUtils;
    @Autowired
    QueueUtils queueUtils;

    @Value("${liquidnet.service.order.url}")
    private String orderUrl;
    @Value("${liquidnet.service.order.url-pay.pay}")
    private String payUrl;
    @Value("${liquidnet.service.order.url-pay.check}")
    private String checkUrl;

    @Override
    public ResponseDto<GoblinPayInnerResultVo> checkOrder(MixOrderParam param, String uid) {
        List<String> canBuyIds = CollectionUtil.linkedListString();//有库存的id
        String mixId = param.getMixId();
        String orderMasterCode = IDGenerator.storeMasterCode();//总订单号
        HashMap<String, Object> boxSkuId = CollectionUtil.mapStringObject();//盲盒种的skuid
        HashMap<String, Object> stockSkuId = CollectionUtil.mapStringObject();  // 盲盒id
        boolean isUseLimit = false;//是否减限购
        try {
            GoblinMixDetailsVo mixVo = redisUtils.getMixDetails(param.getMixId());
            String storeName = redisUtils.getStoreInfoVo(mixVo.getStoreId()).getStoreName();
            String mobile = StringUtils.defaultString(((String) CurrentUtil.getTokenClaims().get(CurrentUtil.TOKEN_MOBILE)), "");
            String nickName = StringUtils.defaultString(((String) CurrentUtil.getTokenClaims().get(CurrentUtil.TOKEN_NICKNAME)), "");
            String headerCliSource = CurrentUtil.getHeaderCliSource(), headerCliVersion = CurrentUtil.getHeaderCliVersion();
            String source = headerCliSource == null ? "" : headerCliSource, version = headerCliVersion == null ? "" : headerCliVersion;
            //判断 活动状态
            LocalDateTime st = LocalDateTime.parse(mixVo.getTimeStart(), DTF_YMD_HMS);
            LocalDateTime et = LocalDateTime.parse(mixVo.getTimeEnd(), DTF_YMD_HMS);
            LocalDateTime nt = LocalDateTime.now();
            if (!(nt.isAfter(st) && nt.isBefore(et)) || mixVo.getStatus() == 7) {
                return ResponseDto.failure("不在活动有效期内");
            }
            //判断白名单
            int whiteType = mixVo.getWhiteType();
            if (whiteType != 2) {
                Boolean canBuy = redisUtils.getListCanBuy(mixId, mixId, mobile, uid, whiteType);
                if (!canBuy) {
                    return ResponseDto.failure("该藏品仅对部分用户开放～");
                }
            }
            //判断限购
            Integer limitCount = mixVo.getLimit();
            if (limitCount != 0) {
                int buyCount = redisUtils.incrMixLimit(mixId, uid);
                isUseLimit = true;
                log.debug("limit = " + buyCount);
                if (buyCount > limitCount) {
                    redisUtils.decrMixLimit(mixId, uid);
                    return ResponseDto.failure("超出限购数量");
                }
            } else {
                int buyCount = redisUtils.incrMixLimit(mixId, uid);
            }
            //构建 各个商品信息
            List<GoblinGoodsSkuInfoVo> skuInfoList = ObjectUtil.cloneArrayGoblinGoodsSkuInfoListVo();
            List<GoblinGoodsSkuInfoVo> nftInfoList = ObjectUtil.cloneArrayGoblinGoodsSkuInfoListVo();
            HashMap<String, Object> skuMix = CollectionUtil.mapStringObject();//mix配置的sku数量和价格
            HashMap<String, Object> nftMix = CollectionUtil.mapStringObject();//mix配置的nft数量和价格
            for (GoblinMixDetailsItemVo itemVo : mixVo.getItem()) {
                String skuId = itemVo.getSkuId();
                GoblinGoodsSkuInfoVo skuInfoVo = redisUtils.getGoodsSkuInfoVo(skuId);
                //库存处理
                if (skuInfoVo.getUnbox().equals("1")) {//盲盒
                    GoblinGoodsInfoVo spuInfoVo = redisUtils.getGoodsInfoVo(skuInfoVo.getSpuId());
                    List<String> skuIdList = spuInfoVo.getSkuIdList();
                    if (CollectionUtil.isEmpty(skuIdList)) {
                        initStock(canBuyIds, mixId, isUseLimit, uid);
                        return ResponseDto.failure("该商品SPU不存在～");
                    } else {
                        GoblinGoodsSkuInfoVo boxSkuInfo = nftOrderUtils.lotteryDraw(skuIdList, itemVo.getCount(), nt, mixId);
                        if (null == boxSkuInfo) {
                            return ResponseDto.failure("库存不足啦～");
                        } else {
                            log.debug("stockId = " + boxSkuInfo.getSkuId() + " stock = " + itemVo.getCount());
                            canBuyIds.add(boxSkuInfo.getSkuId() + "," + itemVo.getCount());
                        }
                        boxSkuId.put(skuId, boxSkuInfo.getSkuId());
                        stockSkuId.put(skuId, skuInfoVo.getSkuId());
                    }
                } else {//正常
                    if (skuInfoVo.getSkuType() == 1) {
                        stockSkuId.put(skuId, skuId);
                        boxSkuId.put(skuId, "");
                    }
                    int stock = redisUtils.decrSkuStock(mixId, skuId, itemVo.getCount());
                    if (stock < 0) {
                        redisUtils.incrSkuStock(mixId, skuId, itemVo.getCount());
                        for (String canBuyId : canBuyIds) {
                            String[] data = canBuyId.split(",");
                            String skuIdSingle = data[0];
                            int count = Integer.parseInt(data[1]);
                            redisUtils.incrSkuStock(mixId, skuIdSingle, count);
                        }
                        return ResponseDto.failure("库存不足");
                    } else {
                        canBuyIds.add(skuId + "," + itemVo.getCount());
                    }
                }
                if (skuInfoVo.getSkuType() == 0) {
                    skuInfoList.add(skuInfoVo);
                    skuMix.put(skuInfoVo.getSkuId() + "price", itemVo.getPrice());
                    skuMix.put(skuInfoVo.getSkuId() + "count", itemVo.getCount());
                } else if (skuInfoVo.getSkuType() == 1) {
                    nftInfoList.add(skuInfoVo);
                    nftMix.put(skuInfoVo.getSkuId() + "price", itemVo.getPrice());
                    nftMix.put(skuInfoVo.getSkuId() + "priceV", itemVo.getPriceV());
                    nftMix.put(skuInfoVo.getSkuId() + "produceId", itemVo.getProductId());
                    nftMix.put(skuInfoVo.getSkuId() + "count", itemVo.getCount());
                } else {
                    initStock(canBuyIds, mixId, isUseLimit, uid);
                    return ResponseDto.failure("参数异常");
                }
            }
            // 认证验证
            if (nftInfoList.size() > 0) {
                GoblinUserNftAccInfoVo openAccountInfo = redisUtils.getOpenAccountInfo(uid);
                if (null == openAccountInfo || !NftAccStatusEnum.StatusAcc.SUCCESS.getCode().equals(openAccountInfo.getCode())) {
                    initStock(canBuyIds, mixId, isUseLimit, uid);
                    return ResponseDto.failure("您还未开通数字账户");
                }
            }
            //下单
            //生成nft订单
            List<GoblinNftOrder> nftOrderList = ObjectUtil.goblinNftOrderList();
            if (nftInfoList.size() > 0) {
                nftOrderList = nftOrder(nftInfoList, nftMix, boxSkuId, mobile, source, version, nickName, nt, uid, param.getPayType(), param.getDeviceFrom());
            }
            //生成goblin订单
            GoblinOrderPreParam preParam = null;
            if (skuInfoList.size() > 0) {
                preParam = goblinOrder(skuInfoList, skuMix, param.getAddressesVo(), mobile, source, version, nickName, nt, uid, param.getPayType(), param.getDeviceFrom(),orderMasterCode);
            }
            return ResponseDto.success(payOrder(nftOrderList, preParam, orderMasterCode, param.getShowUrl(), param.getReturnUrl(), param.getDeviceFrom(), param.getPayType(), mixId, mixVo.getName(), param.getOpenId(), nt, storeName));
        } catch (Exception e) {
            e.printStackTrace();
            initStock(canBuyIds, mixId, isUseLimit, uid);
            return ResponseDto.failure();
        }
    }

    //商品订单
    private GoblinOrderPreParam goblinOrder(List<GoblinGoodsSkuInfoVo> skuVoList, HashMap<String, Object> skuMix, AddressVo addressVo, String mobile, String source, String version, String nickName, LocalDateTime now, String uid, String payType, String deviceFrom,String masterCode) {
        String orderMasterCode = masterCode;
        String orderId = IDGenerator.nextSnowId();
        String orderCode = IDGenerator.storeCode(orderId);
        String storeId = "";
        BigDecimal storeTotalPrice = BigDecimal.ZERO;//订单总金额
        String skuName = "";
        List<GoblinOrderSku> goblinOrderSkuList = ObjectUtil.getGoblinOrderSkuArrayList();

        for (GoblinGoodsSkuInfoVo skuVo : skuVoList) {
            storeId = skuVo.getStoreId();
            BigDecimal price;//sku应付价格
            BigDecimal priceBase;//sku原价
            log.debug("number key = " + skuVo.getSkuId() + "count");
            int number = (Integer) skuMix.get(skuVo.getSkuId() + "count");
            GoblinGoodsInfoVo spuVo = redisUtils.getGoodsInfoVo(skuVo.getSpuId());
            skuName = skuName.concat(skuVo.getName()).concat(",");
            //获得 活动原价
            price = (BigDecimal) skuMix.get(skuVo.getSkuId() + "price");
            priceBase = skuVo.getPrice();
//            price = price.multiply(BigDecimal.valueOf(number));
            storeTotalPrice = storeTotalPrice.add(price);
            //订单sku
            GoblinOrderSku orderSku = GoblinOrderSku.getNew();
            orderSku.setOrderSkuId(IDGenerator.nextTimeId2());
            orderSku.setOrderId(orderId);
            orderSku.setStatus(GoblinStatusConst.Status.ORDER_STATUS_0.getValue());
            orderSku.setSpuId(skuVo.getSpuId());
            orderSku.setSpuName(spuVo.getName());
            orderSku.setSkuId(skuVo.getSkuId());
            orderSku.setNum(number);
            orderSku.setSkuPrice(priceBase);
            orderSku.setSkuPriceActual(price);
            orderSku.setSkuName(skuVo.getName());
            orderSku.setSkuNo(skuVo.getSkuNo());
            orderSku.setSkuImage(skuVo.getSkuPic());
            orderSku.setSkuSpecs(JSON.toJSONString(skuVo.getSkuSpecList()));
            orderSku.setPriceVoucher(BigDecimal.ZERO);
            orderSku.setCreatedAt(now);
            orderSku.setSpuName(spuVo.getName());
            orderSku.setSpuPic(spuVo.getCoverPic());
            goblinOrderSkuList.add(orderSku);
        }
        GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVo(storeId);
        //生成订单
        GoblinStoreOrder storeOrder = GoblinStoreOrder.getNew();
        storeOrder.setMasterOrderCode(orderMasterCode);
        storeOrder.setOrderId(orderId);
        storeOrder.setStoreId(storeId);
        storeOrder.setStoreName(storeInfoVo.getStoreName());
        storeOrder.setOrderCode(orderCode);
        storeOrder.setUserId(uid);
        storeOrder.setUserName(nickName);
        storeOrder.setUserMobile(mobile);
        storeOrder.setPriceExpress(BigDecimal.ZERO);
        storeOrder.setPriceTotal(storeTotalPrice.add(storeOrder.getPriceExpress()));
        BigDecimal priceActual = storeOrder.getPriceTotal();
        if (priceActual.compareTo(storeOrder.getPriceExpress()) < 0) {
            priceActual = storeOrder.getPriceExpress();
        }
        storeOrder.setPriceActual(priceActual.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : priceActual);
        storeOrder.setPriceRefund(BigDecimal.ZERO);
        storeOrder.setPriceCoupon(BigDecimal.ZERO);
        storeOrder.setStorePriceCoupon(BigDecimal.ZERO);
        storeOrder.setPriceVoucher(BigDecimal.ZERO);
        storeOrder.setStatus(0);
        storeOrder.setUcouponId("");
        storeOrder.setStoreCouponId("");
        storeOrder.setPayType(payType);
        storeOrder.setDeviceFrom(deviceFrom);
        storeOrder.setSource(source);
        storeOrder.setVersion(version);
        storeOrder.setIsMember(-1);
        storeOrder.setOrderType(0);
        storeOrder.setWriteOffCode("");
        storeOrder.setPayCountdownMinute(5);
        storeOrder.setIpAddress(CurrentUtil.getCliIpAddr());
        storeOrder.setCreatedAt(now);
        storeOrder.setMarketId("");
        storeOrder.setMarketType("");

        //订单attr
        GoblinOrderAttr orderAttr = GoblinOrderAttr.getNew();
        orderAttr.setOrderAttrId(IDGenerator.nextTimeId2());
        orderAttr.setOrderId(orderId);
        orderAttr.setExpressContacts(addressVo.getName());
        orderAttr.setExpressAddress(addressVo.getProvince() + " " + addressVo.getCity() + " " + addressVo.getCounty());
        orderAttr.setExpressAddressDetail(addressVo.getAddress());
        orderAttr.setExpressPhone(addressVo.getPhone());
        orderAttr.setExpressType(1);
        orderAttr.setCreatedAt(now);

        //返回值
        GoblinOrderPreParam preParam = GoblinOrderPreParam.getNew();
        preParam.setPriceActual(storeOrder.getPriceActual());
        preParam.setStoreName(storeInfoVo.getStoreName());
        preParam.setSkuName(skuName);
        preParam.setOrderIdList(storeOrder.getOrderId());
        preParam.setStoreOrder(storeOrder);
        preParam.setOrderAttr(orderAttr);
        preParam.setOrderSkuList(goblinOrderSkuList);

        return preParam;
    }

    //nft订单
    private List<GoblinNftOrder> nftOrder(List<GoblinGoodsSkuInfoVo> nftSkuVo, HashMap<String, Object> nftMix, HashMap<String, Object> boxSkuId, String mobile, String source, String version, String nickName, LocalDateTime now, String uid, String payType, String deviceFrom) {
        List<GoblinNftOrder> goblinNftOrderList = ObjectUtil.goblinNftOrderList();
        for (GoblinGoodsSkuInfoVo skuVo : nftSkuVo) {
            BigDecimal voucherPrice = BigDecimal.ZERO;
            BigDecimal storeVoucherPrice = BigDecimal.ZERO;
            String orderId = IDGenerator.nextSnowId();
            String orderCode = IDGenerator.storeCode(orderId);
            BigDecimal totalPrice = (BigDecimal) nftMix.get(skuVo.getSkuId() + "price");
            if (payType.equals("applepay")) {
                totalPrice = (BigDecimal) nftMix.get(skuVo.getSkuId() + "priceV");
            }
            GoblinStoreInfoVo storeInfoVo = redisUtils.getStoreInfoVo(skuVo.getStoreId());
            String storeName = storeInfoVo.getStoreName();
            // 生成订单
            GoblinNftOrder nftOrder = GoblinNftOrder.getNew();
            nftOrder.setOrderId(orderId);
            nftOrder.setSpuId(skuVo.getSpuId());
            nftOrder.setSkuId(skuVo.getSkuId());
            nftOrder.setSkuTitle(skuVo.getName());
            nftOrder.setBoxSkuId((String) boxSkuId.get(skuVo.getSkuId()));
            nftOrder.setNum(1);
            nftOrder.setStoreId(skuVo.getStoreId());
            nftOrder.setStoreName(storeName);
            nftOrder.setOrderCode(orderCode);
            nftOrder.setUserId(uid);
            nftOrder.setUserName(nickName);
            nftOrder.setUserMobile(mobile);
            nftOrder.setListId("");
            nftOrder.setExCode("");
            nftOrder.setPriceTotal(totalPrice);
            nftOrder.setPriceCoupon(voucherPrice);
            nftOrder.setStorePriceCoupon(storeVoucherPrice);
            nftOrder.setPriceRedEnvelope(BigDecimal.ZERO);
            nftOrder.setPriceVoucher(voucherPrice.add(storeVoucherPrice));
            BigDecimal priceActual = totalPrice.subtract(voucherPrice).subtract(storeVoucherPrice);
            nftOrder.setPriceActual(priceActual.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : priceActual);
            nftOrder.setUcouponId("");
            nftOrder.setStoreCouponId("");
            nftOrder.setRedEnvelopeCode("");
            nftOrder.setStatus(GoblinStatusConst.NftStatus.ORDER_STATUS_1.getValue());
            nftOrder.setSource(source);
            nftOrder.setOrderType(1);
            nftOrder.setPayType(payType);
            nftOrder.setDeviceFrom(deviceFrom);
            nftOrder.setVersion(version);
            nftOrder.setPayCountdownMinute(5);
            nftOrder.setIpAddress(CurrentUtil.getCliIpAddr());
            nftOrder.setCreatedAt(now);

            goblinNftOrderList.add(nftOrder);
        }
        return goblinNftOrderList;
    }


    private GoblinPayInnerResultVo payOrder(List<GoblinNftOrder> nftOrder, GoblinOrderPreParam preParam, String masterCode, String showUrl, String returnUrl, String deviceForm, String payType, String mixId, String mixName, String openId, LocalDateTime now, String storeName) {
        GoblinPayInnerResultVo NftPayResultVo = GoblinPayInnerResultVo.getNew();
        //是否免费
        boolean isFree = false;
        BigDecimal payPrice = BigDecimal.ZERO;
        String details = "";
        String payCode;
        String orderStr = "";
        String orderMixStr = "";
        String nftOrderStr = "";

        for (GoblinNftOrder item : nftOrder) {
            payPrice = payPrice.add(item.getPriceActual());
            details = details.concat(item.getSkuTitle()).concat(",");
            nftOrderStr = nftOrderStr.concat(item.getOrderId()).concat(",");
        }

        if (preParam != null) {
            details = details.concat(preParam.getSkuName()).substring(1);
            orderStr = orderStr.concat(preParam.getOrderIdList());
            payPrice = payPrice.add(preParam.getPriceActual());
            storeName = preParam.getStoreName();
        }

        if (payPrice.compareTo(BigDecimal.valueOf(0)) > 0) {
            // 调用支付
            LinkedMultiValueMap<String, String> httpData = CollectionUtil.linkedMultiValueMapStringString();
            httpData.add("type", "MIX");
            httpData.add("price", payPrice.toString());
            httpData.add("name", storeName);
            httpData.add("detail", details);
            httpData.add("orderCode", masterCode);
            httpData.add("orderId", nftOrderStr.concat(orderStr));
            httpData.add("clientIp", CurrentUtil.getCliIpAddr());
            httpData.add("notifyUrl", orderUrl + "/order/mix/syncOrder");
            httpData.add("createDate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            httpData.add("expireTime", "5");
            httpData.add("payType", payType);
            httpData.add("deviceFrom", deviceForm);

            if (deviceForm.equals("js") || deviceForm.equals("applet")) {
                httpData.add("openId", openId);
            }
            httpData.add("showUrl", showUrl + masterCode);
            httpData.add("returnUrl", returnUrl + masterCode);
            LinkedMultiValueMap<String, String> header = CollectionUtil.linkedMultiValueMapStringString();
            header.add("Accept", "application/json;charset=UTF-8");
            String returnData = HttpUtil.post(payUrl, httpData, header);
            log.info("调用 DRAGON 结果 = " + returnData);
            ResponseDto<GoblinPayInnerResultVo> dto = JsonUtils.fromJson(returnData, new TypeReference<ResponseDto<GoblinPayInnerResultVo>>() {
            });

            NftPayResultVo.setCode(dto.getData().getCode());
            NftPayResultVo.setOrderCode(dto.getData().getOrderCode());
            NftPayResultVo.setPayData(dto.getData().getPayData());
            NftPayResultVo.setPayType(payType);
            NftPayResultVo.setPrice(payPrice);
            payCode = NftPayResultVo.getCode();
            NftPayResultVo.setShowUrl(showUrl);
            NftPayResultVo.setReturnUrl(returnUrl);
        } else if (payType == null) {
            isFree = true;
            payType = "FREE";
            payCode = "FREE_PAY_CODE";
            NftPayResultVo.setPrice(BigDecimal.valueOf(0));
            NftPayResultVo.setPayType(payType);
        } else {
            isFree = true;
            payType = "FREE";
            payCode = "FREE_PAY_CODE";
            NftPayResultVo.setPrice(BigDecimal.valueOf(0));
            NftPayResultVo.setPayType(payType);
        }
        NftPayResultVo.setOrderMasterCode(masterCode);

        // 待支付发送队列
        queueUtils.sendMsgByRedisGoblinStock(masterCode, now, "MIX", 5);

        LinkedList<String> sqls = CollectionUtil.linkedListString();
        sqls.add(SqlMapping.get("goblin_nft_order.insert"));
        sqls.add(SqlMapping.get("goblin.order.create.sku_insert"));
        sqls.add(SqlMapping.get("goblin.order.create.order_insert"));
        sqls.add(SqlMapping.get("goblin.order.create.attr_insert"));
        //构建 nft sql对象
        LinkedList<Object[]> sqlDataOrder = CollectionUtil.linkedListObjectArr();
        for (GoblinNftOrder nftItem : nftOrder) {
            orderMixStr = orderMixStr.concat(nftItem.getOrderId()).concat(",");
            nftItem.setPayCode(payCode);
            sqlDataOrder.add(new Object[]{
                    nftItem.getOrderId(), nftItem.getSpuId(), nftItem.getSkuId(), nftItem.getBoxSkuId(),
                    nftItem.getNum(), nftItem.getStoreId(), nftItem.getStoreName(), nftItem.getOrderCode(), nftItem.getUserId(), nftItem.getUserName(), nftItem.getUserMobile(), nftItem.getPriceTotal(), nftItem.getPriceCoupon(),
                    nftItem.getStorePriceCoupon(), nftItem.getPriceRedEnvelope(), nftItem.getPriceVoucher(), nftItem.getPriceActual(), nftItem.getUcouponId(), nftItem.getStoreCouponId(), nftItem.getRedEnvelopeCode(), nftItem.getStatus(), nftItem.getSource(),
                    nftItem.getOrderType(), nftItem.getPayType(), nftItem.getDeviceFrom(), nftItem.getVersion(), nftItem.getPayCountdownMinute(), nftItem.getIpAddress(), nftItem.getCreatedAt(), nftItem.getPayCode(),
                    nftItem.getSkuTitle(), nftItem.getListId(), nftItem.getExCode(), mixId
            });
            // 订单vo redis
            GoblinNftOrderVo orderVo = GoblinNftOrderVo.getNew().copyMix(nftItem, mixId, mixName);
            orderVo.setCreatedAt(nftItem.getCreatedAt());
            nftOrderUtils.setNftOrder(orderVo);
            nftOrderUtils.setNftOrderIdOfCode(nftItem.getOrderCode(), nftItem.getOrderId());
            //mongo
            queueUtils.setMongoList(GoblinNftOrderVo.class.getSimpleName(), "orderId", orderVo.getOrderId(), GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_INFO, 1);
        }

        //构建 goblin sql 对象
        LinkedList<Object[]> sqlDataSku = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> sqlDataGoblin = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> sqlDataAttr = CollectionUtil.linkedListObjectArr();
        List<String> goblinOrderSkuIdList = CollectionUtil.linkedListString();
        if (preParam != null) {
            for (GoblinOrderSku orderSku : preParam.getOrderSkuList()) {
                sqlDataSku.add(new Object[]{
                        orderSku.getOrderSkuId(), orderSku.getOrderId(), orderSku.getSpuId(), orderSku.getSpuName(), orderSku.getSpuPic(), orderSku.getSkuId(), orderSku.getNum(), orderSku.getSkuPrice(), orderSku.getSkuPriceActual(), orderSku.getSkuName(),
                        orderSku.getSkuNo(), orderSku.getSkuImage(), orderSku.getSkuSpecs(), orderSku.getPriceVoucher(), orderSku.getCreatedAt()
                });
                //订单 orderSku Vo
                GoblinOrderSkuVo orderSkuVo = GoblinOrderSkuVo.getNew().copy(orderSku);
                redisUtils.setGoblinOrderSku(orderSkuVo.getOrderSkuId(), orderSkuVo);
                goblinOrderSkuIdList.add(orderSkuVo.getOrderSkuId());
            }
            GoblinStoreOrder storeOrder = preParam.getStoreOrder();
            storeOrder.setPayCode(payCode);
            sqlDataGoblin.add(new Object[]{
                    storeOrder.getMasterOrderCode(), storeOrder.getOrderId(), storeOrder.getStoreId(), storeOrder.getStoreName(), storeOrder.getOrderCode(), storeOrder.getUserId(), storeOrder.getUserName(), storeOrder.getUserMobile(), storeOrder.getPriceTotal(), storeOrder.getPayCode(),
                    storeOrder.getPriceActual(), storeOrder.getPriceRefund(), storeOrder.getPriceExpress(), storeOrder.getPriceCoupon(), storeOrder.getStorePriceCoupon(), storeOrder.getPriceVoucher(), storeOrder.getStatus(), storeOrder.getUcouponId(), storeOrder.getStoreCouponId(), storeOrder.getPayType(), storeOrder.getDeviceFrom(),
                    storeOrder.getSource(), storeOrder.getVersion(), storeOrder.getIsMember(), storeOrder.getOrderType(), storeOrder.getWriteOffCode(), storeOrder.getPayCountdownMinute(), storeOrder.getIpAddress(), storeOrder.getMarketId(), storeOrder.getMarketType(), storeOrder.getCreatedAt(), ""
            });
            GoblinOrderAttr orderAttr = preParam.getOrderAttr();
            sqlDataAttr.add(new Object[]{
                    orderAttr.getOrderAttrId(), orderAttr.getOrderId(), orderAttr.getExpressContacts(), orderAttr.getExpressAddress(), orderAttr.getExpressAddressDetail(), orderAttr.getExpressPhone(), orderAttr.getExpressType(), orderAttr.getCreatedAt()
            });
            //订单vo
            GoblinStoreOrderVo orderVo = GoblinStoreOrderVo.getNew().copyMix(storeOrder, mixId, mixName);
            //订单attr vo
            GoblinOrderAttrVo orderAttrVo = GoblinOrderAttrVo.getNew().copy(orderAttr);
            //redis 赋值
            orderVo.setOrderAttrVo(orderAttrVo);
            orderVo.setOrderSkuVoIds(goblinOrderSkuIdList);
            orderVo.setCreatedAt(getNowTime());
            redisUtils.setGoblinOrder(orderVo.getOrderId(), orderVo);
            redisUtils.setMasterCode(orderVo.getMasterOrderCode(), orderStr);
            queueUtils.setMongoList(GoblinStoreOrderVo.class.getSimpleName(), "orderId", orderVo.getOrderId(), GoblinRedisConst.REDIS_GOBLIN_ORDER, 1);
        }

        orderMixStr = orderMixStr.concat("&&").concat(orderStr);
        redisUtils.setMixMasterCode(masterCode, orderMixStr);

        // 执行sql
        String sqlData = SqlMapping.gets(sqls, sqlDataOrder, sqlDataSku, sqlDataGoblin, sqlDataAttr);
        queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_NFT_ORDER.getKey(), sqlData);

        if (isFree) {
            SyncOrderParam syncOrderParam = SyncOrderParam.getNew();
            syncOrderParam.setOrderCode(masterCode);
            syncOrderParam.setPaymentAt(DateUtil.format(LocalDateTime.now(), DateUtil.Formatter.yyyyMMddHHmmss));
            syncOrderParam.setCode(payCode);
            syncOrderParam.setPrice(payPrice);
            syncOrderParam.setPaymentId("FREE_PAYMENT_ID");
            syncOrderParam.setPaymentType(null);
            syncOrderParam.setStatus(1);
            syncOrder(syncOrderParam);
            return NftPayResultVo;
        } else {
            return NftPayResultVo;
        }
    }

    //回滚占用的库存
    private void initStock(List<String> canBuyIds, String mixId, Boolean isUseLimit, String uid) {
        for (String canBuyId : canBuyIds) {
            String[] data = canBuyId.split(",");
            String skuIdSingle = data[0];
            int count = Integer.parseInt(data[1]);
            int stock = redisUtils.incrSkuStock(mixId, skuIdSingle, count);
            log.debug("stockId = " + skuIdSingle + " stock = " + stock);
        }

        if (isUseLimit) {
            redisUtils.decrMixLimit(mixId, uid);
        }
    }

    @Override
    public String syncOrder(SyncOrderParam syncOrderParam) {
        log.info("NFT支付回调 参数: " + syncOrderParam.toString());
        String orderCode = syncOrderParam.getOrderCode();
        HashMap<String, String[]> map = redisUtils.getMixMasterCode(orderCode);
        String[] nft = map.get("nft");
        String[] goblin = map.get("goblin");
        String code = syncOrderParam.getCode(), paymentType = syncOrderParam.getPaymentType(), paymentId = syncOrderParam.getPaymentId(), paymentAt = syncOrderParam.getPaymentAt();
        int status = syncOrderParam.getStatus();
        boolean isSuccess = true;

        for (String orderId : nft) {
            if ("".equals(orderId)) {
                break;
            }
            String nftR = syncNft(orderId, code, status, paymentType, paymentId, paymentAt);
            if ("fail".equals(nftR)) {
                isSuccess = false;
            }
        }
        for (String orderId : goblin) {
            if ("".equals(orderId)) {
                break;
            }
            String goblinR = syncGoblin(orderId, code, status, paymentType, paymentId, paymentAt);
            if ("fail".equals(goblinR)) {
                isSuccess = false;
            }
        }
        if (isSuccess) {
            return "success";
        } else {
            return "fail";
        }
    }

    public String syncGoblin(String orderId, String code, int status, String paymentType, String paymentId, String paymentAt) {
        //支付时间
        LocalDateTime now = LocalDateTime.now();
        BigDecimal priceActual = BigDecimal.ZERO;
        GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
        if (orderVo == null) {
            log.error("mix订单Id：" + orderId + " 订单不存在");
            return "fail";//订单不存在
        }
        if (orderVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) {
            if (orderVo.getPayCode().equals(code)) {
                log.error("Mix订单Id：" + orderId + " 重复支付");
                return "success";//重复支付
            }
        }
        priceActual = priceActual.add(orderVo.getPriceActual());
        LinkedList<String> sqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> sqlDataOrder = CollectionUtil.linkedListObjectArr();
        LinkedList<Object[]> sqlDataSku = CollectionUtil.linkedListObjectArr();
        GoblinStoreOrder storeOrder = GoblinStoreOrder.getNew();
        if (orderVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_5.getValue() && status == 1) {
            log.error("Mix订单Id {} 的订单超时支付", orderId);
            // 商铺退款逻辑
            orderUtils.refundOrderSku(orderId, paymentId, paymentType);
        } else if ((orderVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_6.getValue() || orderVo.getStatus() == GoblinStatusConst.Status.ORDER_STATUS_7.getValue()) && status == 1) {
            log.error("Mix订单Id为 {} 的订单正在退款 或者已退款", orderId);
        } else if (status == 1) {
            log.error("Mix订单Id {} 的订单正常流程", orderId);
            storeOrder.setPaymentType(paymentType);
            storeOrder.setPaymentId(paymentId);
            storeOrder.setPayCode(code);
            LocalDateTime payTime = LocalDateTime.parse(paymentAt, DTF_YMD_HMS);
            storeOrder.setPayTime(payTime);
            storeOrder.setWriteOffCode("");
            storeOrder.setStatus(GoblinStatusConst.Status.ORDER_STATUS_2.getValue());
            storeOrder.setUpdatedAt(now);
            sqls.add(SqlMapping.get("goblin_order.pay.order"));
            sqlDataOrder.add(new Object[]{
                    storeOrder.getPaymentType(), storeOrder.getPaymentId(), storeOrder.getPayCode(), storeOrder.getPayTime(), storeOrder.getWriteOffCode(), storeOrder.getStatus(), storeOrder.getUpdatedAt(),
                    orderId, now, now
            });
            sqls.add(SqlMapping.get("goblin_order.pay.sku"));
            List<String> skuList = orderVo.getOrderSkuVoIds();
            for (String orderSkuVoIds : skuList) {
                GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(orderSkuVoIds);
                //增加销量
                log.debug("增加销量 spuId=" + orderSkuVo.getSpuId() + ",skuId=" + orderSkuVo.getSkuId());
                redisUtils.incrSkuSaleCount(orderSkuVo.getSpuId(), orderSkuVo.getSkuId(), orderSkuVo.getNum());
                orderSkuVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_2.getValue());
                //redis
                redisUtils.setGoblinOrderSku(orderSkuVo.getOrderSkuId(), orderSkuVo);
                //mongo
                queueUtils.setMongoList(GoblinOrderSkuVo.class.getSimpleName(), "orderSkuId", orderSkuVo.getOrderSkuId(), GoblinRedisConst.REDIS_GOBLIN_ORDER_SKU, 2);
                //mongo 添加操作日志
                GoblinOrderLogVo logVo = GoblinOrderLogVo.getNew();
                logVo.setOrderId(orderVo.getOrderId());
                logVo.setOrderCode(orderVo.getOrderCode());
                logVo.setPayCode(orderVo.getPayCode());
                logVo.setStoreId(orderVo.getStoreId());
                logVo.setOrderType("order");
                logVo.setSpuId(orderSkuVo.getSpuId());
                logVo.setSkuId(orderSkuVo.getSkuId());
                logVo.setSkuPriceActual(orderSkuVo.getSkuPriceActual().multiply(new BigDecimal(100)).longValue());
                logVo.setStatus(GoblinStatusConst.Status.ORDER_LOG_STATUS_11.getValue());
                logVo.setRemark(GoblinStatusConst.Status.ORDER_LOG_STATUS_11.getDesc());
                logVo.setOperationId(orderVo.getUserId());
                logVo.setOperationName(orderVo.getUserName());
                logVo.setOperationType(GoblinStatusConst.Type.OPERATION_TYPE_1.getValue());
                logVo.setCreatedAt(LocalDateTime.now());
//                redisUtils.setLogVo(orderVo.getOrderId()+orderVo.getOrderCode(),logVo);
//                queueUtils.setMongoList(GoblinOrderLogVo.class.getSimpleName(), "orderId", orderSkuVo.getSpuId()+orderVo.getOrderCode(), GoblinRedisConst.GOBLIN_ORDER_LOG, 1);
                mongoUtils.insertGoblinOrderLogVo(logVo);

                //mysql
                sqlDataSku.add(new Object[]{
                        GoblinStatusConst.Status.ORDER_STATUS_2.getValue(), now,
                        orderSkuVo.getOrderSkuId(), now, now
                });
            }
            //redis
            orderVo.setPayCode(storeOrder.getPayCode());
            orderVo.setPayTime(paymentAt);
            orderVo.setWriteOffCode(storeOrder.getWriteOffCode());
            orderVo.setStatus(storeOrder.getStatus());
            orderVo.setPaymentId(paymentId);
            orderVo.setPaymentType(paymentType);
            orderVo.setOrderSkuVoIds(skuList);
            redisUtils.setGoblinOrder(orderId, orderVo);
            queueUtils.setMongoList(GoblinStoreOrderVo.class.getSimpleName(), "orderId", orderVo.getOrderId(), GoblinRedisConst.REDIS_GOBLIN_ORDER, 2);
            //mysql
            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
                    SqlMapping.gets(sqls, sqlDataOrder, sqlDataSku));
        }
        //添加列表
        redisUtils.addOrderList(orderVo.getUserId(), orderVo.getOrderId());
        //加分
        orderUtils.integral(orderVo.getUserId(), priceActual, "购买商品", 1);
        return "success";
    }

    public String syncNft(String orderId, String code, int status, String paymentType, String paymentId, String paymentAt) {
        if (orderId.isEmpty()) {
            log.error("MIX Nft订单不存在1 param:[orderId:{}]", orderId);
            return "fail";
        }
        GoblinNftOrderVo orderVo = nftOrderUtils.getNftOrder(orderId);
        if (null == orderVo) {
            log.error("MIX Nft订单不存在2 param:[orderId:{}]", orderId);
            return "fail";
        }
        // 苹果支付下单时候没有PayCode
        if (!orderVo.getPayType().equals("applepay") && !orderVo.getPayCode().equals(code)) {
            log.error("MIX Nft订单payCode不匹配 param:[orderId:{}, payCode:{}, code:{}]", orderId, orderVo.getPayCode(), code);
            return "fail";
        }
        if (!Objects.equals(orderVo.getStatus(), GoblinStatusConst.NftStatus.ORDER_STATUS_1.getValue()) && !Objects.equals(orderVo.getStatus(), GoblinStatusConst.NftStatus.ORDER_STATUS_3.getValue())) {
            log.error("MIX Nft订单当前状态不能支付回调 param:[orderId:{}, status:{}]", orderId, orderVo.getStatus());
            return "success";
        }
        if (Objects.equals(1, status)) {
            String uid = orderVo.getUserId();
            Integer orderStatusOld = orderVo.getStatus();
            LocalDateTime now = LocalDateTime.now();
            // 更新订单信息
            if (StringUtil.isEmpty(paymentType)) {
                orderVo.setPaymentType("");
            } else {
                orderVo.setPaymentType(paymentType);
            }
            if (StringUtil.isEmpty(paymentId)) {
                orderVo.setPaymentId("");
            } else {
                orderVo.setPaymentId(paymentId);
            }
            if (StringUtil.isEmpty(code)) {
                orderVo.setPayCode("");
            } else {
                orderVo.setPayCode(code);
            }
            if (StringUtil.isEmpty(paymentAt)) {
                orderVo.setPayTime(now);
            } else {
                LocalDateTime payTime = LocalDateTime.parse(paymentAt, DTF_YMD_HMS);
                orderVo.setPayTime(payTime);
            }
            orderVo.setUpdatedAt(now);
            if (Objects.equals(orderStatusOld, GoblinStatusConst.NftStatus.ORDER_STATUS_3.getValue())) {// 超时支付 退款中状态
                orderVo.setStatus(GoblinStatusConst.NftStatus.ORDER_STATUS_4.getValue());
            } else {// 正常流程 已付款状态
                orderVo.setStatus(GoblinStatusConst.NftStatus.ORDER_STATUS_2.getValue());
            }
            // redis
            nftOrderUtils.setNftOrder(orderVo);
            // mongo
            queueUtils.setMongoList(GoblinNftOrderVo.class.getSimpleName(), "orderId", orderVo.getOrderId(), GoblinRedisConst.REDIS_GOBLIN_NFT_ORDER_INFO, 2);
            // mysql
            LinkedList<String> sqls = CollectionUtil.linkedListString();
            LinkedList<Object[]> sqlDataOrder = CollectionUtil.linkedListObjectArr();
            sqls.add(SqlMapping.get("goblin_nft_order.update.pay"));
            sqlDataOrder.add(new Object[]{
                    orderVo.getPaymentType(), orderVo.getPaymentId(), orderVo.getPayCode(),
                    orderVo.getPayTime(), orderVo.getStatus(), orderVo.getUpdatedAt(),
                    orderId, now, now
            });
            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.GOBLIN_NFT_ORDER.getKey(),
                    SqlMapping.gets(sqls, sqlDataOrder));
            // 加积分
            orderUtils.integral(uid, orderVo.getPriceActual(), "购买数字藏品", 1);
            // 处理订单退款和其他流程
            if (Objects.equals(orderStatusOld, GoblinStatusConst.NftStatus.ORDER_STATUS_3.getValue())) {// 超时支付 退款
                log.info("Nft订单超时支付 param:[orderId:{}]", orderId);
                nftOrderUtils.refundOrderSku(orderId, paymentId, paymentType, orderVo.getPayType());
            } else {// 正常流程
                nftOrderUtils.generateUserArtwork(orderVo, 1);
                // 增加销量
                redisUtils.incrSkuSaleCount(orderVo.getSpuId(), orderVo.getSkuId(), orderVo.getNum());
            }
            // 写入用户订单列表 因取消的订单不展示 所以放在这里
            nftOrderUtils.addNftOrderList(uid, orderVo.getOrderId());
        }
        return "success";
    }


    @Override
    public ResponseDto<Integer> checkOrderResultMaterCode(String materCode) {
        String uid = CurrentUtil.getCurrentUid();

        HashMap<String, String[]> map = redisUtils.getMixMasterCode(materCode);
        String[] nft = map.get("nft");
        String[] goblin = map.get("goblin");

        for (String orderId : nft) {
            if ("".equals(orderId)) {
                break;
            }
            String nftR = nftCheck(uid, orderId);
            if ("订单不存在".equals(nftR)) {
                return ResponseDto.failure("订单不存在");
            } else if ("0".equals(nftR)) {
                return ResponseDto.success(0);
            }
        }
        for (String orderId : goblin) {
            if ("".equals(orderId)) {
                break;
            }
            String goblinR = goblinCheck(uid, orderId);
            if ("订单不存在".equals(goblinR)) {
                return ResponseDto.failure("订单不存在");
            } else if ("0".equals(goblinR)) {
                return ResponseDto.success(0);
            }
        }
        return ResponseDto.success(1);
    }


    private String nftCheck(String uid, String orderId) {
        GoblinNftOrderVo nftOrder = nftOrderUtils.getNftOrder(orderId);
        if (null == nftOrder || !nftOrder.getUserId().equals(uid)) {
            return "订单不存在";
        } else {
            String returnCheckData = HttpUtil.get(checkUrl + "?code=" + nftOrder.getPayCode(), null);
            ResponseDto<SyncOrderParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<SyncOrderParam>>() {
            });
            if (syncOrderDtoParam.getData().getStatus() == 1) {
                //处理订单
                syncOrder(syncOrderDtoParam.getData());
                return "1";
            } else {
                return "0";
            }
        }
    }

    private String goblinCheck(String uid, String orderId) {
        GoblinStoreOrderVo storeOrderVo = redisUtils.getGoblinOrder(orderId);
        if (null == storeOrderVo || !storeOrderVo.getUserId().equals(uid)) {
            return "订单不存在";
        }
        String returnCheckData = HttpUtil.get(checkUrl + "?code=" + storeOrderVo.getPayCode(), null);
        ResponseDto<SyncOrderParam> syncOrderDtoParam = JsonUtils.fromJson(returnCheckData, new TypeReference<ResponseDto<SyncOrderParam>>() {
        });
        if (syncOrderDtoParam.getData().getStatus() == 1) {
            //处理订单
            syncOrder(syncOrderDtoParam.getData());
            return "1";
        } else {
            return "0";
        }
    }
}
