package com.fzm.mall.server.admin.mq.consumer.order_center;

import cn.fzm.chain.simplesdk.constant.TxStatusEnum;
import cn.fzm.chain.simplesdk.model.TxResult;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fzm.mall.server.admin.block_chain.util.ChainUtil;
import com.fzm.mall.server.admin.config.Redis;
import com.fzm.mall.server.admin.constant.MALLGlobalConfig;
import com.fzm.mall.server.admin.mq.MsgProducer;
import com.fzm.mall.server.admin.mq.enums.QueueTTLTypeEnum;
import com.fzm.mall.server.admin.order.entity.dto.TranDto;
import com.fzm.mall.server.admin.order.model.DeliveryRefund;
import com.fzm.mall.server.admin.order.model.Order;
import com.fzm.mall.server.admin.order.service.IDeliveryRefundService;
import com.fzm.mall.server.admin.order.service.IOrderService;
import io.netty.util.internal.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 提币确认
 *
 * @author lgang
 * @date 创建时间：2018年12月13日 下午3:01:41
 */
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class WithdrawConsumer {

    private final ChainUtil chainUtil;
    private final MsgProducer msgProducer;
    private final IDeliveryRefundService deliveryRefundService;
    private final IOrderService orderService;
    private final Redis redis;

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue(value = "ly.order.refund.conins.queue"), exchange = @Exchange(value = "ly-order-refund-coins-exchange"), key = "ly.order.refund.coins.key")})
    public void receiver(String message) {
        try {
            if (StringUtil.isNullOrEmpty(message)) {
                return;
            }
            TranDto tranDto = JSON.parseObject(message, TranDto.class);
            if (tranDto == null) {
                log.error("admin WithdrawConsumer :" + message + "-tranDto == null");
                return;
            }
            TxResult txResult = chainUtil.confirmTxWithTxHash(tranDto.getHash());
            TxStatusEnum txStatusEnum = txResult.getStatus();
            switch (txStatusEnum) {
                case SUCCESS:
                    commit(tranDto, true);
                    break;
                case FAIL:
                    rollBack(tranDto);
                    break;
                default:
                    resend(tranDto);
                    break;
            }

        } catch (Exception e) {
            StackTraceElement[] stackTrace = e.getStackTrace();
            StringBuilder sb = new StringBuilder();
            for (StackTraceElement stackTraceElement : stackTrace) {
                sb.append(stackTraceElement.getFileName()).append("*").append(stackTraceElement.getLineNumber()).append("*");
            }
            log.error("admin-WithdrawConsumer-message:" + message + "-e" + sb.toString());
            msgProducer.sendTTL(message, QueueTTLTypeEnum.ORDER_REFUND_COINS_TTL,
                    5 * 1000);
        }

    }

    public void commit(TranDto tranDto, boolean successed) {
        switch (tranDto.getTableType()) {
//            case 1:
//                updateDelivery(tranDto, successed);
//                break;
            case 2:
                updateOrderInfo(tranDto, successed);
                break;
            default:
                //不抛异常,日志记录
                log.error("admin-WithdrawConsumer-tranDto type error: " + tranDto.getTableType());
                break;
        }

    }

    private void updateOrderInfo(TranDto tranDto, boolean successed) {
        UpdateWrapper<Order> orderUpdateWrapper = new UpdateWrapper<>();
        orderUpdateWrapper.eq("oid", tranDto.getTableId());

        Order order = new Order();
        order.setOid(tranDto.getTableId());
        if (successed) {
            order.setOrderState(Order.ORDER_STATE_REFUNDED);
        } else {
            order.setOrderState(Order.ORDER_STATE_REFUNDED_ONCHAIN_FAIL);
        }

        orderService.update(order, orderUpdateWrapper);
    }

    private void updateDelivery(TranDto tranDto, boolean successed) {
        UpdateWrapper<DeliveryRefund> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("order_delivery_id", tranDto.getTableId());

        DeliveryRefund deliveryRefund = new DeliveryRefund();
        deliveryRefund.setOrderDeliveryId(tranDto.getTableId());
        if (successed) {
            deliveryRefund.setState(DeliveryRefund.DELIVERY_REFUND_STATE_RFUND_SUCCESS);
        } else {
            deliveryRefund.setState(DeliveryRefund.DELIVERY_REFUND_STATE_RFUND_FAIL);
        }
        deliveryRefundService.update(deliveryRefund, updateWrapper);
    }

    public void rollBack(TranDto tranDto) {
        log.error("admin-withdraw fail,hash:" + tranDto.getHash());
        if (checkNum(tranDto)){
            return;
        }

        String hash = getHash(tranDto);
        tranDto.setHash(hash);
        msgProducer.sendTTL(JSON.toJSONString(tranDto), QueueTTLTypeEnum.ORDER_REFUND_COINS_TTL,
                5 * 1000);
    }

    private void resend(TranDto tranDto) {
        log.error("admin-withdraw queue resend:" + tranDto);
        if (checkNum(tranDto)){
            return;
        }
        if (StringUtil.isNullOrEmpty(tranDto.getHash())) {
            String hash = getHash(tranDto);
            tranDto.setHash(hash);
        }
        /**
         * 重试时间加长为20s
         */
        msgProducer.sendTTL(JSON.toJSONString(tranDto), QueueTTLTypeEnum.ORDER_REFUND_COINS_TTL,
                20 * 1000);
    }

    private String getHash(TranDto tranDto) {
        String hash;
        if (tranDto.getCoinType().equals(TranDto.COIN_TYPE_NFT_OUT)) {
            long[] tokenIds = new long[]{tranDto.getTokenId()};
            long[] amounts = new long[]{tranDto.getAmount().longValue()};
            hash = chainUtil.evmExec(tokenIds, amounts, tranDto.getFromAddr(), tranDto.getToAddr());
        } else {
            hash = chainUtil.evmTransfer(tranDto.getToAddr(), tranDto.getFromAddr(), tranDto.getTokenId(), tranDto.getAmount().longValue());
        }
        return hash;
    }

    private boolean checkNum(TranDto tranDto){
        /**
         * 失败重试3次
         */
        String redisKey = MALLGlobalConfig.PROJECT_PREFIX + "_" + tranDto.getTableId();
        String countStr = redis.getKey(redisKey);
        if (countStr == null) {
            redis.setKey(redisKey, "1", 600000);
        } else if (Integer.valueOf(countStr) < 3) {
            Integer count = Integer.valueOf(countStr);
            count++;
            redis.setKey(redisKey, count.toString(), 600000);
        } else {
            //失败重试次数满了，操作失败
            commit(tranDto, false);
            return true;
        }
        return false;
    }
}
