package com.testor.module.mq.config;

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.RejectAndDontRequeueRecoverer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

//常用的三个配置如下
//1---设置手动应答（acknowledge-mode: manual）
// 2---设置生产者消息发送的确认回调机制 (  #这个配置是保证提供者确保消息推送到交换机中，不管成不成功，都会回调
//    publisher-confirm-type: correlated
//    #保证交换机能把消息推送到队列中
//    publisher-returns: true
//     template:
//      #以下是rabbitmqTemplate配置
//      mandatory: true)
// 3---设置重试
@Configuration
public class RabbitConfig {

	@Autowired
	private ConnectionFactory rabbitConnectionFactory;

	//@Bean  缓存连接池
	//public CachingConnectionFactory rabbitConnectionFactory

	@Autowired
	private RabbitProperties properties;

	//这里因为使用自动配置的connectionFactory，所以把自定义的connectionFactory注解掉
	// 存在此名字的bean 自带的连接工厂会不加载（也就是说yml中rabbitmq下一级不生效），如果想自定义来区分开 需要改变bean 的名称
//    @Bean
//    public ConnectionFactory connectionFactory() throws Exception {
//        //创建工厂类
//        CachingConnectionFactory cachingConnectionFactory=new CachingConnectionFactory();
//        //用户名
//        cachingConnectionFactory.setUsername("gust");
//        //密码
//        cachingConnectionFactory.setPassword("gust");
//        //rabbitMQ地址
//        cachingConnectionFactory.setHost("127.0.0.1");
//        //rabbitMQ端口
//        cachingConnectionFactory.setPort(Integer.parseInt("5672"));
//
//        //设置发布消息后回调
//        cachingConnectionFactory.setPublisherReturns(true);
//        //设置发布后确认类型，此处确认类型为交互
//        cachingConnectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
//
//        cachingConnectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CHANNEL);
//        return  cachingConnectionFactory;
//    }


	// 存在此名字的bean 自带的容器工厂会不加载（yml下rabbitmq下的listener下的simple配置），如果想自定义来区分开 需要改变bean 的名称
	@Bean
	public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
		SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
		containerFactory.setConnectionFactory(rabbitConnectionFactory);

		// 并发消费者数量
		containerFactory.setConcurrentConsumers(1);
		containerFactory.setMaxConcurrentConsumers(20);
		// 预加载消息数量 -- QOS
		containerFactory.setPrefetchCount(1);
		// 应答模式（此处设置为手动）
		containerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
		//消息序列化方式
		containerFactory.setMessageConverter(new Jackson2JsonMessageConverter());
		// 设置通知调用链 （这里设置的是重试机制的调用链）
		containerFactory.setAdviceChain(
			RetryInterceptorBuilder
				.stateless()
				.recoverer(new RejectAndDontRequeueRecoverer())
				.retryOperations(rabbitRetryTemplate())
				.build()
		);
		return containerFactory;
	}

	// 存在此名字的bean 自带的容器工厂会不加载（yml下rabbitmq下的template的配置），如果想自定义来区分开 需要改变bean 的名称
	@Bean
	public RabbitTemplate rabbitTemplate(){
		RabbitTemplate rabbitTemplate=new RabbitTemplate(rabbitConnectionFactory);
		//默认是用jdk序列化
		//数据转换为json存入消息队列，方便可视化界面查看消息数据
		rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
		//设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
		rabbitTemplate.setMandatory(true);
		//此处设置重试template后，会再生产者发送消息的时候，调用该template中的调用链
		rabbitTemplate.setRetryTemplate(rabbitRetryTemplate());
		//CorrelationData correlationData, boolean b, String s
		rabbitTemplate.setConfirmCallback(
			(correlationData, b, s) -> {
				System.out.println("ConfirmCallback     "+"相关数据："+  correlationData);
				System.out.println("ConfirmCallback     "+"确认情况："+b);
				System.out.println("ConfirmCallback     "+"原因："+s);
			});
		//Message message, int i, String s, String s1, String s2
		rabbitTemplate.setReturnCallback((message, i, s, s1, s2) -> {
			System.out.println("ReturnCallback：     "+"消息："+message);
			System.out.println("ReturnCallback：     "+"回应码："+i);
			System.out.println("ReturnCallback：     "+"回应消息："+s);
			System.out.println("ReturnCallback：     "+"交换机："+s1);
			System.out.println("ReturnCallback：     "+"路由键："+s2);
		});

		return rabbitTemplate;
	}

	//重试的Template
	@Bean
	public RetryTemplate rabbitRetryTemplate() {
		RetryTemplate retryTemplate = new RetryTemplate();
		// 设置监听  调用重试处理过程
		retryTemplate.registerListener(new RetryListener() {
			@Override
			public <T, E extends Throwable> boolean open(RetryContext retryContext, RetryCallback<T, E> retryCallback) {
				// 执行之前调用 （返回false时会终止执行）
				return true;
			}

			@Override
			public <T, E extends Throwable> void close(RetryContext retryContext, RetryCallback<T, E> retryCallback, Throwable throwable) {
				// 重试结束的时候调用 （最后一次重试 ）
				System.out.println("---------------最后一次调用");

				return ;
			}
			@Override
			public <T, E extends Throwable> void onError(RetryContext retryContext, RetryCallback<T, E> retryCallback, Throwable throwable) {
				//  异常 都会调用
				System.err.println("-----第{}次调用"+retryContext.getRetryCount());
			}
		});
		retryTemplate.setBackOffPolicy(backOffPolicyByProperties());
		retryTemplate.setRetryPolicy(retryPolicyByProperties());
		return retryTemplate;
	}

	@Bean
	public ExponentialBackOffPolicy backOffPolicyByProperties() {
		ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
		long maxInterval = properties.getListener().getSimple().getRetry().getMaxInterval().getSeconds();
		long initialInterval = properties.getListener().getSimple().getRetry().getInitialInterval().getSeconds();
		double multiplier = properties.getListener().getSimple().getRetry().getMultiplier();
		// 重试间隔
		backOffPolicy.setInitialInterval(initialInterval * 1000);
		// 重试最大间隔
		backOffPolicy.setMaxInterval(maxInterval * 1000);
		// 重试间隔乘法策略
		backOffPolicy.setMultiplier(multiplier);
		return backOffPolicy;
	}

	@Bean
	public SimpleRetryPolicy retryPolicyByProperties() {
		SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
		int maxAttempts = properties.getListener().getSimple().getRetry().getMaxAttempts();
		retryPolicy.setMaxAttempts(maxAttempts);
		return retryPolicy;
	}
}
