# -- deprecation ---------------------------------------------------------------

test_that("a5_grid is soft-deprecated in favour of a5_polygon_to_cells", {
  lifecycle::expect_deprecated(
    a5_grid(c(-3.3, 55.9, -3.1, 56.0), resolution = 5),
    "a5_polygon_to_cells"
  )
})

# -- input validation -----------------------------------------------------------

test_that("a5_grid rejects bad bbox", {
  local_quiet_lifecycle()
  expect_error(a5_grid(c(1, 2, 3), resolution = 3), "length 4")
  expect_error(a5_grid(c(1, 2, NA, 4), resolution = 3), "NA")
  expect_error(a5_grid(c(0, 10, 1, 5), resolution = 3), "ymin")
  expect_error(a5_grid(c(0, 5, 1, 5), resolution = 3), "ymin")
  expect_error(a5_grid(c(5, 0, 5, 1), resolution = 3), "xmin.*xmax.*equal")
})

test_that("a5_grid rejects invalid resolution", {
  local_quiet_lifecycle()
  expect_error(a5_grid(c(0, 0, 1, 1), resolution = -1))
  expect_error(a5_grid(c(0, 0, 1, 1), resolution = 31))
})

# -- return type ---------------------------------------------------------------

test_that("a5_grid returns a5_cell at correct resolution", {
  local_quiet_lifecycle()
  cells <- a5_grid(c(-3.3, 55.9, -3.1, 56.0), resolution = 5)
  expect_s3_class(cells, "a5_cell")
  expect_true(length(cells) > 0)
  expect_true(all(a5_get_resolution(cells) == 5L))
})

# -- correctness ---------------------------------------------------------------

test_that("res 0 with global bbox returns 12 cells", {
  local_quiet_lifecycle()
  cells <- a5_grid(c(-180, -90, 180, 90), resolution = 0)
  expect_length(cells, 12L)
})

test_that("result cells intersect the target (bbox path)", {
  local_quiet_lifecycle()
  bbox <- c(-3.3, 55.9, -3.1, 56.0)
  cells <- a5_grid(bbox, resolution = 5)
  target_wkt <- sprintf(
    "POLYGON ((%s %s, %s %s, %s %s, %s %s, %s %s))",
    bbox[1], bbox[2], bbox[3], bbox[2], bbox[3], bbox[4],
    bbox[1], bbox[4], bbox[1], bbox[2]
  )
  filtered <- cells_from_rs(a5_grid_intersects_rs(
    cell_data(cells), target_wkt
  ))
  expect_equal(length(filtered), length(cells))
})

test_that("geometry input filters cells by intersection", {
  local_quiet_lifecycle()
  # Triangle is strictly smaller than its bounding box, so the exact

  # intersection filter should return fewer cells than a bbox grid
  tri <- wk::wkt("POLYGON ((-3.3 55.9, -3.1 55.95, -3.3 56, -3.3 55.9))")
  tri_cells <- a5_grid(tri, resolution = 8)
  bbox_cells <- a5_grid(c(-3.3, 55.9, -3.1, 56.0), resolution = 8)
  expect_true(length(tri_cells) < length(bbox_cells))
  # All triangle cells should be a subset of the bbox cells
  expect_true(all(format(tri_cells) %in% format(bbox_cells)))
})

test_that("interior point is covered by a returned cell", {
  local_quiet_lifecycle()
  bbox <- c(-3.3, 55.9, -3.1, 56.0)
  cells <- a5_grid(bbox, resolution = 5)
  # the centroid of the bbox should fall inside one of the cells
  centre <- a5_lonlat_to_cell(-3.2, 55.95, resolution = 5)
  expect_true(any(format(cells) == format(centre)))
})

test_that("no duplicate cells", {
  local_quiet_lifecycle()
  cells <- a5_grid(c(-3.3, 55.9, -3.1, 56.0), resolution = 5)
  expect_equal(length(cells), length(unique(format(cells))))
})

# -- input types ---------------------------------------------------------------

test_that("a5_grid accepts wkt polygon", {
  local_quiet_lifecycle()
  poly <- wk::wkt("POLYGON ((-3.3 55.9, -3.1 55.9, -3.1 56, -3.3 56, -3.3 55.9))")
  cells <- a5_grid(poly, resolution = 5)
  expect_s3_class(cells, "a5_cell")
  expect_true(length(cells) > 0)
})

test_that("a5_grid accepts a5_cell as area", {
  local_quiet_lifecycle()
  cell <- a5_lonlat_to_cell(-3.19, 55.95, resolution = 3)
  children <- a5_grid(cell, resolution = 5)
  expect_s3_class(children, "a5_cell")
  expect_true(length(children) > 0)
  expect_true(all(a5_get_resolution(children) == 5L))
})

# -- edge cases ----------------------------------------------------------------

test_that("res 1 works (below filter threshold)", {
  local_quiet_lifecycle()
  cells <- a5_grid(c(-180, -90, 180, 90), resolution = 1)
  expect_s3_class(cells, "a5_cell")
  expect_true(all(a5_get_resolution(cells) == 1L))
})

test_that("tiny bbox returns at least 1 cell", {
  local_quiet_lifecycle()
  cells <- a5_grid(c(-3.19, 55.95, -3.189, 55.951), resolution = 8)
  expect_true(length(cells) >= 1L)
})

test_that("antimeridian-crossing bbox works", {
  local_quiet_lifecycle()
  # Fiji: bbox straddles the antimeridian (xmin > xmax)
  cells <- a5_grid(c(177, -19, -178, -17), resolution = 5)
  expect_s3_class(cells, "a5_cell")
  expect_true(length(cells) > 0)
  expect_true(all(a5_get_resolution(cells) == 5L))
  # cells on both sides of the antimeridian
  centres <- a5_cell_to_lonlat(cells)
  lons <- wk::wk_coords(centres)$x
  expect_true(any(lons > 0))
  expect_true(any(lons < 0))
})

test_that("near-pole bbox returns cells", {
  local_quiet_lifecycle()
  # Rust bbox grid skips filtering near poles (buf >= 45) to avoid false negatives
  cells <- a5_grid(c(0, 89.999, 0.001, 90), resolution = 5)
  expect_s3_class(cells, "a5_cell")
  expect_true(length(cells) >= 1L)
})

test_that("a5_grid accepts multiple geometries (GEOMETRYCOLLECTION path)", {
  local_quiet_lifecycle()
  # Two small polygons far apart — should return cells near both
  polys <- wk::wkt(c(
    "POLYGON ((-3.3 55.9, -3.1 55.9, -3.1 56, -3.3 56, -3.3 55.9))",
    "POLYGON ((135 -33, 136 -33, 136 -32, 135 -32, 135 -33))"
  ))
  cells <- a5_grid(polys, resolution = 5)
  expect_s3_class(cells, "a5_cell")
  expect_true(length(cells) > 0)
  centres <- a5_cell_to_lonlat(cells)
  lons <- wk::wk_coords(centres)$x
  # Cells near both polygons
  expect_true(any(lons < 0))
  expect_true(any(lons > 100))
})

test_that("a5_grid coerces double resolution to integer", {
  local_quiet_lifecycle()
  cells <- a5_grid(c(-3.3, 55.9, -3.1, 56.0), resolution = 5.0)
  expect_s3_class(cells, "a5_cell")
  expect_true(all(a5_get_resolution(cells) == 5L))
})

test_that("a5_grid rejects vector resolution", {
  local_quiet_lifecycle()
  expect_error(a5_grid(c(0, 0, 1, 1), resolution = c(3, 5)), "size 1")
})
