package com.liquidnet.service.goblin.service.impl;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.PagedResult;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.goblin.dto.GoblinQueueBizArtworkGenDto;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.service.IGoblinUserDigitalArtworkService;
import com.liquidnet.service.goblin.service.impl.inner.GoblinGalaxyService;
import com.liquidnet.service.goblin.util.GoblinMongoUtils;
import com.liquidnet.service.goblin.util.GoblinRedisUtils;
import com.liquidnet.service.goblin.util.ObjectUtil;
import com.liquidnet.service.goblin.util.QueueUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;

@Slf4j
@Service
public class GoblinUserDigitalArtworkServiceImpl implements IGoblinUserDigitalArtworkService {
    @Autowired
    private QueueUtils queueUtils;
    @Autowired
    private GoblinRedisUtils goblinRedisUtils;
    @Autowired
    private GoblinMongoUtils goblinMongoUtils;
    @Autowired
    private GoblinGalaxyService goblinGalaxyService;

    @Override
    public GoblinUserDigitalArtworkPageVo page(String uid, Integer pageNum, Integer pageSize) {
        GoblinUserDigitalArtworkPageVo pageVo = GoblinUserDigitalArtworkPageVo.getNew();
        long artworkNum = goblinRedisUtils.countUserDigitalArtwork(uid);
        if (artworkNum > 0) {
            PagedResult<GoblinUserDigitalArtworkListVo> listVoPagedResult = ObjectUtil.getGoblinUserDigitalArtworkListVoPagedResult();
            List<String> subArtworkIdList;
            if (artworkNum > 30 && pageNum * pageSize > 30) {// 查询Mongo
//                if ((long) pageNum * pageSize > artworkNum) pageNum = Math.toIntExact(artworkNum / pageSize);
                subArtworkIdList = goblinMongoUtils.getPageUserDigitalArtworkIds(uid, pageNum, pageSize);
                if (CollectionUtils.isEmpty(subArtworkIdList)) {
                    log.warn("#我的藏品列表:藏品IDS数据为空[uid={},artworkNum={}]", uid, artworkNum);
                    return pageVo.setArtworkNum((int) artworkNum).setPagedResult(listVoPagedResult).setSystime(LocalDateTime.now());
                }
            } else {
                List<String> userDigitalArtworkIds = goblinRedisUtils.getUserDigitalArtworkIds(uid);
//                if (CollectionUtils.isEmpty(userDigitalArtworkIds)) {
                if (CollectionUtils.isEmpty(userDigitalArtworkIds) || ((pageNum - 1) * pageSize > (userDigitalArtworkIds.size() - 1))) {
                    log.warn("#我的藏品列表:藏品IDS缓存为空[uid={},artworkNum={}]", uid, artworkNum);
                    return pageVo.setArtworkNum((int) artworkNum).setPagedResult(listVoPagedResult).setSystime(LocalDateTime.now());
                }

                int cacheSize = userDigitalArtworkIds.size(), cacheNum = cacheSize / pageSize;// 分几页
//                int fromIndex = Math.min(Math.min(cacheNum, pageNum - 1) * pageSize, cacheSize - 1);
                int fromIndex = (pageNum - 1) * pageSize;
                subArtworkIdList = userDigitalArtworkIds.subList(fromIndex, Math.min(fromIndex + pageSize, cacheSize));
            }

            List<GoblinUserDigitalArtworkListVo> list = ObjectUtil.getGoblinUserDigitalArtworkListVoArrayList();
            LocalDateTime now = LocalDateTime.now(), toCheckTime = now.minusSeconds(15);
            for (int i = 0, subSize = subArtworkIdList.size(); i < subSize; i++) {
                GoblinUserDigitalArtworkVo userDigitalArtworkVo = goblinRedisUtils.getUserDigitalArtworkVo(subArtworkIdList.get(i));
                GoblinGoodsSkuInfoVo goodsSkuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(userDigitalArtworkVo.getSkuId());

                GoblinUserDigitalArtworkListVo listVo = GoblinUserDigitalArtworkListVo.getNew().copy(userDigitalArtworkVo);
                listVo.setName(goodsSkuInfoVo.getName());
                listVo.setSubtitle(goodsSkuInfoVo.getSubtitle());
                listVo.setCoverPic(goodsSkuInfoVo.getSkuPic());
                listVo.setEdition(goodsSkuInfoVo.getSkuStock());
                listVo.setUnbox(goodsSkuInfoVo.getUnbox());
                listVo.setOpeningTime(goodsSkuInfoVo.getOpeningTime());

                if ("0".equals(listVo.getUnbox()) && 1 != listVo.getState() && listVo.getCreatedAt().isBefore(toCheckTime)) {// 15秒前创建的藏品，去主动核实发行交易状态
                    List<String> checkRespDataList = goblinGalaxyService.checkNftTradeResult(uid, userDigitalArtworkVo.getOrderId(), goodsSkuInfoVo.getRouteType());
                    this.updateUserDigitalArtworkByChainTrade(userDigitalArtworkVo, listVo, checkRespDataList, now);
                } else if (1 == listVo.getState() && "1".equals(listVo.getUnbox())) {// 盲盒已开启的，直接展示开出的藏品封面
                    GoblinNftOrderVo goblinNftOrder = goblinRedisUtils.getGoblinNftOrder(userDigitalArtworkVo.getOrderId());
                    goodsSkuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(goblinNftOrder.getBoxSkuId());
                    listVo.setCoverPic(goodsSkuInfoVo.getSkuPic());
                }

                list.add(listVo);
            }

            listVoPagedResult.setList(list).setTotal(artworkNum, pageSize);
            pageVo.setArtworkNum((int) artworkNum).setPagedResult(listVoPagedResult).setSystime(LocalDateTime.now());
        }
        return pageVo;
    }

    @Override
    public GoblinUserDigitalArtworkInfoVo info(String uid, String artworkId) {
        GoblinUserDigitalArtworkVo userDigitalArtworkVo = goblinRedisUtils.getUserDigitalArtworkVo(artworkId);
        if (null != userDigitalArtworkVo && userDigitalArtworkVo.getUid().equals(uid)) {
            GoblinUserDigitalArtworkInfoVo artworkInfoVo = GoblinUserDigitalArtworkInfoVo.getNew().copy(userDigitalArtworkVo);

            GoblinGoodsSkuInfoVo goodsSkuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(userDigitalArtworkVo.getSkuId());
            GoblinGoodsInfoVo goodsInfoVo = goblinRedisUtils.getGoodsInfoVo(goodsSkuInfoVo.getSpuId());

            artworkInfoVo.setName(goodsSkuInfoVo.getName());
            artworkInfoVo.setSubtitle(goodsSkuInfoVo.getSubtitle());
            artworkInfoVo.setMaterialType(goodsSkuInfoVo.getMaterialType());
            artworkInfoVo.setMaterialUrl(goodsSkuInfoVo.getMaterialUrl());
            artworkInfoVo.setEdition(goodsSkuInfoVo.getSkuStock());
            artworkInfoVo.setAuthor(goodsInfoVo.getAuthor());
            artworkInfoVo.setPublisher(goodsInfoVo.getPublisher());
            artworkInfoVo.setDetails(goodsSkuInfoVo.getDetails());
//            artworkInfoVo.setGenerateTime(goodsSkuInfoVo.getDeclareAt());

            return artworkInfoVo;
        }
        return null;
    }

//    @Override
//    public GoblinUserDigitalArtworkListVo unboxingForBuyOrExchange(String uid, GoblinUserDigitalArtworkVo userDigitalArtworkVo) {
//        String orderId = userDigitalArtworkVo.getOrderId();
//        GoblinNftOrderVo goblinNftOrder = goblinRedisUtils.getGoblinNftOrder(orderId);
//        GoblinGoodsSkuInfoVo unboxSkuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(goblinNftOrder.getBoxSkuId());
//
//        GoblinUserDigitalArtworkListVo unboxDigitalArtworkListVo = GoblinUserDigitalArtworkListVo.getNew();
////        unboxDigitalArtworkListVo.setArtworkId();
//        unboxDigitalArtworkListVo.setName(unboxSkuInfoVo.getName());
//        unboxDigitalArtworkListVo.setSubtitle(unboxSkuInfoVo.getSubtitle());
//        unboxDigitalArtworkListVo.setCoverPic(unboxSkuInfoVo.getSkuPic());
////        unboxDigitalArtworkListVo.setSource(userDigitalArtworkVo.getSource());
////        unboxDigitalArtworkListVo.setState(0);
////        unboxDigitalArtworkListVo.setCreatedAt();
////        unboxDigitalArtworkListVo.setUnbox("0");
////        unboxDigitalArtworkListVo.setOpeningTime(now);
//
//        LocalDateTime now = LocalDateTime.now();
//        userDigitalArtworkVo.setState(1);
//        userDigitalArtworkVo.setDelFlg("1");// 盲盒开启后直接删除
//        userDigitalArtworkVo.setOpeningAt(now);
//        userDigitalArtworkVo.setUpdatedAt(now);
//        userDigitalArtworkVo.setDeletedAt(now);
//
////        if (goblinMongoUtils.updateUserDigitalArtworkVoByUnboxing(userDigitalArtworkVo)) {
//            goblinRedisUtils.setexUserDigitalArtworkVo(userDigitalArtworkVo);
//
//            String artworkId = userDigitalArtworkVo.getArtworkId();
//            GoblinQueueBizArtworkGenDto queueBizArtworkGenDto = GoblinQueueBizArtworkGenDto.getNew();
//            queueBizArtworkGenDto.setUid(uid);
//            queueBizArtworkGenDto.setSkuId(unboxSkuInfoVo.getSkuId());
//            queueBizArtworkGenDto.setOrderId(orderId);
//            queueBizArtworkGenDto.setSource(userDigitalArtworkVo.getSource());
//            queueBizArtworkGenDto.setFromArtId(artworkId);
//            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.BIZ_ARTWORK_GEN.getKey(), queueBizArtworkGenDto.toJson());
//            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_ARTWORK_GEN.getKey(),
//                    SqlMapping.get("goblin_user_digital_artwork.update_for_unboxing", now, now, now, artworkId));
////        }
//        return unboxDigitalArtworkListVo;
//    }

    @Override
    public GoblinUserDigitalArtworkListVo unboxingForBuyOrExchange(String uid, GoblinUserDigitalArtworkVo userDigitalArtworkBoxVo) {
        LocalDateTime now = LocalDateTime.now();
        userDigitalArtworkBoxVo.setState(1);
        userDigitalArtworkBoxVo.setDelFlg("1");// 盲盒开启后直接删除
        userDigitalArtworkBoxVo.setOpeningAt(now);
        userDigitalArtworkBoxVo.setUpdatedAt(now);
        userDigitalArtworkBoxVo.setDeletedAt(now);

        if (goblinMongoUtils.updateUserDigitalArtworkVoByUnboxing(userDigitalArtworkBoxVo)) {
            String orderId = userDigitalArtworkBoxVo.getOrderId();
            GoblinNftOrderVo orderVo = goblinRedisUtils.getGoblinNftOrder(orderId);
            GoblinGoodsSkuInfoVo unboxSkuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(orderVo.getBoxSkuId());

            GoblinUserDigitalArtworkListVo unboxDigitalArtworkListVo = GoblinUserDigitalArtworkListVo.getNew();
//            unboxDigitalArtworkListVo.setArtworkId();
            unboxDigitalArtworkListVo.setName(unboxSkuInfoVo.getName());
            unboxDigitalArtworkListVo.setSubtitle(unboxSkuInfoVo.getSubtitle());
            unboxDigitalArtworkListVo.setCoverPic(unboxSkuInfoVo.getSkuPic());
//            unboxDigitalArtworkListVo.setSource(userDigitalArtworkBoxVo.getSource());
//            unboxDigitalArtworkListVo.setState(0);
//            unboxDigitalArtworkListVo.setCreatedAt();
//            unboxDigitalArtworkListVo.setUnbox("0");
//            unboxDigitalArtworkListVo.setOpeningTime(now);

            String skuId = orderVo.getBoxSkuId(), spuId = orderVo.getSpuId();
            Integer source = 1;
//            if (goblinRedisUtils.hasGenUserDigitalArtwork(uid, skuId, orderId)) {// 已生成
//                log.warn("#UNBOX_ARTWORK EXIST_ART[uid={},skuId={},orderId={},source={}]", uid, skuId, orderId, source);
//                return unboxDigitalArtworkListVo;
//            }
            goblinRedisUtils.setexUserDigitalArtworkVo(userDigitalArtworkBoxVo);

            GoblinGoodsInfoVo goodsInfoVo = goblinRedisUtils.getGoodsInfoVo(spuId);

            GoblinUserDigitalArtworkVo initUserDigitalArtworkVo = GoblinUserDigitalArtworkVo.getNew();
            String artworkId = IDGenerator.nextMilliId2();
            initUserDigitalArtworkVo.setArtworkId(artworkId);
            initUserDigitalArtworkVo.setSkuId(skuId);
            initUserDigitalArtworkVo.setUid(uid);
            initUserDigitalArtworkVo.setOrderId(orderId);
            initUserDigitalArtworkVo.setSource(source);
            initUserDigitalArtworkVo.setState(0);
            initUserDigitalArtworkVo.setDelFlg("0");
            initUserDigitalArtworkVo.setCreatedAt(now);
            initUserDigitalArtworkVo.setAuthor(goodsInfoVo.getAuthor());
            initUserDigitalArtworkVo.setPublisher(goodsInfoVo.getPublisher());

            // Mongo记录VO
            goblinMongoUtils.insertUserDigitalArtworkVo(initUserDigitalArtworkVo);
            // Redis记录VO
            goblinRedisUtils.setUserDigitalArtworkVo(initUserDigitalArtworkVo);
            // Redis更新藏品ID列表
            goblinRedisUtils.addUserDigitalArtworkIdsForUnbox(uid, artworkId, userDigitalArtworkBoxVo.getArtworkId());
//            // Redis生成标记（缓存三天），用于上面的生成检查
//            goblinRedisUtils.markGenUserDigitalArtwork(uid, skuId, orderId);

            if ("0".equals(unboxSkuInfoVo.getUnbox())) {// 非盲盒，NFT发行购买
                ObjectNode bizNftBuyNode = JsonUtils.OM().createObjectNode().put("nftOrderPayId", orderId).put("routerType", unboxSkuInfoVo.getRouteType())
                        .put("skuId", skuId).put("userId", uid).put("buyTimestamp", DateUtil.Formatter.yyyyMMddHHmmss.format(now));;
                queueUtils.sendMsgByRedis(MQConst.GalaxyQueue.JSON_NFT_PUBLISH_AND_BUY.getKey(), bizNftBuyNode.toString());
            }

            // Mysql持久化
            LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
            toMqSqls.add(SqlMapping.get("goblin_user_digital_artwork.insert"));
            LinkedList<Object[]> initUserDigitalArtworkObjs = CollectionUtil.linkedListObjectArr();
            initUserDigitalArtworkObjs.add(new Object[]{initUserDigitalArtworkVo.getArtworkId(), skuId, uid, orderId, source, initUserDigitalArtworkVo.getState(), now});
            toMqSqls.add(SqlMapping.get("goblin_user_digital_artwork.update_for_unboxing"));
            LinkedList<Object[]> updateBoxArtworkObjs = CollectionUtil.linkedListObjectArr();
            updateBoxArtworkObjs.add(new Object[]{now, now, now, userDigitalArtworkBoxVo.getArtworkId()});
            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_ARTWORK_GEN.getKey(), SqlMapping.gets(toMqSqls, initUserDigitalArtworkObjs, updateBoxArtworkObjs));

            return unboxDigitalArtworkListVo;
        }
        return null;
    }

    /* -------------------------------------------------------------------------------------------------------------- */
    /* -------------------------------------------------------------------------------------------------------------- */
    /* -------------------------------------------------------------------------------------------------------------- */

    private void updateUserDigitalArtworkByChainTrade(GoblinUserDigitalArtworkVo userDigitalArtworkVo, GoblinUserDigitalArtworkListVo listVo,
                                                      List<String> checkRespDataList, LocalDateTime now) {
        if (!CollectionUtils.isEmpty(checkRespDataList)) {
            String nftId = checkRespDataList.get(0);
            String nftPublishChainTimestamp = checkRespDataList.get(1);
            String nftPublishTxHash = checkRespDataList.get(2);
            String nftBuyChainTimestamp = checkRespDataList.get(3);
            String nftBuyTxHash = checkRespDataList.get(4);

            listVo.setState(1);
            listVo.setEditionSn(Integer.valueOf(nftId.substring(nftId.indexOf("_") + 1)));

            userDigitalArtworkVo.setEditionSn(listVo.getEditionSn());
            userDigitalArtworkVo.setNftId(nftId);
            userDigitalArtworkVo.setReleaseAt(nftPublishChainTimestamp);
            userDigitalArtworkVo.setReleaseTxhash(nftPublishTxHash);
            userDigitalArtworkVo.setTradingAt(nftBuyChainTimestamp);
            userDigitalArtworkVo.setTradingTxhash(nftBuyTxHash);
            userDigitalArtworkVo.setState(1);
            userDigitalArtworkVo.setUpdatedAt(now);

            goblinRedisUtils.setUserDigitalArtworkVo(userDigitalArtworkVo);
            goblinMongoUtils.updateUserDigitalArtworkVoByChainTrade(userDigitalArtworkVo);

            queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_ARTWORK_GEN.getKey(),
                    SqlMapping.get("goblin_user_digital_artwork.update_for_chain",
                            userDigitalArtworkVo.getEditionSn(), userDigitalArtworkVo.getNftId(), userDigitalArtworkVo.getReleaseTxhash(),
                            userDigitalArtworkVo.getReleaseAt(), userDigitalArtworkVo.getTradingTxhash(), userDigitalArtworkVo.getTradingAt(),
                            userDigitalArtworkVo.getState(), userDigitalArtworkVo.getUpdatedAt(), userDigitalArtworkVo.getArtworkId())
            );
        }
    }
}
