# explanation of how reverse works:
# https://github.com/easystats/datawizard/issues/106#issuecomment-1066628399

test_that("reverse works with numeric", {
  expect_identical(
    reverse(1:5),
    as.double(5:1)
  )
  expect_identical(
    reverse(-2:2),
    as.double(2:-2)
  )
})

test_that("reverse works with factor", {
  expect_identical(
    reverse(factor(1:5)),
    factor(5:1)
  )
  expect_identical(
    reverse(factor(-2:2)),
    factor(2:-2)
  )
})

test_that("reverse works with data frame", {
  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse(test, select = "x"),
    data.frame(
      x = as.double(5:1),
      y = c(3, 8, 2, 5, 1)
    )
  )
  expect_identical(
    reverse(test, exclude = "x"),
    data.frame(
      x = 1:5,
      y = c(6, 1, 7, 4, 8)
    )
  )
  expect_identical(
    reverse(test),
    data.frame(
      x = as.double(5:1),
      y = c(6, 1, 7, 4, 8)
    )
  )
})

test_that("reverse works with data frame and append", {
  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse(test, select = "x", append = TRUE),
    data.frame(
      x = 1:5,
      y = c(3, 8, 2, 5, 1),
      x_r = as.double(5:1)
    )
  )
  expect_identical(
    reverse(test, append = TRUE),
    data.frame(
      x = 1:5,
      y = c(3, 8, 2, 5, 1),
      x_r = as.double(5:1),
      y_r = c(6, 1, 7, 4, 8)
    )
  )
})

test_that("reverse: arg 'select' works with formula", {
  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse(test, select = ~x),
    data.frame(
      x = as.double(5:1),
      y = c(3, 8, 2, 5, 1)
    )
  )
  expect_identical(
    reverse(test, select = ~ x + y),
    data.frame(
      x = as.double(5:1),
      y = c(6, 1, 7, 4, 8)
    )
  )
})

test_that("reverse: arg 'exclude' works with formula", {
  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse(test, exclude = ~x),
    data.frame(
      x = 1:5,
      y = c(6, 1, 7, 4, 8)
    )
  )
  expect_identical(
    reverse(test, exclude = ~ x + y),
    test
  )
})

test_that("reverse: argument 'range' works", {
  expect_identical(
    reverse(c(1, 3, 4), range = c(0, 4)),
    c(3, 1, 0)
  )
  expect_identical(
    reverse(factor(c(1, 2, 3, 4, 5)), range = 0:10),
    factor(9:5, levels = 0:10)
  )

  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse(test, select = "x", range = c(0, 8)),
    data.frame(
      x = as.double(7:3),
      y = c(3, 8, 2, 5, 1)
    )
  )
  expect_identical(
    reverse(test, range = c(0, 8)),
    data.frame(
      x = as.double(7:3),
      y = c(5, 0, 6, 3, 7)
    )
  )
})

test_that("reverse ignores NA", {
  expect_identical(
    reverse(c(1, 2, 8, NA)),
    c(8, 7, 1, NA)
  )
})

test_that("reverse returns NA if only NA provided", {
  expect_identical(
    reverse(c(NA_real_, NA_real_)),
    c(NA_real_, NA_real_)
  )
  expect_identical(
    reverse(factor(c(NA, NA))),
    factor(c(NA, NA))
  )
})

test_that("reverse warns if single value to reverse", {
  expect_warning(
    reverse(1),
    regexp = "A `range` must be provided for data with only one unique value."
  )
  expect_warning(
    reverse(factor(1)),
    regexp = "A `range` must be provided for data with only one unique value."
  )
})

test_that("reverse msg for unsupported", {
  expect_message(reverse(as.Date(c("2022-04-24", "2022-04-23"))))
})


# Same tests with reverse_scale (alias) --------------------------

test_that("reverse_scale works with numeric", {
  expect_identical(
    reverse_scale(1:5),
    as.double(5:1)
  )
  expect_identical(
    reverse_scale(-2:2),
    as.double(2:-2)
  )
})

test_that("reverse_scale works with factor", {
  expect_identical(
    reverse_scale(factor(1:5)),
    factor(5:1)
  )
  expect_identical(
    reverse_scale(factor(-2:2)),
    factor(2:-2)
  )
})

test_that("reverse_scale works with data frame", {
  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse_scale(test, select = "x"),
    data.frame(
      x = as.double(5:1),
      y = c(3, 8, 2, 5, 1)
    )
  )
  expect_identical(
    reverse_scale(test, exclude = "x"),
    data.frame(
      x = 1:5,
      y = c(6, 1, 7, 4, 8)
    )
  )
  expect_identical(
    reverse_scale(test),
    data.frame(
      x = as.double(5:1),
      y = c(6, 1, 7, 4, 8)
    )
  )
})

test_that("reverse_scale: arg 'select' works with formula", {
  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse_scale(test, select = ~x),
    data.frame(
      x = as.double(5:1),
      y = c(3, 8, 2, 5, 1)
    )
  )
  expect_identical(
    reverse_scale(test, select = ~ x + y),
    data.frame(
      x = as.double(5:1),
      y = c(6, 1, 7, 4, 8)
    )
  )
})

test_that("reverse_scale: arg 'exclude' works with formula", {
  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse_scale(test, exclude = ~x),
    data.frame(
      x = 1:5,
      y = c(6, 1, 7, 4, 8)
    )
  )
  expect_identical(
    reverse_scale(test, exclude = ~ x + y),
    test
  )
})

test_that("reverse_scale: argument 'range' works", {
  expect_identical(
    reverse_scale(c(1, 3, 4), range = c(0, 4)),
    c(3, 1, 0)
  )
  expect_identical(
    reverse_scale(factor(c(1, 2, 3, 4, 5)), range = 0:10),
    factor(9:5, levels = 0:10)
  )

  test <- data.frame(
    x = 1:5,
    y = c(3, 8, 2, 5, 1)
  )
  expect_identical(
    reverse_scale(test, select = "x", range = c(0, 8)),
    data.frame(
      x = as.double(7:3),
      y = c(3, 8, 2, 5, 1)
    )
  )
  expect_identical(
    reverse_scale(test, range = c(0, 8)),
    data.frame(
      x = as.double(7:3),
      y = c(5, 0, 6, 3, 7)
    )
  )
})

test_that("reverse_scale ignores NA", {
  expect_identical(
    reverse_scale(c(1, 2, 8, NA)),
    c(8, 7, 1, NA)
  )
})

test_that("reverse_scale returns NA if only NA provided", {
  expect_identical(
    reverse_scale(c(NA_real_, NA_real_)),
    c(NA_real_, NA_real_)
  )
  expect_identical(
    reverse_scale(factor(c(NA, NA))),
    factor(c(NA, NA))
  )
})

test_that("reverse_scale warns if single value to reverse", {
  expect_warning(
    reverse_scale(1),
    regexp = "A `range` must be provided for data with only one unique value."
  )
  expect_warning(
    reverse_scale(factor(1)),
    regexp = "A `range` must be provided for data with only one unique value."
  )
})


test_that("reverse_scale select helpers", {
  data(iris)
  out <- rescale(
    iris,
    to = list(
      Sepal.Length = c(0, 1),
      Petal.Length = c(-1, 0)
    ),
    select = ends_with("length")
  )

  expect_identical(out$Sepal.Length, iris$Sepal.Length, tolerance = 1e-3)

  out <- rescale(
    iris,
    to = list(
      Sepal.Length = c(0, 1),
      Petal.Length = c(-1, 0)
    ),
    select = ends_with("length"),
    ignore_case = TRUE
  )

  expect_identical(
    head(out$Sepal.Length),
    c(0.22222, 0.16667, 0.11111, 0.08333, 0.19444, 0.30556),
    tolerance = 1e-3
  )
})


# with grouped data -------------------------------------------

set.seed(123)
value1 <- sample(1:10, 6, replace = TRUE)
set.seed(456)
value2 <- sample(1:10, 6, replace = TRUE)

test_df <- data.frame(
  id = rep(c("A", "B"), each = 3),
  value1 = value1,
  value2 = value2,
  stringsAsFactors = FALSE
)

test_that("reverse works with data frames (grouped data)", {
  skip_if_not_installed("poorman")

  expect_identical(
    test_df %>%
      poorman::group_by(id) %>%
      reverse(exclude = "id") %>%
      poorman::ungroup(),
    data.frame(
      id = rep(c("A", "B"), each = 3),
      value1 = c(10, 10, 3, 6, 2, 3),
      value2 = c(4, 6, 3, 10, 6, 5),
      stringsAsFactors = FALSE
    )
  )
})


test_that("reverse works with grouped data frames and append", {
  skip_if_not_installed("poorman")

  test <- data.frame(
    x = 1:6,
    y = c(3, 8, 2, 5, 1, 4),
    grp = rep(c("a", "b"), 3),
    stringsAsFactors = FALSE
  )
  expect_identical(
    test %>%
      poorman::group_by(grp) %>%
      reverse(append = TRUE) %>%
      poorman::ungroup(),
    data.frame(
      x = 1:6,
      y = c(3, 8, 2, 5, 1, 4),
      grp = rep(c("a", "b"), 3),
      x_r = as.double(c(5, 6, 3, 4, 1, 2)),
      y_r = as.double(c(1, 4, 2, 7, 3, 8)),
      stringsAsFactors = FALSE
    )
  )
})


set.seed(789)
value1 <- sample(c(1:10, NA), 6, replace = TRUE)
set.seed(10)
value2 <- sample(c(1:10, NA), 6, replace = TRUE)

test_df <- data.frame(
  id = rep(c("A", "B"), each = 3),
  value1 = value1,
  value2 = value2,
  stringsAsFactors = FALSE
)

test_that("reverse works with data frames containing NAs (grouped data)", {
  skip_if_not_installed("poorman")

  expect_identical(
    test_df %>%
      poorman::group_by(id) %>%
      reverse(exclude = "id") %>%
      poorman::ungroup(),
    data.frame(
      id = rep(c("A", "B"), each = 3),
      value1 = c(10, 4, 4, 5, 3, 4),
      value2 = c(NA, 10, 9, 7, 6, 8),
      stringsAsFactors = FALSE
    )
  )
})

# select helpers ------------------------------
test_that("reverse regex", {
  expect_identical(
    reverse(mtcars, select = "arb", regex = TRUE),
    reverse(mtcars, select = "carb")
  )
})


# work or give informative errors / warnings (#380) ------------------
test_that("reverse, larger range", {
  # works
  expect_identical(
    reverse(c(1, 3, 4), range = c(0, 4)),
    c(3, 1, 0)
  )
  expect_identical(
    reverse(factor(c(1, 3, 4)), range = 0:4),
    structure(
      c(4L, 2L, 1L),
      levels = c("0", "1", "2", "3", "4"),
      class = "factor"
    )
  )
  expect_identical(
    reverse(factor(c(1, 3, 4)), range = c(0, 4)),
    structure(
      c(4L, 2L, 1L),
      levels = c("0", "1", "2", "3", "4"),
      class = "factor"
    )
  )

  # errors on invalid input
  expect_error(reverse(c(1, 3, 4), range = 0:4))
  expect_error(reverse(factor(c(1, 3, 4, 5)), range = c(0, 2, 4)))
  # errors on invalid input (NA in range)
  expect_error(reverse(c(1, 3, 4), range = c(1, NA)), regex = "missing")
  expect_error(
    reverse(factor(letters[1:3]), range = c(1, NA)),
    regex = "missing"
  )

  # warns
  expect_warning(
    reverse(factor(c("a", "b", "c")), range = c(1, 3, 5, 7)),
    regex = "No current"
  )
  expect_warning(
    reverse(factor(c(9, 10, 11)), range = c(1, 3, 5, 7)),
    regex = "No current"
  )
  expect_warning(
    reverse(factor(c(1, 3, 11)), range = c(1, 3, 5, 7)),
    regex = "Not all"
  )

  # silent
  expect_silent(reverse(
    factor(c("a", "b", "c")),
    range = c(1, 3, 5, 7),
    verbose = FALSE
  ))
  expect_silent(reverse(
    factor(c(9, 10, 11)),
    range = c(1, 3, 5, 7),
    verbose = FALSE
  ))
  expect_silent(reverse(
    factor(c(1, 3, 11)),
    range = c(1, 3, 5, 7),
    verbose = FALSE
  ))

  # works as intended
  expect_identical(
    reverse(factor(c(1, 3, 11)), range = c(1, 3, 5, 7), verbose = FALSE),
    structure(c(4L, 3L, NA), levels = c("1", "3", "5", "7"), class = "factor")
  )
})
