package cloud.nstart.terravault.ordinis.readapi.spatial;

import org.junit.jupiter.api.Test;

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

class BoundingBoxTest {

  @Test
  void parsesValidBboxString() {
    BoundingBox bbox = BoundingBox.parse("37.5,55.5,37.8,55.8");
    assertThat(bbox.west()).isEqualTo(37.5);
    assertThat(bbox.south()).isEqualTo(55.5);
    assertThat(bbox.east()).isEqualTo(37.8);
    assertThat(bbox.north()).isEqualTo(55.8);
  }

  @Test
  void parsesNegativeCoordinates() {
    BoundingBox bbox = BoundingBox.parse("-180,-90,180,90");
    assertThat(bbox.west()).isEqualTo(-180);
    assertThat(bbox.east()).isEqualTo(180);
  }

  @Test
  void trimsWhitespace() {
    BoundingBox bbox = BoundingBox.parse("  37.5 , 55.5 , 37.8 , 55.8  ");
    assertThat(bbox.west()).isEqualTo(37.5);
  }

  @Test
  void rejectsEmpty() {
    assertThatThrownBy(() -> BoundingBox.parse(""))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessageContaining("empty");
    assertThatThrownBy(() -> BoundingBox.parse(null))
        .isInstanceOf(IllegalArgumentException.class);
  }

  @Test
  void rejectsWrongCount() {
    assertThatThrownBy(() -> BoundingBox.parse("1,2,3"))
        .hasMessageContaining("4 comma-separated");
    assertThatThrownBy(() -> BoundingBox.parse("1,2,3,4,5"))
        .hasMessageContaining("4 comma-separated");
  }

  @Test
  void rejectsNonNumeric() {
    assertThatThrownBy(() -> BoundingBox.parse("a,b,c,d"))
        .hasMessageContaining("not a number");
  }

  @Test
  void rejectsLongitudeOutOfRange() {
    assertThatThrownBy(() -> BoundingBox.parse("-181,0,1,1"))
        .hasMessageContaining("longitude");
    assertThatThrownBy(() -> BoundingBox.parse("0,0,181,1"))
        .hasMessageContaining("longitude");
  }

  @Test
  void rejectsLatitudeOutOfRange() {
    assertThatThrownBy(() -> BoundingBox.parse("0,-91,1,0"))
        .hasMessageContaining("latitude");
    assertThatThrownBy(() -> BoundingBox.parse("0,0,1,91"))
        .hasMessageContaining("latitude");
  }

  @Test
  void rejectsAntiMeridianCrossing() {
    // west > east — обычно signal что caller хотел anti-meridian (Pacific
    // crossing). v1 не поддерживает — лучше явная ошибка чем неправильный
    // intersect.
    assertThatThrownBy(() -> BoundingBox.parse("170,0,-170,1"))
        .hasMessageContaining("anti-meridian");
  }

  @Test
  void rejectsInvertedNorthSouth() {
    assertThatThrownBy(() -> BoundingBox.parse("0,55,1,30"))
        .hasMessageContaining("south");
  }

  @Test
  void areaInDegreesSquared() {
    BoundingBox unit = new BoundingBox(0, 0, 1, 1);
    assertThat(unit.areaInDegreesSquared()).isEqualTo(1.0);

    BoundingBox moscow = new BoundingBox(37.0, 55.0, 38.0, 56.0);
    assertThat(moscow.areaInDegreesSquared()).isEqualTo(1.0);
  }

  @Test
  void worldwide() {
    BoundingBox world = BoundingBox.parse("-180,-90,180,90");
    assertThat(world.areaInDegreesSquared()).isEqualTo(360.0 * 180.0);
  }
}
