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 GeoJsonPolygonTest {

  // Простой square Moscow ~37.5..37.8, ~55.5..55.8 (closed: first == last)
  private static final String VALID_SQUARE = """
      {"type":"Polygon","coordinates":[[
        [37.5,55.5],[37.8,55.5],[37.8,55.8],[37.5,55.8],[37.5,55.5]
      ]]}""";

  @Test
  void acceptsValidPolygon() {
    String canonical = GeoJsonPolygon.validateAndCanonicalize(VALID_SQUARE);
    // Whitespace stripped в canonical form
    assertThat(canonical).doesNotContain("\n");
    assertThat(canonical).contains("\"type\":\"Polygon\"");
    assertThat(canonical).contains("\"coordinates\"");
  }

  @Test
  void acceptsPolygonWithHole() {
    String withHole = """
        {"type":"Polygon","coordinates":[
          [[0,0],[10,0],[10,10],[0,10],[0,0]],
          [[2,2],[3,2],[3,3],[2,3],[2,2]]
        ]}""";
    String canonical = GeoJsonPolygon.validateAndCanonicalize(withHole);
    assertThat(canonical).contains("Polygon");
  }

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

  @Test
  void rejectsNonJson() {
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize("not json {{"))
        .hasMessageContaining("not valid JSON");
  }

  @Test
  void rejectsNonObjectRoot() {
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize("[]"))
        .hasMessageContaining("must be an object");
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize("\"hello\""))
        .hasMessageContaining("must be an object");
  }

  @Test
  void rejectsWrongType() {
    String point = """
        {"type":"Point","coordinates":[37.5,55.5]}""";
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize(point))
        .hasMessageContaining("type must be \"Polygon\"");
  }

  @Test
  void rejectsMissingCoordinates() {
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize("{\"type\":\"Polygon\"}"))
        .hasMessageContaining("coordinates");
  }

  @Test
  void rejectsTooFewPointsInRing() {
    String triangle = """
        {"type":"Polygon","coordinates":[[
          [0,0],[1,0],[0,0]
        ]]}""";
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize(triangle))
        .hasMessageContaining(">= 4 points");
  }

  @Test
  void rejectsUnclosedRing() {
    String unclosed = """
        {"type":"Polygon","coordinates":[[
          [0,0],[1,0],[1,1],[0,1]
        ]]}""";
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize(unclosed))
        .hasMessageContaining("must be closed");
  }

  @Test
  void rejectsLongitudeOutOfRange() {
    String bad = """
        {"type":"Polygon","coordinates":[[
          [-181,0],[1,0],[1,1],[0,1],[-181,0]
        ]]}""";
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize(bad))
        .hasMessageContaining("longitude");
  }

  @Test
  void rejectsLatitudeOutOfRange() {
    String bad = """
        {"type":"Polygon","coordinates":[[
          [0,91],[1,0],[1,1],[0,1],[0,91]
        ]]}""";
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize(bad))
        .hasMessageContaining("latitude");
  }

  @Test
  void rejectsPointWithoutLatitude() {
    String bad = """
        {"type":"Polygon","coordinates":[[
          [0],[1,0],[1,1],[0,1],[0]
        ]]}""";
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize(bad))
        .hasMessageContaining("[longitude, latitude]");
  }

  @Test
  void rejectsTooLargeJson() {
    StringBuilder huge = new StringBuilder("{\"type\":\"Polygon\",\"coordinates\":[[");
    while (huge.length() < GeoJsonPolygon.MAX_JSON_BYTES) {
      huge.append("[0,0],");
    }
    huge.append("[0,0]]]}");
    assertThatThrownBy(() -> GeoJsonPolygon.validateAndCanonicalize(huge.toString()))
        .hasMessageContaining("exceeds");
  }
}
