package com.bonus.mq;

import com.bonus.mq.tx.TxHolder;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.slf4j.MDC;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.sql.SQLException;
import java.util.UUID;

public abstract class MQTemplate {
   private final MQProperties mqProperties;

   protected MQTemplate(MQProperties mqProperties) {
      this.mqProperties = mqProperties;
   }

   protected void checkTopic(String destination) {
      this.mqProperties.checkTopicDefinition(destination.split(":", 2)[0]);
   }

   protected String getTraceId() {
      String existTraceId = MDC.get("X-Trace-Id");
      return existTraceId == null ? UUID.randomUUID().toString() : existTraceId;
   }

   public void sendDelay(String destination, Object payload, int delayTime) {
      this.checkTopic(destination);
      this.sendDelayImpl(destination, payload, delayTime);
   }

   protected abstract void sendDelayImpl(String destination, Object payload, int delayTime);

   public void send(String destination, Object payload) {
      this.checkTopic(destination);
      this.sendImpl(destination, payload);
   }

   protected abstract void sendImpl(String destination, Object payload);

   public TxHolder txBegin(String destination, Object payload) throws SQLException, MQBrokerException, RemotingException, InterruptedException, MQClientException {
      this.checkTopic(destination);
      return this.txBeginImpl(destination, payload);
   }

   protected abstract TxHolder txBeginImpl(String destination, Object payload) throws MQBrokerException, RemotingException, InterruptedException, MQClientException, SQLException;

   public abstract TxHolder mqttTxBegin(String destination, Object payload) throws MQBrokerException, RemotingException, InterruptedException, MQClientException, SQLException;

   public void txEnd(final TxHolder txHolder) {
      if (TransactionSynchronizationManager.isSynchronizationActive()) {
         TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            public void afterCompletion(int status) {
               TransactionSynchronization.super.afterCompletion(status);
               if (status == 0) {
                  txHolder.commit();
               } else if (status == 1) {
                  try {
                     txHolder.rollback();
                  } catch (SQLException e) {
                     throw new RuntimeException(e);
                  }
               } else {
                  txHolder.unknown();
               }
            }
         });
      } else {
         txHolder.commit();
      }
   }

   public abstract void txCommit(TxHolder txHolder);

   public abstract void txRollback(TxHolder txHolder, Throwable exception) throws SQLException;

   public abstract void txUnknown(TxHolder txHolder, Throwable exception);

   public abstract void sendMqtt(String destination, Object payload) throws MQBrokerException, RemotingException, InterruptedException, MQClientException;
}
