package cloud.nstart.terravault.ordinis.app.e2e;

import org.springframework.test.context.DynamicPropertyRegistry;
import org.testcontainers.containers.KafkaContainer;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;

/**
 * Общий setup Testcontainers для e2e тестов: Postgres 18 + PostGIS-совместимый
 * образ + Kafka 7.x. Контейнеры стартуют один раз (static), переиспользуются
 * между тестовыми классами через {@link org.testcontainers.junit.jupiter.Container}
 * shared mode.
 *
 * <p>Postgres стартует eagerly в class init — нужен всем e2e тестам.
 * Kafka стартует lazy — только если {@link #registerProperties} вызвана. Тесты
 * которые не используют Kafka (например {@code WebhookE2ETest}) экономят
 * 5-10 секунд cold-start на CI и убирают целый класс flakiness (kafka broker
 * не успевает отвечать на ранние produce/poll).
 *
 * <p>Использовать через composition в @SpringBootTest классах.
 */
public final class E2ESupport {

  // Используем postgis image — у нас есть geometry колонки в dictionary_records.
  // db-init.sql создаёт postgis + btree_gist (Liquibase 0001 проверяет их).
  static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>(
      DockerImageName.parse("postgis/postgis:18-3.6")
          .asCompatibleSubstituteFor("postgres"))
      .withDatabaseName("ordinis_test")
      .withUsername("ordinis_test")
      .withPassword("test_password")
      .withInitScript("db-init.sql")
      .withReuse(true);

  // Confluent Kafka — confluentinc/cp-kafka работает out-of-the-box.
  // Confluent 7.6.0 включает в Kafka 3.6 — kraft mode.
  // Lazy: стартует при первом вызове registerProperties() (через kafka()).
  @SuppressWarnings("resource")
  private static final KafkaContainer KAFKA = new KafkaContainer(
      DockerImageName.parse("confluentinc/cp-kafka:7.6.0"))
      .withReuse(true);

  private static volatile boolean kafkaStarted = false;

  static {
    POSTGRES.start();
  }

  private E2ESupport() {}

  /**
   * Lazy-старт Kafka при первом обращении. Synchronized — testcontainers
   * shared между классами, гонок не должно быть.
   */
  private static synchronized KafkaContainer kafka() {
    if (!kafkaStarted) {
      KAFKA.start();
      kafkaStarted = true;
    }
    return KAFKA;
  }

  /**
   * Прокидывает DB + Kafka URLs в Spring Boot context. Использовать в тестах
   * где есть Kafka producers/consumers.
   */
  public static void registerProperties(DynamicPropertyRegistry registry) {
    registerPostgresProperties(registry);
    registry.add("spring.kafka.bootstrap-servers", () -> kafka().getBootstrapServers());
  }

  /**
   * Прокидывает только DB URLs. Использовать в тестах которые не туда не
   * сюда не нуждаются в Kafka — экономит 5-10s cold start на CI и убирает
   * Kafka-специфичную flakiness.
   *
   * <p>Тест ДОЛЖЕН либо отключить {@code spring-kafka} autoconfig
   * через {@code spring.autoconfigure.exclude}, либо отключить outbox
   * poller (он единственный использует {@code KafkaTemplate} в runtime
   * code) через {@code ordinis.outbox.enabled=false}. Иначе Spring при
   * старте попытается подключиться к {@code localhost:9092} и зависнет.
   */
  public static void registerPostgresOnlyProperties(DynamicPropertyRegistry registry) {
    registerPostgresProperties(registry);
  }

  private static void registerPostgresProperties(DynamicPropertyRegistry registry) {
    registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
    registry.add("spring.datasource.username", POSTGRES::getUsername);
    registry.add("spring.datasource.password", POSTGRES::getPassword);
    registry.add("spring.datasource.driver-class-name", () -> "org.postgresql.Driver");

    // Liquibase использует тот же datasource — отдельные креды не нужны.
    registry.add("spring.liquibase.url", POSTGRES::getJdbcUrl);
    registry.add("spring.liquibase.user", POSTGRES::getUsername);
    registry.add("spring.liquibase.password", POSTGRES::getPassword);
  }
}
