package com.brframework.commonmq.core;

import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSON;
import com.brframework.common.utils.DateTimeUtils;
import com.brframework.commonweb.exception.HandleException;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

/**
 * Redisson 实现的MQ客户端
 *
 * @Author xu
 * @Date 2019年9月5日01:06:17
 */
@Component
@Slf4j
public class RedissonMQClient implements MQClient, ApplicationRunner {

    public static final String MQ_KEY = "MQ_MESSAGE";

    @Autowired
    private RedissonClient client;
    //监听topic的listener
    private Map<String, List<MQMessageListener>> topicListeners = Maps.newConcurrentMap();


    @Override
    public void publish(String topic, String message) {

        try {
            MQData mqData = new MQData();
            mqData.setTopic(topic);
            mqData.setMessage(message);
            client.getBlockingQueue(MQ_KEY).put(JSON.toJSONString(mqData));
        } catch (InterruptedException e) {
            log.error("线程切换错误", e);
            throw new HandleException();
        }
    }

    @Override
    public void publishAfterTransaction(String topic, String message) {
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                publish(topic, message);
            }
        });
    }

    @Override
    public void publish(String topic, String message, long delay, TimeUnit unit) {
        try {
            MQData mqData = new MQData();
            mqData.setTopic(topic);
            mqData.setMessage(message);
            RQueue<String> distinationQueue = client.getBlockingQueue(MQ_KEY);
            client.getDelayedQueue(distinationQueue).offer(JSON.toJSONString(mqData), delay, unit);
        } catch (Exception e){
            log.error("线程切换错误", e);
            throw new HandleException();
        }

    }

    @Override
    public void publish(String topic, String message, LocalDateTime time) {
        try {
            MQData mqData = new MQData();
            mqData.setTopic(topic);
            mqData.setMessage(message);
            RQueue<String> distinationQueue = client.getBlockingQueue(MQ_KEY);
            long seconds = DateTimeUtils.toEpochSecond(time) - DateTimeUtils.toEpochSecond(LocalDateTime.now());
            client.getDelayedQueue(distinationQueue).offer(JSON.toJSONString(mqData), seconds, TimeUnit.SECONDS);
        } catch (Exception e){
            log.error("线程切换错误", e);
            throw new HandleException();
        }
    }

    @Override
    public void remove(String topic, String message) {
        MQData mqData = new MQData();
        mqData.setTopic(topic);
        mqData.setMessage(message);
        String value = JSON.toJSONString(mqData);
        RQueue<String> distinationQueue = client.getBlockingQueue(MQ_KEY);
        distinationQueue.remove(value);
        client.getDelayedQueue(distinationQueue).remove(value);
    }

    @Override
    public void addListener(String topic, MQMessageListener listener) {
        if(!topicListeners.containsKey(topic)) topicListeners.put(topic, new Vector<>());

        topicListeners.get(topic).add(listener);
    }

    public void startListener(){

        ThreadUtil.excAsync(() -> {
            while (true){
                try {
                    Object value = client.getBlockingQueue(MQ_KEY).take();
                    MQData mqData = JSON.parseObject(value.toString(), MQData.class);
                    List<MQMessageListener> mqMessageListeners = topicListeners.get(mqData.getTopic());
                    if(mqMessageListeners == null) continue;

                    for (MQMessageListener mqMessageListener : mqMessageListeners) {
                        try {
                            mqMessageListener.onMessage(mqData.getTopic(), mqData.getMessage());
                        } catch (Throwable e){
                            log.error("消息处理错误", e);
                        }
                    }

                } catch (Exception e) {
                    log.error("消息队列出现不可处理错误！", e);
                }
            }

        }, true);

    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("mq initialize ......");
        startListener();
    }
}
