package com.bonus.common.houqin.mq.tx;

import cn.hutool.core.text.CharSequenceUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.SmartMessageConverter;
import org.springframework.messaging.support.MessageBuilder;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class MQTxCheckComposite implements MQTxCheck<Object> {
   private static final Logger log = LoggerFactory.getLogger(MQTxCheckComposite.class);
   private final List<MQTxCheck<Object>> listeners = Lists.newArrayList();
   private final Map<Object, GenericTypeHolder> typeHolderHashMap = Maps.newHashMap();
   private final MessageConverter messageConverter;

   public MQTxCheckComposite(MessageConverter messageConverter) {
      this.messageConverter = messageConverter;
   }

   public boolean support(Message message) {
      return true;
   }

   public MQTxState check(MessageExt message, Object payload) {
      LinkedList<? extends MQTxCheck<Object>> trxExecutors = this.listeners.stream().filter((listener) -> {
         return listener.support(message);
      }).collect(Collectors.toCollection(LinkedList::new));
      if (trxExecutors.size() != 1) {
         Logger var10000 = log;
         String var10002 = message.getTopic();
         var10000.error("Message[{}] need exactly one listener, but found {}, message was discarded.", var10002 + (CharSequenceUtil.isNotBlank(message.getTags()) ? ":" + message.getTags() : ""), trxExecutors);
         return MQTxState.ROLLBACK;
      } else {
         MQTxCheck<Object> trxExecutor = (MQTxCheck)trxExecutors.getFirst();
         GenericTypeHolder typeHolder = (GenericTypeHolder)this.typeHolderHashMap.get(trxExecutor);
         return trxExecutor.check(message, this.doConvertMessage(message, typeHolder.getMessageType(), typeHolder.getMethodParameter()));
      }
   }

   private Object doConvertMessage(MessageExt messageExt, Type messageType, MethodParameter methodParameter) {
      if (!Objects.equals(messageType, MessageExt.class) && !Objects.equals(messageType, Message.class)) {
         String str = new String(messageExt.getBody(), StandardCharsets.UTF_8);
         if (Objects.equals(messageType, String.class)) {
            return str;
         } else {
            try {
               return messageType instanceof Class ? this.messageConverter.fromMessage(MessageBuilder.withPayload(str).build(), (Class)messageType) : ((SmartMessageConverter)this.messageConverter).fromMessage(MessageBuilder.withPayload(str).build(), (Class)((ParameterizedType)messageType).getRawType(), methodParameter);
            } catch (Exception var6) {
               log.info("convert failed. str:{}, msgType:{}", str, messageType);
               throw new RuntimeException("cannot convert message to " + String.valueOf(messageType), var6);
            }
         }
      } else {
         return messageExt;
      }
   }

   public void addTrxExecutor(MQTxCheck<Object> trxExecutor) {
      Type messageType = this.parseMessageType(trxExecutor);
      MethodParameter methodParameter = this.parseMethodParameter(trxExecutor, messageType);
      this.typeHolderHashMap.put(trxExecutor, new GenericTypeHolder(messageType, methodParameter));
      this.listeners.add(trxExecutor);
   }

   private Type parseMessageType(MQTxCheck<Object> trxExecutor) {
      Class<?> targetClass = AopProxyUtils.ultimateTargetClass(trxExecutor);

      Type matchedGenericInterface;
      Type[] actualTypeArguments;
      for(matchedGenericInterface = null; Objects.nonNull(targetClass); targetClass = targetClass.getSuperclass()) {
         actualTypeArguments = targetClass.getGenericInterfaces();
         Type[] var5 = actualTypeArguments;
         int var6 = actualTypeArguments.length;

         for(int var7 = 0; var7 < var6; ++var7) {
            Type type = var5[var7];
            if (type instanceof ParameterizedType && Objects.equals(((ParameterizedType)type).getRawType(), MQTxCheck.class)) {
               matchedGenericInterface = type;
               break;
            }
         }
      }

      if (Objects.isNull(matchedGenericInterface)) {
         return Object.class;
      } else {
         actualTypeArguments = ((ParameterizedType)matchedGenericInterface).getActualTypeArguments();
         if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {
            return actualTypeArguments[0];
         } else {
            return Object.class;
         }
      }
   }

   private MethodParameter parseMethodParameter(MQTxCheck<Object> trxExecutor, Type messageType) {
      Class<?> targetClass = AopProxyUtils.ultimateTargetClass(trxExecutor);
      Class clazz;
      if (messageType instanceof ParameterizedType && this.messageConverter instanceof SmartMessageConverter) {
         clazz = (Class)((ParameterizedType)messageType).getRawType();
      } else {
         if (!(messageType instanceof Class)) {
            throw new RuntimeException("parameterType:" + String.valueOf(messageType) + " of MQTxCheck#check method is not supported");
         }

         clazz = (Class)messageType;
      }

      try {
         Method method = targetClass.getMethod("check", MessageExt.class, clazz);
         return new MethodParameter(method, 1);
      } catch (NoSuchMethodException var6) {
         throw new RuntimeException("parameterType:" + String.valueOf(messageType) + " of MQTxCheck#check method is not supported");
      }
   }
}
