package cloud.nstart.terravault.ordinis.domain.outbox;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.List;

public interface OutboxEventRepository extends JpaRepository<OutboxEvent, Long> {

  /**
   * Unpublished events для polling. Использует partial index
   * {@code idx_outbox_unpublished} (where published_at IS NULL AND dlq_at IS NULL).
   */
  @Query("""
      SELECT o FROM OutboxEvent o
      WHERE o.publishedAt IS NULL AND o.dlqAt IS NULL
      ORDER BY o.id ASC
      """)
  List<OutboxEvent> findUnpublished(Pageable pageable);

  /**
   * Recent events filtered by event_type, regardless of {@code published_at}.
   * Used by NotificationsDispatcher (and future side-channel processors) что
   * RACE с OutboxPoller (Kafka publisher) — Poller polls every 1s и flips
   * {@code published_at} быстрее чем 30s notifications tick → notifications
   * никогда не находил event'ы с {@link #findUnpublished}.
   *
   * <p>Idempotency обеспечивается через own dedupe table (notification_log
   * UNIQUE (event_id, channel, recipient)) — same event может быть processed
   * многократно без duplicate sends.
   *
   * @param types event_type filter (e.g. RecordDraftSubmitted, ...)
   * @param since createdAt cutoff — обычно last 24h чтобы не сканировать
   *              историческую таблицу при cold start
   */
  @Query("""
      SELECT o FROM OutboxEvent o
      WHERE o.eventType IN :types
        AND o.createdAt >= :since
        AND o.dlqAt IS NULL
      ORDER BY o.id ASC
      """)
  List<OutboxEvent> findRecentByEventTypes(
      Collection<String> types, OffsetDateTime since, Pageable pageable);

  /**
   * Сколько ожидает публикации (без DLQ). Метрика
   * {@code ordinis_outbox_pending_events}.
   */
  @Query("SELECT COUNT(o) FROM OutboxEvent o WHERE o.publishedAt IS NULL AND o.dlqAt IS NULL")
  long countPending();

  /**
   * Сколько в DLQ. Метрика {@code ordinis_outbox_dlq_size} — растёт ⇒ алерт.
   */
  long countByDlqAtIsNotNull();

  /**
   * DLQ-листинг для admin UI. Партиальный индекс {@code idx_outbox_dlq} по
   * {@code dlq_at DESC}.
   */
  Page<OutboxEvent> findByDlqAtIsNotNull(Pageable pageable);

  /** @deprecated используйте {@link #countPending()} (не считает DLQ). */
  @Deprecated
  long countByPublishedAtIsNull();
}
