package com.bonus.mq.rocket.mqtt;

import cn.hutool.jwt.JWTUtil;
import com.bonus.mq.MQListener;
import com.bonus.mq.MQMessageConverter;
import com.bonus.mq.MQProperties;
import com.bonus.mq.mqtt.MqttAuth;
import com.bonus.mq.mqtt.MqttAuthCheck;
import com.bonus.mq.mqtt.MqttProperties;
import com.bonus.mq.rocket.RocketListenerContainer;
import com.bonus.mq.rocket.RocketProperties;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.eclipse.paho.mqttv5.client.MqttAsyncClient;
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions;
import org.eclipse.paho.mqttv5.client.persist.MemoryPersistence;
import org.eclipse.paho.mqttv5.common.MqttException;
import org.eclipse.paho.mqttv5.common.MqttMessage;
import org.eclipse.paho.mqttv5.common.util.MqttTopicValidator;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.support.MessageBuilder;

import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

//@Configuration
@Conditional({RocketProperties.MqttEnabled.class})
public class MqttConfiguration implements InitializingBean {
   private static final Logger log = LoggerFactory.getLogger(MqttConfiguration.class);
   public static final String IOT_MESSAGE_LISTENER_CONTAINER_BEAN_NAME = "iotMessageListenerContainer";
   private static final ScheduledExecutorService HEARTBEAT_TICK = Executors.newSingleThreadScheduledExecutor();
   private final MqttProperties mqttProperties;
   private final MessageConverter messageConverter;
   private final MQProperties mqProperties;
   private final RedissonClient redissonClient;
   private final ApplicationContext applicationContext;
   private final RocketMQTemplate rocketMQTemplate;

   public MqttConfiguration(ApplicationContext applicationContext, RocketMQTemplate rocketMQTemplate, RedissonClient redissonClient, MQMessageConverter mqMessageConverter, MQProperties mqProperties, MqttProperties mqttProperties) {
      this.applicationContext = applicationContext;
      this.rocketMQTemplate = rocketMQTemplate;
      this.messageConverter = mqMessageConverter.getMessageConverter();
      this.mqProperties = mqProperties;
      this.mqttProperties = mqttProperties;
      this.redissonClient = redissonClient;
   }

   @Bean
   @ConditionalOnMissingBean
   public MqttAuthController mqttAuthController(ObjectProvider<MqttAuthCheck> mqttAuthServiceObjectProvider) {
      return new MqttAuthController((MqttAuthCheck)mqttAuthServiceObjectProvider.getIfUnique(() -> {
         return new MqttAuthCheck() {
            public MqttAuth.Authenticating.Response authc(MqttAuth.Authenticating.Request auth) {
               return MqttAuth.Authenticating.ALLOW;
            }

            public MqttAuth.Authorizing.Response authz(MqttAuth.Authorizing.Request auth) {
               return MqttAuth.Authorizing.ALLOW;
            }
         };
      }));
   }

   private void registerContainer(MQListener<Object> mqttClient) {
      GenericApplicationContext genericApplicationContext = (GenericApplicationContext)this.applicationContext;
      if (!genericApplicationContext.containsBean("iotMessageListenerContainer")) {
         RocketListenerContainer container = new RocketListenerContainer();
         container.setNameServer(this.mqProperties.getRocketmq().getNameServer());
         container.setAccessKey(this.mqProperties.getRocketmq().getAccessKey());
         container.setSecretKey(this.mqProperties.getRocketmq().getSecretKey());
         container.setEnableMsgTrace(this.mqProperties.getRocketmq().getConsumer().isEnableMsgTrace());
         container.setCustomizedTraceTopic(this.mqProperties.getRocketmq().getConsumer().getCustomizedTraceTopic());
         container.setTlsEnable(this.mqProperties.getRocketmq().getConsumer().isTlsEnable());
         container.setMaxReconsumeTimes(this.mqttProperties.getEmqx().getParentTopicMaxReconsumeTimes());
         container.setTopic(this.mqttProperties.getEmqx().getParentTopic());
         container.setConsumerGroup(this.mqttProperties.getEmqx().getParentTopic() + "_GROUP");
         container.setMqListener(mqttClient);
         container.setMessageConverter(this.messageConverter);
         container.setName(container.getConsumerGroup());
         genericApplicationContext.registerBean("iotMessageListenerContainer", RocketListenerContainer.class, () -> {
            return container;
         }, new BeanDefinitionCustomizer[0]);
         RocketListenerContainer rocketListenerContainer = (RocketListenerContainer)genericApplicationContext.getBean("iotMessageListenerContainer", RocketListenerContainer.class);
         genericApplicationContext.getBeanFactory().initializeBean(rocketListenerContainer, "iotMessageListenerContainer");
         rocketListenerContainer.start();
      }
   }

   private void deregisterContainer() {
      GenericApplicationContext genericApplicationContext = (GenericApplicationContext)this.applicationContext;
      if (genericApplicationContext.containsBean("iotMessageListenerContainer")) {
         RocketListenerContainer rocketListenerContainer = (RocketListenerContainer)genericApplicationContext.getBean("iotMessageListenerContainer", RocketListenerContainer.class);
         rocketListenerContainer.stop();
         genericApplicationContext.getBeanFactory().destroyBean(rocketListenerContainer);
         genericApplicationContext.removeBeanDefinition("iotMessageListenerContainer");
      }
   }

   public void afterPropertiesSet() throws MqttException {
      final MqttAsyncClient mqttClient = new MqttAsyncClient(this.mqttProperties.getEmqx().getBroker(), this.mqttProperties.getEmqx().getBridgeClientId(), new MemoryPersistence());
      mqttClient.setManualAcks(true);
      mqttClient.setCallback(new MqttCallbacks() {
         public void messageArrived(String topic, MqttMessage message) throws Exception {
            String finalTopic = topic.replace(".", "/");
            String[] matchedRocketTopics = (String[])MqttConfiguration.this.mqttProperties.getAllRoutes().entrySet().stream().filter((mapping) -> {
               return MqttTopicValidator.isMatched((String)mapping.getKey(), finalTopic);
            }).map(Map.Entry::getValue).toArray((x$0) -> {
               return new String[x$0];
            });
            String[] var5 = matchedRocketTopics;
            int var6 = matchedRocketTopics.length;

            for(int var7 = 0; var7 < var6; ++var7) {
               String matchedRocketTopic = var5[var7];
               SendResult sendResult = MqttConfiguration.this.rocketMQTemplate.syncSend(matchedRocketTopic, MessageBuilder.withPayload(message.getPayload()).setHeader("__SUB_TOPIC", finalTopic).build());
               if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                  MqttConfiguration.log.error("MQTT message from [{}] ack failed: {}", finalTopic, sendResult.getSendStatus());
                  return;
               }
            }

            mqttClient.messageArrivedComplete(message.getId(), message.getQos());
         }

         public void connectComplete(boolean reconnect, String serverURI) {
            MqttConfiguration.log.info("MQTT broker connected");

            try {
               mqttClient.subscribe("#", 1);
            } catch (Exception var4) {
               MqttConfiguration.log.error("MQTT subscribe failed: {}", var4.getMessage());
               throw new RuntimeException(var4);
            }
         }
      });
      MqttConnectionOptions connOpts = new MqttConnectionOptions();
      connOpts.setUserName("*");
      connOpts.setPassword(JWTUtil.createToken((Map)null, this.mqttProperties.getEmqx().getSecret().getBytes()).getBytes());
      connOpts.setCleanStart(false);
      connOpts.setSessionExpiryInterval(4294967295L);
      HEARTBEAT_TICK.scheduleWithFixedDelay(() -> {
         try {
            RLock lock = this.redissonClient.getLock(this.mqttProperties.getEmqx().getLockKey() + ".mqtt.bridge.lock");
            if (!lock.tryLock()) {
               if (mqttClient.isConnected()) {
                  mqttClient.disconnect();
               }

               this.deregisterContainer();
               return;
            }

            while(true) {
               if (mqttClient.isConnected()) {
                  this.registerContainer((MQListener)new MqttMessageListener(mqttClient));
                  return;
               }

               try {
                  mqttClient.connect(connOpts).waitForCompletion((long)this.mqttProperties.getEmqx().getTimeToWait());
                  this.registerContainer((MQListener)new MqttMessageListener(mqttClient));
                  break;
               } catch (Throwable var6) {
                  log.error("MQTT broker connect failed", var6);

                  try {
                     this.deregisterContainer();
                     TimeUnit.SECONDS.sleep(3L);
                  } catch (Throwable var5) {
                  }
               }
            }
         } catch (Throwable var7) {
         }

      }, 0L, 15L, TimeUnit.SECONDS);
   }
}
