package com.liquidnet.service.consumer.kylin.receiver;

import com.fasterxml.jackson.databind.JsonNode;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.HttpUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
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.dto.vo.GoblinGoodsSkuInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
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.data.mongodb.core.query.Update;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;

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

@Slf4j
@Component
public class ConsumerGoblinBizArtworkUpcRdsReceiver extends AbstractBizRedisReceiver {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private MongoTemplate mongoTemplate;


    @Autowired
    Environment env;
//    @Value("${liquidnet.service.galaxy.url}")// TODO: 2022/4/1 ==zhanggb
//    private String sevGalaxyUrl;

    private static final String SQL_UPDATE_GOODS_SKU_NFT = "UPDATE goblin_goods_sku_nft SET upchain=?,series_id=?,series_hash=?,nft_hash=?,declare_at=?,updated_at=? WHERE skuId=? AND upchain=9 ";

    @Override
    protected String getRedisStreamKey() {
        return MQConst.GoblinQueue.BIZ_ARTWORK_UPC.getKey();
    }

    @Override
    protected String getRedisStreamGroup() {
        return MQConst.GoblinQueue.BIZ_ARTWORK_UPC.getGroup();
    }

    @Override
    protected boolean consumerMessageHandler(String msg) {
        boolean aBoolean = false;
        try {
            if (StringUtils.isEmpty(msg)) {
                log.warn("CONSUMER MSG NULL_DTO ==> [{}]:{}", this.getRedisStreamKey(), msg);
                aBoolean = true;
            } else {
                String[] msgArr = msg.split(",");
                String skuId = msgArr[0], time = msgArr[1];

                LocalDateTime now = LocalDateTime.now(), checkTime = now.minusSeconds(10);
                LocalDateTime createAt = StringUtils.isEmpty(time) ? checkTime : LocalDateTime.parse(time);
                long durationToMillis = Duration.between(createAt, checkTime).toMillis();
                if (durationToMillis >= 0) {
                    aBoolean = this.bizArtworkUpcProcessing(skuId);
                } else {
                    try {
                        Thread.sleep(Math.abs(durationToMillis));
                    } catch (InterruptedException ignored) {
                    }
                    aBoolean = this.bizArtworkUpcProcessing(skuId);
                }
            }
        } catch (Exception e) {
            log.error("CONSUMER MSG EX_HANDLE ==> [{}]:{}", this.getRedisStreamKey(), msg, e);
        } finally {
            if (!aBoolean) {
                HashMap<String, String> map = CollectionUtil.mapStringString();
                map.put("message", msg);
                stringRedisTemplate.opsForStream().add(StreamRecords.mapBacked(map).withStreamKey(this.getRedisStreamKey()));
            }
        }
        return aBoolean;
    }

    private boolean bizArtworkUpcProcessing(String skuId) {
        GoblinGoodsSkuInfoVo goodsSkuInfoVo = this.getGoodsSkuInfoVoFromMdb(skuId);
        if (null == goodsSkuInfoVo) {
            log.warn("#CONSUMER MSG NULL_SKU[{}]:[skuId={}]", this.getRedisStreamKey(), skuId);
            return true;
        }
        int skuType = goodsSkuInfoVo.getSkuType(), upchain = goodsSkuInfoVo.getUpchain();
        String unbox = goodsSkuInfoVo.getUnbox();
        if (1 != skuType || !"0".equals(unbox) || 9 != upchain) {// 非数字藏品 || 盲盒 || 非上传中
            log.warn("#CONSUMER MSG VOID_SKU[{}]:[skuId={},skuType={},unbox={},upchain={}]",
                    this.getRedisStreamKey(), skuId, skuType, unbox, upchain);
            return true;
        }

        List<String> checkNftClaimResult = this.checkNftClaimFromGalaxy(skuId, goodsSkuInfoVo.getRouteType());
        if (CollectionUtils.isEmpty(checkNftClaimResult)) return false;

        String seriesId = checkNftClaimResult.get(0);
        String txHash = checkNftClaimResult.get(1);
        String nftHash = checkNftClaimResult.get(2);
        String chainTimestamp = checkNftClaimResult.get(3);

        LocalDateTime now = LocalDateTime.now();
        mongoTemplate.getCollection(GoblinGoodsSkuInfoVo.class.getSimpleName()).updateOne(
                Query.query(Criteria.where("skuId").is(skuId).and("delFlg").is("0")).getQueryObject(),
                Update.update("upchain", 1).set("seriesId", seriesId).set("seriesHash", txHash).set("nftHash", nftHash).set("declareAt", chainTimestamp).getUpdateObject()
        );
        redisUtil.del(GoblinRedisConst.BASIC_GOODS_SKU.concat(skuId));

        // Mysql持久化
        HashMap<String, String> sqlUpdateMap = CollectionUtil.mapStringString();
        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        toMqSqls.add(SQL_UPDATE_GOODS_SKU_NFT);
        LinkedList<Object[]> updateGoodsSkuNftObjs = CollectionUtil.linkedListObjectArr();
        updateGoodsSkuNftObjs.add(new Object[]{1, seriesId, txHash, nftHash, chainTimestamp, now, skuId});

        sqlUpdateMap.put("message", SqlMapping.gets(toMqSqls, updateGoodsSkuNftObjs));
        stringRedisTemplate.opsForStream().add(StreamRecords.mapBacked(sqlUpdateMap).withStreamKey(MQConst.GoblinQueue.SQL_GOODS.getKey()));
        return true;
    }

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

    private List<String> checkNftClaimFromGalaxy(String skuId, String routerType) {
        LinkedMultiValueMap<String, String> paramsMap = CollectionUtil.linkedMultiValueMapStringString();
        paramsMap.add("skuId", skuId);
        paramsMap.add("routerType", routerType);

//        String postUrl = sevGalaxyUrl + "/user/register", blockChainAddress;// TODO: 2022/4/1 ==zhanggb
        String postUrl = "https://ENVgalaxy.zhengzai.tv/galaxy/artwork/seriesClaimResultQuery".replace("ENV", env.getProperty(CurrentUtil.CK_ENV_ACTIVE)), blockChainAddress;
        try {
            LinkedMultiValueMap<String, String> headerMap = CollectionUtil.linkedMultiValueMapStringString();
            headerMap.add("Accept", MediaType.APPLICATION_JSON_VALUE);
            String postRespStr = HttpUtil.post(postUrl, paramsMap, headerMap);
            JsonNode postRespJNode = JsonUtils.fromJson(postRespStr, JsonNode.class), postRespDataJNode;
            if (null == postRespJNode || null == postRespJNode.get("code") || !postRespJNode.get("code").asText().equals("0")) {
                log.warn("#CONSUMER MSG FAIL_UPC[{}]查询失败[paramsMap={},postRespStr={}]", this.getRedisStreamKey(), paramsMap, postRespStr);
                return null;
            }
            if (1 != (postRespDataJNode = postRespJNode.get("data")).get("taskStatus").asInt()) {
                log.warn("#CONSUMER MSG FAIL_UPC[{}]声明失败[paramsMap={},postRespStr={}]", this.getRedisStreamKey(), paramsMap, postRespStr);
                return null;
            }

            List<String> respDataList = CollectionUtil.arrayListString();
            respDataList.add(postRespDataJNode.get("seriesId").asText());
            respDataList.add(postRespDataJNode.get("txHash").asText());
            respDataList.add(postRespDataJNode.get("nftHash").asText());
            respDataList.add(postRespDataJNode.get("chainTimestamp").asText());
            return respDataList;
        } catch (Exception e) {
            log.error("Ex.CONSUMER MSG ERROR_UPC[{}]请求异常[skuId={},url={},paramsMap={}],ex:{}",
                    this.getRedisStreamKey(), skuId, postUrl, paramsMap, e.getMessage());
            return null;
        }
    }

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

    public GoblinGoodsSkuInfoVo getGoodsSkuInfoVoFromMdb(String skuId) {
        return mongoTemplate.findOne(Query.query(Criteria.where("skuId").is(skuId).and("delFlg").is("0")),
                GoblinGoodsSkuInfoVo.class, GoblinGoodsSkuInfoVo.class.getSimpleName());
    }

    /* ------------------------------------------------------------------------------------ */
}
