package ru.ibs.socrates.confexporter.rabbitmq;


import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.containers.RabbitMQContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.rabbitmq.dto.SdtLlmRagGateEventDto;
import ru.ibs.socrates.confexporter.service.content.service.ProcessContentService;


@ActiveProfiles({"rabbit-test", /* "disable-feign-client" */})
@SpringBootTest
@Testcontainers
class ContentEventListenerTest {

  @Container
  private static final PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
      .withDatabaseName("testdb")
      .withUsername("test")
      .withPassword("test");
  
  // Запускаем RabbitMQ в контейнере
  @Container
  static RabbitMQContainer rabbitMQ = new RabbitMQContainer("rabbitmq:3-management")
      .withExposedPorts(5672)
      .waitingFor(Wait.forLogMessage(".*Server startup complete.*\\n", 1));

  @DynamicPropertySource
  static void postgresqlProperties(DynamicPropertyRegistry registry) {
    registry.add("spring.datasource.url", postgres::getJdbcUrl);
    registry.add("spring.datasource.password", postgres::getPassword);
    registry.add("spring.datasource.username", postgres::getUsername);

    // Динамически переопределяем настройки
    registry.add("spring.rabbitmq.host", rabbitMQ::getHost);
    registry.add("spring.rabbitmq.port", rabbitMQ::getAmqpPort);
    registry.add("spring.rabbitmq.username", rabbitMQ::getAdminUsername);
    registry.add("spring.rabbitmq.password", rabbitMQ::getAdminPassword);
  }

  @BeforeAll
  static void setup() {
    rabbitMQ.start();
    System.out.println("RabbitMQ started: " + rabbitMQ.isRunning());
  }

  @AfterEach
  void checkRabbitStatus() {
    System.out.println("RabbitMQ logs:\n" + rabbitMQ.getLogs());
  }

  @Autowired
  private RabbitTemplate rabbitTemplate;

  @Autowired
  private ObjectMapper objectMapper;

  // SpyBean для проверки вызовов listener'а
  @MockitoBean
  private ProcessContentService processContentService;

  @Test
  void shouldReceiveRabbitEvent() throws JsonMappingException, JsonProcessingException {

    assertTrue(rabbitMQ.isRunning(), "RabbitMQ container should be running");

    // 2. Отправляем тестовое событие
    String message = """
        {
            "message": {
                "id": "test-id-123",
                "eventName": "Update"
            }
        }
        """;
    SdtLlmRagGateEventDto dto = objectMapper.readValue(message, SdtLlmRagGateEventDto.class);


    doNothing().when(processContentService).processContent(any());

    // Отправка сообщения
    rabbitTemplate.convertAndSend(
        "SDT.LlmRagGate.Application.PublicEvents", // Exchange
        "ContentChangedPublicEvent", // Routing key
        dto);


    await().atMost(30, SECONDS)
        .pollInterval(1, SECONDS)
        .untilAsserted(() -> {

          // 4. Проверяем получение
          verify(processContentService, timeout(5000).atLeast(1))
              .processContent(any());

          verify(processContentService, timeout(5000).atLeast(1))
              .processContent(eq(dto));
        });

  }
}
