library(testthat)
library(DataAudit)

#==========================================================
# Basic output
#==========================================================

test_that("range_check returns RangeCheck", {

  dat <- data.frame(x = 1:10)

  result <- range_check(
    dat,
    "x",
    min = 1,
    max = 10
  )

  expect_s3_class(
    result,
    "RangeCheck"
  )

})

test_that("range_check returns data.frame", {

  dat <- data.frame(x = 1:10)

  result <- range_check(
    dat,
    "x",
    min = 1,
    max = 10
  )

  expect_s3_class(
    result,
    "data.frame"
  )

})

test_that("range_check contains expected columns", {

  dat <- data.frame(x = 1:5)

  result <- range_check(
    dat,
    "x",
    min = 1,
    max = 5
  )

  expect_equal(
    names(result),
    c(
      "Row",
      "Value",
      "BelowMinimum",
      "AboveMaximum",
      "OutOfRange"
    )
  )

})


#==========================================================
# Valid values
#==========================================================

test_that("range_check identifies valid values", {

  dat <- data.frame(
    x = 1:5
  )

  result <- range_check(
    dat,
    "x",
    min = 1,
    max = 5
  )

  expect_false(
    any(result$OutOfRange)
  )

})


#==========================================================
# Lower violation
#==========================================================

test_that("range_check detects values below minimum", {

  dat <- data.frame(
    x = c(
      -1,
      1,
      2,
      3
    )
  )

  result <- range_check(
    dat,
    "x",
    min = 0,
    max = 5
  )

  expect_equal(
    result$BelowMinimum,
    c(
      TRUE,
      FALSE,
      FALSE,
      FALSE
    )
  )

  expect_equal(
    result$AboveMaximum,
    rep(FALSE, 4)
  )

  expect_equal(
    result$OutOfRange,
    c(
      TRUE,
      FALSE,
      FALSE,
      FALSE
    )
  )

})


#==========================================================
# Upper violation
#==========================================================

test_that("range_check detects values above maximum", {

  dat <- data.frame(
    x = c(
      1,
      2,
      10
    )
  )

  result <- range_check(
    dat,
    "x",
    min = 0,
    max = 5
  )

  expect_equal(
    result$AboveMaximum,
    c(
      FALSE,
      FALSE,
      TRUE
    )
  )

  expect_true(
    result$OutOfRange[3]
  )

})


#==========================================================
# Both limits
#==========================================================

test_that("range_check detects lower and upper violations", {

  dat <- data.frame(
    x = c(
      -5,
      5,
      15
    )
  )

  result <- range_check(
    dat,
    "x",
    min = 0,
    max = 10
  )

  expect_equal(
    result$OutOfRange,
    c(
      TRUE,
      FALSE,
      TRUE
    )
  )

})


#==========================================================
# Boundary values
#==========================================================

test_that("range_check treats limits as valid", {

  dat <- data.frame(
    x = c(
      0,
      5,
      10
    )
  )

  result <- range_check(
    dat,
    "x",
    min = 0,
    max = 10
  )

  expect_false(
    any(result$OutOfRange)
  )

})


#==========================================================
# Minimum only
#==========================================================

test_that("range_check works with minimum only", {

  dat <- data.frame(
    x = c(
      -1,
      0,
      10,
      100
    )
  )

  result <- range_check(
    dat,
    "x",
    min = 0
  )

  expect_equal(
    result$BelowMinimum,
    c(
      TRUE,
      FALSE,
      FALSE,
      FALSE
    )
  )

  expect_false(
    any(result$AboveMaximum)
  )

})


#==========================================================
# Maximum only
#==========================================================

test_that("range_check works with maximum only", {

  dat <- data.frame(
    x = c(
      -100,
      0,
      10,
      11
    )
  )

  result <- range_check(
    dat,
    "x",
    max = 10
  )

  expect_false(
    any(result$BelowMinimum)
  )

  expect_equal(
    result$AboveMaximum,
    c(
      FALSE,
      FALSE,
      FALSE,
      TRUE
    )
  )

})


#==========================================================
# Missing values
#==========================================================

test_that("range_check does not flag missing values", {

  dat <- data.frame(
    x = c(
      1,
      NA,
      20
    )
  )

  result <- range_check(
    dat,
    "x",
    min = 0,
    max = 10
  )

  expect_false(
    result$OutOfRange[2]
  )

  expect_true(
    is.na(result$Value[2])
  )

})


#==========================================================
# Infinite values
#==========================================================

test_that("range_check handles infinite values", {

  dat <- data.frame(
    x = c(
      -Inf,
      5,
      Inf
    )
  )

  result <- range_check(
    dat,
    "x",
    min = 0,
    max = 10
  )

  expect_equal(
    result$OutOfRange,
    c(
      TRUE,
      FALSE,
      TRUE
    )
  )

})


#==========================================================
# Row numbers
#==========================================================

test_that("range_check returns row numbers", {

  dat <- data.frame(
    x = c(10, 20, 30)
  )

  result <- range_check(
    dat,
    "x",
    min = 0
  )

  expect_equal(
    result$Row,
    1:3
  )

})


#==========================================================
# Zero-row data
#==========================================================

test_that("range_check handles zero-row data", {

  dat <- data.frame(
    x = numeric(0)
  )

  result <- range_check(
    dat,
    "x",
    min = 0,
    max = 10
  )

  expect_s3_class(
    result,
    "RangeCheck"
  )

  expect_equal(
    nrow(result),
    0L
  )

})


#==========================================================
# Tibble
#==========================================================

test_that("range_check handles tibble", {

  skip_if_not_installed("tibble")

  dat <- tibble::tibble(
    x = c(
      -1,
      5,
      20
    )
  )

  result <- range_check(
    dat,
    "x",
    min = 0,
    max = 10
  )

  expect_equal(
    result$OutOfRange,
    c(
      TRUE,
      FALSE,
      TRUE
    )
  )

})


#==========================================================
# Variable validation
#==========================================================

test_that("range_check rejects missing variable argument", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      min = 0
    ),
    "'variable' must be a single non-empty character string"
  )

})

test_that("range_check rejects NULL variable", {

  expect_error(
    range_check(
      iris,
      variable = NULL,
      min = 0
    ),
    "'variable' must be a single non-empty character string"
  )

})

test_that("range_check rejects numeric variable argument", {

  expect_error(
    range_check(
      iris,
      variable = 1,
      min = 0
    ),
    "'variable' must be a single non-empty character string"
  )

})

test_that("range_check rejects vector variable argument", {

  expect_error(
    range_check(
      iris,
      variable = c(
        "Sepal.Length",
        "Sepal.Width"
      ),
      min = 0
    ),
    "'variable' must be a single non-empty character string"
  )

})

test_that("range_check rejects NA variable", {

  expect_error(
    range_check(
      iris,
      variable = NA_character_,
      min = 0
    ),
    "'variable' must be a single non-empty character string"
  )

})

test_that("range_check rejects empty variable name", {

  expect_error(
    range_check(
      iris,
      variable = "",
      min = 0
    ),
    "'variable' must be a single non-empty character string"
  )

})

test_that("range_check rejects unknown variable", {

  expect_error(
    range_check(
      iris,
      variable = "Unknown",
      min = 0
    ),
    "Variable 'Unknown' was not found"
  )

})

test_that("range_check rejects non-numeric variable", {

  expect_error(
    range_check(
      iris,
      variable = "Species",
      min = 0
    ),
    "'variable' must refer to a numeric variable"
  )

})


#==========================================================
# Limit validation
#==========================================================

test_that("range_check requires at least one limit", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x"
    ),
    "At least one of 'min' or 'max' must be supplied"
  )

})

test_that("range_check rejects invalid minimum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      min = "0"
    ),
    "'min' must be NULL or a single finite numeric value"
  )

})

test_that("range_check rejects vector minimum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      min = c(0, 1)
    ),
    "'min' must be NULL or a single finite numeric value"
  )

})

test_that("range_check rejects NA minimum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      min = NA_real_
    ),
    "'min' must be NULL or a single finite numeric value"
  )

})

test_that("range_check rejects infinite minimum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      min = Inf
    ),
    "'min' must be NULL or a single finite numeric value"
  )

})

test_that("range_check rejects invalid maximum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      max = "10"
    ),
    "'max' must be NULL or a single finite numeric value"
  )

})

test_that("range_check rejects vector maximum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      max = c(5, 10)
    ),
    "'max' must be NULL or a single finite numeric value"
  )

})

test_that("range_check rejects NA maximum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      max = NA_real_
    ),
    "'max' must be NULL or a single finite numeric value"
  )

})

test_that("range_check rejects infinite maximum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      max = Inf
    ),
    "'max' must be NULL or a single finite numeric value"
  )

})

test_that("range_check rejects minimum above maximum", {

  dat <- data.frame(
    x = 1:5
  )

  expect_error(
    range_check(
      dat,
      "x",
      min = 10,
      max = 5
    ),
    "'min' cannot be greater than 'max'"
  )

})


#==========================================================
# Invalid data
#==========================================================

test_that("range_check rejects NULL data", {

  expect_error(
    range_check(
      NULL,
      "x",
      min = 0
    ),
    "'data' cannot be NULL"
  )

})

test_that("range_check rejects numeric vector", {

  expect_error(
    range_check(
      1:10,
      "x",
      min = 0
    ),
    "'data' must be a data.frame"
  )

})

test_that("range_check rejects list", {

  expect_error(
    range_check(
      list(x = 1:5),
      "x",
      min = 0
    ),
    "'data' must be a data.frame"
  )

})

test_that("range_check rejects empty data.frame", {

  expect_error(
    range_check(
      data.frame(),
      "x",
      min = 0
    ),
    "at least one variable"
  )

})
