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

import cloud.nstart.terravault.ordinis.domain.DataScope;
import cloud.nstart.terravault.ordinis.domain.outbox.OutboxEvent;
import cloud.nstart.terravault.ordinis.domain.webhook.WebhookSubscription;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Unit-тесты filter logic в {@link WebhookDispatcher#matches}. Самая
 * критическая бизнес-логика дисптчера (если matches врёт — либо доставим
 * не туда, либо пропустим). Полный e2e тест с embedded HTTP server —
 * отложен (см. Phase 5 e2e plan).
 */
class WebhookDispatcherMatchTest {

  private static OutboxEvent event(DataScope scope, String dictName, String eventType) {
    OutboxEvent e = new OutboxEvent(
        eventType,
        "DICTIONARY_RECORD",
        "test-id",
        scope,
        JsonNodeFactory.instance.objectNode(),
        "ordinis.cuod.events." + scope.name().toLowerCase(),
        "test-key");
    e.setDictionaryName(dictName);
    return e;
  }

  private static WebhookSubscription sub(
      List<DataScope> scopeFilter,
      List<String> dictFilter,
      List<String> eventTypeFilter) {
    WebhookSubscription s = new WebhookSubscription("test", "https://x.example.com", "secret", "test");
    s.setScopeFilter(scopeFilter);
    s.setDictionaryFilter(dictFilter);
    s.setEventTypeFilter(eventTypeFilter);
    return s;
  }

  @Test
  void emptyFiltersMatchAnything() {
    OutboxEvent e = event(DataScope.PUBLIC, "spacecraft", "record.created");
    WebhookSubscription s = sub(null, null, null);
    assertThat(WebhookDispatcher.matches(e, s)).isTrue();
  }

  @Test
  void scopeFilterMatchesWhenIncluded() {
    OutboxEvent e = event(DataScope.INTERNAL, "spacecraft", "record.created");
    WebhookSubscription s = sub(List.of(DataScope.INTERNAL, DataScope.RESTRICTED), null, null);
    assertThat(WebhookDispatcher.matches(e, s)).isTrue();
  }

  @Test
  void scopeFilterRejectsWhenNotIncluded() {
    OutboxEvent e = event(DataScope.PUBLIC, "spacecraft", "record.created");
    WebhookSubscription s = sub(List.of(DataScope.INTERNAL, DataScope.RESTRICTED), null, null);
    assertThat(WebhookDispatcher.matches(e, s)).isFalse();
  }

  @Test
  void dictionaryFilterMatchesWhenIncluded() {
    OutboxEvent e = event(DataScope.PUBLIC, "spacecraft", "record.created");
    WebhookSubscription s = sub(null, List.of("spacecraft", "satellite_type"), null);
    assertThat(WebhookDispatcher.matches(e, s)).isTrue();
  }

  @Test
  void dictionaryFilterRejectsWhenNotIncluded() {
    OutboxEvent e = event(DataScope.PUBLIC, "ground_station", "record.created");
    WebhookSubscription s = sub(null, List.of("spacecraft"), null);
    assertThat(WebhookDispatcher.matches(e, s)).isFalse();
  }

  @Test
  void dictionaryFilterRejectsWhenEventDictionaryNull() {
    // Если у события dictionary_name null (не CRUD над record), а sub
    // фильтрует по словарю — не маchaем.
    OutboxEvent e = event(DataScope.PUBLIC, null, "schema.created");
    WebhookSubscription s = sub(null, List.of("spacecraft"), null);
    assertThat(WebhookDispatcher.matches(e, s)).isFalse();
  }

  @Test
  void eventTypeFilterMatchesWhenIncluded() {
    OutboxEvent e = event(DataScope.PUBLIC, "spacecraft", "record.updated");
    WebhookSubscription s = sub(null, null, List.of("record.created", "record.updated"));
    assertThat(WebhookDispatcher.matches(e, s)).isTrue();
  }

  @Test
  void eventTypeFilterRejectsWhenNotIncluded() {
    OutboxEvent e = event(DataScope.PUBLIC, "spacecraft", "record.closed");
    WebhookSubscription s = sub(null, null, List.of("record.created", "record.updated"));
    assertThat(WebhookDispatcher.matches(e, s)).isFalse();
  }

  @Test
  void allFiltersAndedTogether() {
    OutboxEvent e = event(DataScope.INTERNAL, "spacecraft", "record.updated");
    // Всё совпадает — match
    WebhookSubscription s1 = sub(
        List.of(DataScope.INTERNAL),
        List.of("spacecraft"),
        List.of("record.updated"));
    assertThat(WebhookDispatcher.matches(e, s1)).isTrue();

    // Scope не совпадает — no match (хотя dict + event_type matches)
    WebhookSubscription s2 = sub(
        List.of(DataScope.RESTRICTED),
        List.of("spacecraft"),
        List.of("record.updated"));
    assertThat(WebhookDispatcher.matches(e, s2)).isFalse();
  }

  @Test
  void emptyArrayTreatedAsNull() {
    OutboxEvent e = event(DataScope.PUBLIC, "spacecraft", "record.created");
    // Пустой scope_filter (не null) = match all
    WebhookSubscription s = sub(List.of(), List.of(), List.of());
    assertThat(WebhookDispatcher.matches(e, s)).isTrue();
  }
}
