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

import org.junit.jupiter.api.Test;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;

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

/**
 * SSRF guard тесты. Часть тестов используют InetAddress.getByName() с
 * IP literal — DNS не нужен. Тесты с hostname (e.g. example.com) не
 * запускаем чтобы CI работал офлайн.
 */
class SsrfGuardTest {

  private final SsrfGuard guard = new SsrfGuard(List.of("trusted.internal.test"));

  @Test
  void rejectsLoopbackIp() {
    String reason = guard.validate("http://127.0.0.1:8080/hook");
    assertThat(reason).contains("private/loopback");
  }

  @Test
  void rejectsLinkLocalIp() {
    String reason = guard.validate("http://169.254.169.254/latest/meta-data");
    assertThat(reason).contains("private/loopback");
  }

  @Test
  void rejectsRfc1918Ip10() {
    String reason = guard.validate("http://10.0.0.1/hook");
    assertThat(reason).contains("private/loopback");
  }

  @Test
  void rejectsRfc1918Ip192_168() {
    String reason = guard.validate("http://192.168.100.161/hook");
    assertThat(reason).contains("private/loopback");
  }

  @Test
  void rejectsAnyLocalIp() {
    String reason = guard.validate("http://0.0.0.0/hook");
    assertThat(reason).contains("private/loopback");
  }

  @Test
  void rejectsNonHttpScheme() {
    String reason = guard.validate("ftp://example.com/hook");
    assertThat(reason).contains("URL scheme must be http or https");
  }

  @Test
  void rejectsMissingHost() {
    String reason = guard.validate("http:///hook");
    assertThat(reason).contains("must have a host");
  }

  @Test
  void rejectsUnresolvableHost() {
    String reason = guard.validate("http://this-host-must-not-exist-ordinis-test.invalid/");
    assertThat(reason).contains("DNS resolution failed");
  }

  @Test
  void allowedHostsBypassPrivateCheck() {
    // trusted.internal.test в allowlist — даже если резолвится в private, OK
    SsrfGuard guardWithAllow = new SsrfGuard(List.of("127.0.0.1"));
    // Подаём 127.0.0.1 как hostname — но literal IP всё равно private check
    // НО allowlist обрабатывается ПЕРЕД resolution через host string match,
    // не IP. Если URL hostname == allowlist entry, OK.
    String reason = guardWithAllow.validate("http://127.0.0.1/hook");
    assertThat(reason).isNull();
  }

  @Test
  void emptyAllowlistDefaultsToBlockingPrivate() {
    SsrfGuard guardNone = new SsrfGuard(List.of());
    assertThat(guardNone.validate("http://127.0.0.1/")).isNotNull();
  }

  @Test
  void nullAllowlistTreatedAsEmpty() {
    SsrfGuard guardNull = new SsrfGuard(null);
    assertThat(guardNull.validate("http://127.0.0.1/")).isNotNull();
  }

  @Test
  void isPrivateRecognizesLoopback() throws UnknownHostException {
    assertThat(SsrfGuard.isPrivate(InetAddress.getByName("127.0.0.1"))).isTrue();
    assertThat(SsrfGuard.isPrivate(InetAddress.getByName("::1"))).isTrue();
  }

  @Test
  void isPrivateRecognizesLinkLocal() throws UnknownHostException {
    assertThat(SsrfGuard.isPrivate(InetAddress.getByName("169.254.0.1"))).isTrue();
  }

  @Test
  void isPrivateRecognizesSiteLocal() throws UnknownHostException {
    assertThat(SsrfGuard.isPrivate(InetAddress.getByName("10.0.0.1"))).isTrue();
    assertThat(SsrfGuard.isPrivate(InetAddress.getByName("172.16.0.1"))).isTrue();
    assertThat(SsrfGuard.isPrivate(InetAddress.getByName("192.168.1.1"))).isTrue();
  }

  @Test
  void isPrivateAllowsPublicIp() throws UnknownHostException {
    // 1.1.1.1 = Cloudflare DNS, public
    assertThat(SsrfGuard.isPrivate(InetAddress.getByName("1.1.1.1"))).isFalse();
    // 8.8.8.8 = Google DNS, public
    assertThat(SsrfGuard.isPrivate(InetAddress.getByName("8.8.8.8"))).isFalse();
  }
}
