test_that("reverse_geo_lite_sf() rejects invalid coordinate inputs", {
  expect_snapshot(error = TRUE, reverse_geo_lite_sf(0, c(2, 3)))
  expect_snapshot(error = TRUE, reverse_geo_lite_sf("a", "a"))
})

test_that("reverse_geo_lite_sf() reports coordinates clamped to bounds", {
  skip_nominatim_ci()

  expect_snapshot(obj <- reverse_geo_lite_sf(0, 200))
  expect_equal(nrow(obj), 1)
  expect_equal(obj$lon, 180)
  expect_equal(obj$address, NA_character_)
  expect_s3_class(obj, "sf")
  expect_s3_class(obj, "tbl")
  expect_true(sf::st_is_empty(obj))
  expect_identical(sf::st_crs(obj), sf::st_crs(4326))

  expect_snapshot(obj <- reverse_geo_lite_sf(200, 200))
  expect_equal(nrow(obj), 1)
  expect_equal(obj$lat, 90)
  expect_equal(obj$address, NA_character_)
  expect_s3_class(obj, "sf")
  expect_s3_class(obj, "tbl")
  expect_true(sf::st_is_empty(obj))
  expect_identical(sf::st_crs(obj), sf::st_crs(4326))
})


test_that("reverse_geo_lite_sf() returns empty geometry without a match", {
  local_mocked_bindings(api_call = function(...) {
    test_fixture("reverse-error.json")
  })

  expect_snapshot(obj <- reverse_geo_lite_sf(89.999999, 179.9999))

  expect_equal(nrow(obj), 1)
  expect_equal(obj$lat, 89.999999)
  expect_equal(obj$lon, 179.9999)
  expect_s3_class(obj, "tbl")
  expect_s3_class(obj, "sf")
  expect_named(obj, c("address", "lat", "lon", "geometry"))
  expect_equal(obj$address, NA_character_)

  expect_snapshot(
    obj_renamed <- reverse_geo_lite_sf(89.999999, 179.9999, address = "adddata")
  )

  expect_named(obj_renamed, c("adddata", "lat", "lon", "geometry"))

  names(obj_renamed) <- names(obj)

  expect_identical(sf::st_drop_geometry(obj), sf::st_drop_geometry(obj_renamed))
  expect_identical(sf::st_geometry(obj), sf::st_geometry(obj_renamed))
})


test_that("reverse_geo_lite_sf() returns point and polygon geometries", {
  skip_nominatim_ci()

  obj <- reverse_geo_lite_sf(42, 3)
  expect_s3_class(obj, "sf")
  expect_s3_class(obj, "tbl")
  expect_equal(nrow(obj), 1)
  expect_identical(as.character(sf::st_geometry_type(obj)), "POINT")

  # Polygon

  expect_message(
    test <- reverse_geo_lite_sf(
      c(42.34, 89.9999, 39.6777),
      c(-3.474, -179.9999, -4.8383),
      points_only = FALSE,
      custom_query = list(zoom = 5)
    ),
    "No results were found"
  )

  expect_match(
    as.character(sf::st_geometry_type(test)),
    "POLYGON|GEOMETRYCOLLECTION"
  )
  expect_s3_class(test, "sf")
  expect_s3_class(test, "tbl")
  expect_equal(nrow(test), 3)

  expect_identical(sf::st_is_empty(test), c(FALSE, TRUE, FALSE))
})

test_that("reverse_geo_lite_sf() parses a successful GeoJSON response", {
  local_mocked_bindings(api_call = function(...) {
    test_fixture("reverse-one.geojson")
  })

  obj <- reverse_geo_lite_sf(40.4168, -3.7038, full_results = TRUE)

  expect_s3_class(obj, "sf")
  expect_s3_class(obj, "tbl")
  expect_equal(nrow(obj), 1)
  expect_equal(obj$lat, 40.4168)
  expect_equal(obj$lon, -3.7038)
  expect_identical(as.character(sf::st_geometry_type(obj)), "POINT")
  expect_contains(names(obj), c("address.city", "extratags.wikidata"))
})

test_that("reverse_geo_lite_sf() returns sf results and forwards zoom", {
  skip_nominatim_ci()

  obj <- reverse_geo_lite_sf(40.4207414, -3.6687109)
  expect_s3_class(obj, "tbl")
  expect_s3_class(obj, "sf")
  expect_equal(nrow(obj), 1)

  expect_named(obj, c("address", "lat", "lon", "geometry"))

  # Same with different zoom
  obj_zoom <- reverse_geo_lite_sf(
    40.4207414,
    -3.6687109,
    custom_query = list(zoom = 3)
  )

  expect_s3_class(obj_zoom, "tbl")
  expect_s3_class(obj_zoom, "sf")

  expect_equal(nrow(obj_zoom), 1)
  expect_false(identical(obj, obj_zoom))
})

test_that("reverse_geo_lite_sf() handles batches and renamed columns", {
  skip_nominatim_ci()

  # Several coordinates
  sev <- reverse_geo_lite_sf(
    lat = c(40.75728, 55.95335),
    long = c(-73.98586, -3.188375)
  )

  expect_equal(nrow(sev), 2)
  expect_s3_class(sev, "tbl")
  expect_s3_class(sev, "sf")

  # Check opts
  obj <- reverse_geo_lite_sf(40.4207414, -3.6687109, address = "addrs")

  expect_s3_class(obj, "tbl")
  expect_s3_class(obj, "sf")
  expect_equal(nrow(obj), 1)

  expect_named(obj, c("addrs", "lat", "lon", "geometry"))

  # Check opts
  obj <- reverse_geo_lite_sf(
    40.4207414,
    -3.6687109,
    address = "addrs",
    return_coords = FALSE
  )

  expect_s3_class(obj, "tbl")
  expect_s3_class(obj, "sf")
  expect_named(obj, c("addrs", "geometry"))
})

test_that("reverse_geo_lite_sf() controls coordinate and full-result columns", {
  skip_nominatim_ci()

  obj <- reverse_geo_lite_sf(
    40.4207414,
    -3.6687109,
    address = "addrs",
    return_coords = FALSE,
    full_results = TRUE
  )

  expect_s3_class(obj, "tbl")
  expect_s3_class(obj, "sf")
  expect_identical(names(obj)[1:3], c("addrs", "lat", "lon"))
  expect_gt(ncol(obj), 5)

  obj2 <- reverse_geo_lite_sf(
    40.4207414,
    -3.6687109,
    address = "addrs",
    return_coords = TRUE,
    full_results = TRUE
  )

  expect_identical(obj, obj2)
})


test_that("reverse_geo_lite_sf() unnests full results without list columns", {
  skip_nominatim_ci()

  # Several coordinates
  sev <- reverse_geo_lite_sf(
    lat = c(40.75728, 55.95335),
    long = c(-73.98586, -3.188375),
    full_results = TRUE,
    custom_query = list(extratags = TRUE)
  )

  expect_s3_class(sev, "tbl")
  expect_equal(nrow(sev), 2)

  # Classes of all cols

  colclass <- vapply(sf::st_drop_geometry(sev), class, FUN.VALUE = character(1))

  # Not lists
  expect_no_match(colclass, "list", fixed = TRUE)
})

test_that("reverse_geo_lite_sf() deduplicates coordinates and restores order", {
  local_mocked_bindings(reverse_geo_lite_sf_single = function(
    lat_cap,
    long_cap,
    address,
    ...
  ) {
    mock_reverse_sf(lat_cap, long_cap, address)
  })

  # Dupes

  lats <- rep(c(40.75728, 55.95335), 50)
  longs <- rep(c(-73.98586, -3.188375), 50)

  expect_silent(dup <- reverse_geo_lite_sf(lats, longs, progressbar = FALSE))

  expect_s3_class(dup, "sf")
  expect_s3_class(dup, "tbl")
  expect_equal(nrow(dup), 100)

  nms <- unique(as.character(dup$address))
  expect_length(nms, 2)
  expect_equal(as.character(dup$address), rep(nms, 50))

  # Check deduping
  dedup <- dplyr::distinct(dup)

  expect_equal(nrow(dedup), 2)
  expect_equal(as.character(dedup$address), nms)
})

test_that("reverse_geo_lite_sf() shows progress for coordinate batches", {
  local_mocked_bindings(reverse_geo_lite_sf_single = function(
    lat_cap,
    long_cap,
    address,
    ...
  ) {
    mock_reverse_sf(lat_cap, long_cap, address)
  })

  lat <- c(40.75728, 55.95335)
  long <- c(-73.98586, -3.188375)

  # No pbar
  expect_silent(reverse_geo_lite_sf(lat[1], long[1]))
  expect_silent(reverse_geo_lite_sf(lat[1], long[1], progressbar = TRUE))

  # Get a pbar
  expect_output(reverse_geo_lite_sf(lat, long))

  # Not
  expect_silent(reverse_geo_lite_sf(lat, long, progressbar = FALSE))
})
test_that("reverse_geo_lite_sf() returns empty geometry after API failure", {
  local_mocked_bindings(api_call = function(...) FALSE)

  expect_snapshot(
    several <- reverse_geo_lite_sf(40.75728, -73.98, full_results = TRUE)
  )

  expect_all_equal(sf::st_is_empty(several), TRUE)
})
