package com.xingxi.common.mq.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName BaseRabbit
 * @projectName xingxi
 * @Deacription
 * @Author FZ.Dong
 * @Date 2020/9/15 14:52
 * @Version 1.0
 **/



public abstract class AbstractRabbit {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRabbit.class);

    /**
     * 死信队列标识
     */
    protected static final String DEAD_FLAG = ".dead";

    /**
     * 延迟队列标识
     */
    protected static final String DELAY_FLAG = ".delay";

    /**
     * 复活队列标识
     */
    protected static final String REVIVE_FLAG = ".revive";

    /**
     * 死信队列消息过期时间
     */
    @Value("${spring.rabbitmq.xMsgTtl}")
    protected Long xMsgTtl;

    /**
     * rabbit连接对象
     */
    @Autowired
    protected ConnectionFactory connectionFactory;


    /**
     * 通过routeKey将exchange和queue绑定在一起，并创建exchange和queue
     *
     * @param exchangeName
     * @param queueName
     * @param routeKey
     * @return
     */
    protected Binding bind(String exchangeName, String queueName, String routeKey) {

        // exchange
        RabbitAdmin admin = new RabbitAdmin(connectionFactory);
        TopicExchange exchange = new TopicExchange(exchangeName, true, false);
        admin.declareExchange(exchange);

        // 死信队列，过期的消息，会存回到queue
        String reviveRoute = routeKey + "." + queueName + REVIVE_FLAG;
        long ttl = (xMsgTtl == null ? 5 * 60 * 1000 : xMsgTtl);
        Map<String, Object> arg = new HashMap<>(3);
        arg.put("x-dead-letter-exchange", exchangeName);
        arg.put("x-dead-letter-routing-key", reviveRoute);
        // 毫秒
        arg.put("x-message-ttl", ttl);
        // 无消费者
        String deadQueueName = queueName + DEAD_FLAG;
        String deadRoute = routeKey + "." + queueName + DEAD_FLAG;
        Queue deadQueue = new Queue(deadQueueName, true, false, false, arg);
        admin.declareQueue(deadQueue);
        Binding binding = BindingBuilder.bind(deadQueue).to(exchange).with(deadRoute);
        admin.declareBinding(binding);
        LOGGER.info("通过路由【{}】将交换机【{}】和队列【{}】绑定,xMsgTtl={}",
                deadRoute, exchangeName, deadQueueName, ttl);

        // 队列，退回的消息会存放deadQueue
        arg = new HashMap<>(2);
        arg.put("x-dead-letter-exchange", exchangeName);
        arg.put("x-dead-letter-routing-key", deadRoute);
        Queue queue = new Queue(queueName, true, false, false, arg);
        admin.declareQueue(queue);

        binding = BindingBuilder.bind(queue).to(exchange).with(routeKey);
        admin.declareBinding(binding);
        LOGGER.info("通过路由【{}】将交换机【{}】和队列【{}】绑定", routeKey, exchangeName, queueName);

        binding = BindingBuilder.bind(queue).to(exchange).with(reviveRoute);
        admin.declareBinding(binding);
        LOGGER.info("通过路由【{}】将交换机【{}】和队列【{}】绑定", reviveRoute, exchangeName, queueName);

        return binding;
    }

    /**
     * 通过routeKey将exchange和queue绑定在一起，并创建exchange和queue
     *
     * @param exchangeName
     * @param queueName
     * @param routeKey
     * @return
     */
    protected Binding bindDelay(String exchangeName, String queueName, String routeKey, long xmTtl) {

        // exchange
        RabbitAdmin admin = new RabbitAdmin(connectionFactory);
        TopicExchange exchange = new TopicExchange(exchangeName, true, false);
        admin.declareExchange(exchange);

        // 死信队列，过期的消息，会存回到queue
        long ttl = (xMsgTtl == null ? 5 * 60 * 1000 : xMsgTtl);
        String reviveRoute = routeKey + "." + queueName + REVIVE_FLAG;
        String deadRoute = routeKey + "." + queueName + DEAD_FLAG;
        String delayRoute = routeKey + "." + queueName + DELAY_FLAG;

        Map<String, Object> arg = new HashMap<>(3);
        arg.put("x-dead-letter-exchange", exchangeName);
        arg.put("x-dead-letter-routing-key", reviveRoute);
        // 毫秒
        arg.put("x-message-ttl", ttl);

        // 无消费者
        String deadQueueName = queueName + DEAD_FLAG;
        Queue deadQueue = new Queue(deadQueueName, true, false, false, arg);
        admin.declareQueue(deadQueue);
        // 死信队列 ： 死信路由
        Binding binding = BindingBuilder.bind(deadQueue).to(exchange).with(deadRoute);
        admin.declareBinding(binding);
        LOGGER.info("通过路由【{}】将交换机【{}】和队列【{}】绑定,xMsgTtl={}", deadRoute, exchangeName, deadQueueName, ttl);

        // 队列，退回的消息会存放deadQueue
        arg = new HashMap<>(2);
        arg.put("x-dead-letter-exchange", exchangeName);
        arg.put("x-dead-letter-routing-key", delayRoute);
        arg.put("x-message-ttl", xmTtl);
        // 无消费者
        String delayQueueName = queueName + DELAY_FLAG;
        Queue delayQueue = new Queue(delayQueueName, true, false, false, arg);
        admin.declareQueue(delayQueue);
        // 延迟队列 ： 正常路由
        binding = BindingBuilder.bind(delayQueue).to(exchange).with(routeKey);
        admin.declareBinding(binding);
        LOGGER.info("通过路由【{}】将交换机【{}】和队列【{}】绑定", routeKey, exchangeName, delayQueueName);

        arg = new HashMap<>(1);
        arg.put("x-dead-letter-exchange", exchangeName);
        arg.put("x-dead-letter-routing-key", deadRoute);

        Queue queue = new Queue(queueName, true, false, false, arg);
        admin.declareQueue(queue);
        // 正常队列 ： 延迟路由
        binding = BindingBuilder.bind(queue).to(exchange).with(delayRoute);
        admin.declareBinding(binding);
        LOGGER.info("通过路由【{}】将交换机【{}】和队列【{}】绑定", delayRoute, exchangeName, queueName);

        // 正常队列 ： 复活路由
        binding = BindingBuilder.bind(queue).to(exchange).with(reviveRoute);
        admin.declareBinding(binding);
        LOGGER.info("通过路由【{}】将交换机【{}】和队列【{}】绑定", reviveRoute, exchangeName, queueName);

        return binding;
    }


}
