package <%= packageName %>;

import org.assertj.core.api.Condition;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;

import static <%= packageName %>.RabbitConfiguration.EXCHANGE_NAME;
import static <%= packageName %>.RabbitConfiguration.QUEUE_NAME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

class RabbitConfigurationTest {

    private RabbitConfiguration rabbitConfiguration;


    private static BindingFromExchangeToQueueWithRoutingKeyCondition binding() {
        return new BindingFromExchangeToQueueWithRoutingKeyCondition(RabbitConfiguration.EXCHANGE_NAME,
                                                                     RabbitConfiguration.QUEUE_NAME,
                                                                     RabbitConfiguration.ROUTING_KEY);
    }

    @BeforeEach
    void setUp() {
        rabbitConfiguration = new RabbitConfiguration();
    }

    @Test
    void testGetExchange() {
        assertThat(rabbitConfiguration
                       .exchange()
                       .getName()).isEqualTo(EXCHANGE_NAME);
    }

    @Test
    void testGetQueues() {
        Queue queue = rabbitConfiguration.queue();
        assertThat(queue.getName()).isEqualTo(QUEUE_NAME);
    }

    @Test
    void testGetBindings() {
        Binding binding = rabbitConfiguration.binding(rabbitConfiguration.queue(), rabbitConfiguration.exchange());

        assertThat(binding).is(binding());
    }

    @Test
    void testRabbitTemplate() {
        ConnectionFactory factory = mock(ConnectionFactory.class);
        RabbitTemplate template = rabbitConfiguration.rabbitTemplate(factory);
        assertThat(template.getConnectionFactory()).isEqualTo(factory);
        assertThat(template.getMessageConverter()).isNotNull();
        assertThat(template.getMessageConverter()).isInstanceOf(Jackson2JsonMessageConverter.class);
    }

    @Test
    void testProducerJackson2MessageConverter() {
        assertThat(rabbitConfiguration.consumerJackson2MessageConverter()).isNotNull();
    }

    @Test
    void testConsumerJackson2MessageConverter() {
        assertThat(rabbitConfiguration.consumerJackson2MessageConverter()).isNotNull();
    }

    @Test
    void testMessageHandlerMethodFactory() {
        DefaultMessageHandlerMethodFactory factory = rabbitConfiguration.messageHandlerMethodFactory();
        assertThat(factory).isNotNull();
    }

    @Test
    void testConfigureRabbitListeners() {
        RabbitListenerEndpointRegistrar registrar = mock(RabbitListenerEndpointRegistrar.class);
        rabbitConfiguration.configureRabbitListeners(registrar);
        ArgumentCaptor<MessageHandlerMethodFactory> captor = ArgumentCaptor.forClass(
            MessageHandlerMethodFactory.class);
        verify(registrar).setMessageHandlerMethodFactory(captor.capture());
        MessageHandlerMethodFactory messageHandlerMethodFactory = captor.getValue();
        assertThat(messageHandlerMethodFactory).isNotNull();
        assertThat(messageHandlerMethodFactory).isInstanceOf(DefaultMessageHandlerMethodFactory.class);
    }

   private static class BindingFromExchangeToQueueWithRoutingKeyCondition
        extends Condition<Binding> {
        BindingFromExchangeToQueueWithRoutingKeyCondition(String exchangeName,
                                                          String queueName,
                                                          String routingKey) {
            super(binding -> exchangeName.equals(binding.getExchange())
                             && queueName.equals(binding.getDestination())
                             && binding.getDestinationType() == Binding.DestinationType.QUEUE
                             && routingKey.equals(binding.getRoutingKey()),
                  "Queue [name=%s]",
                  queueName);
        }
    }
}
