library(testthat)
library(DataAudit)

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

test_that("constant_check returns ConstantCheck object", {

  result <- constant_check(iris)

  expect_s3_class(
    result,
    "ConstantCheck"
  )

})

test_that("constant_check returns data.frame", {

  result <- constant_check(iris)

  expect_s3_class(
    result,
    "data.frame"
  )

})

test_that("constant_check contains expected columns", {

  result <- constant_check(iris)

  expect_equal(
    names(result),
    c(
      "Variable",
      "UniqueValues",
      "Missing",
      "Constant"
    )
  )

})

test_that("constant_check returns one row per variable", {

  result <- constant_check(iris)

  expect_equal(
    nrow(result),
    ncol(iris)
  )

})


#==========================================================
# Non-constant variables
#==========================================================

test_that("constant_check identifies non-constant variables", {

  dat <- data.frame(
    x = 1:5,
    y = letters[1:5]
  )

  result <- constant_check(dat)

  expect_false(
    any(result$Constant)
  )

})

test_that("constant_check counts unique values correctly", {

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

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    3L
  )

  expect_false(
    result$Constant
  )

})


#==========================================================
# Numeric constants
#==========================================================

test_that("constant_check detects numeric constant", {

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

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    1L
  )

  expect_true(
    result$Constant
  )

})


#==========================================================
# Character constants
#==========================================================

test_that("constant_check detects character constant", {

  dat <- data.frame(
    x = rep("Dog", 5),
    stringsAsFactors = FALSE
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    1L
  )

  expect_true(
    result$Constant
  )

})


#==========================================================
# Factor constants
#==========================================================

test_that("constant_check detects factor constant", {

  dat <- data.frame(
    x = factor(
      rep("A", 5)
    )
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    1L
  )

  expect_true(
    result$Constant
  )

})


#==========================================================
# Logical constants
#==========================================================

test_that("constant_check detects logical constant", {

  dat <- data.frame(
    x = rep(TRUE, 5)
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    1L
  )

  expect_true(
    result$Constant
  )

})


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

test_that("constant_check ignores missing values when counting unique values", {

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

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    1L
  )

  expect_equal(
    result$Missing,
    2L
  )

  expect_true(
    result$Constant
  )

})

test_that("constant_check does not mark varying data with NA as constant", {

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

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    2L
  )

  expect_equal(
    result$Missing,
    1L
  )

  expect_false(
    result$Constant
  )

})


#==========================================================
# All missing
#==========================================================

test_that("constant_check identifies all-NA variable as constant", {

  dat <- data.frame(
    x = rep(
      NA_real_,
      5
    )
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    0L
  )

  expect_equal(
    result$Missing,
    5L
  )

  expect_true(
    result$Constant
  )

})


#==========================================================
# Mixed variables
#==========================================================

test_that("constant_check handles mixed variable types", {

  dat <- data.frame(
    id = 1:5,
    species = rep("Dog", 5),
    farm = factor(
      rep("A", 5)
    ),
    positive = rep(TRUE, 5),
    stringsAsFactors = FALSE
  )

  result <- constant_check(dat)

  expect_equal(
    result$Variable,
    c(
      "id",
      "species",
      "farm",
      "positive"
    )
  )

  expect_equal(
    result$UniqueValues,
    c(
      5L,
      1L,
      1L,
      1L
    )
  )

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

})


#==========================================================
# Date variables
#==========================================================

test_that("constant_check detects constant Date variable", {

  dat <- data.frame(
    date = rep(
      as.Date("2026-01-01"),
      5
    )
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    1L
  )

  expect_true(
    result$Constant
  )

})

test_that("constant_check identifies varying Date variable", {

  dat <- data.frame(
    date = as.Date("2026-01-01") + 0:4
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    5L
  )

  expect_false(
    result$Constant
  )

})


#==========================================================
# POSIXct variables
#==========================================================

test_that("constant_check detects constant POSIXct variable", {

  dat <- data.frame(
    time = rep(
      as.POSIXct(
        "2026-01-01 10:00:00",
        tz = "UTC"
      ),
      5
    )
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    1L
  )

  expect_true(
    result$Constant
  )

})

test_that("constant_check identifies varying POSIXct variable", {

  dat <- data.frame(
    time = as.POSIXct(
      "2026-01-01 10:00:00",
      tz = "UTC"
    ) + 0:4
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    5L
  )

  expect_false(
    result$Constant
  )

})


#==========================================================
# Blank strings
#==========================================================

test_that("constant_check treats blank strings as values", {

  dat <- data.frame(
    x = c(
      "",
      "",
      "",
      ""
    ),
    stringsAsFactors = FALSE
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    1L
  )

  expect_equal(
    result$Missing,
    0L
  )

  expect_true(
    result$Constant
  )

})

test_that("constant_check distinguishes blank and non-blank values", {

  dat <- data.frame(
    x = c(
      "",
      "A",
      "",
      "A"
    ),
    stringsAsFactors = FALSE
  )

  result <- constant_check(dat)

  expect_equal(
    result$UniqueValues,
    2L
  )

  expect_false(
    result$Constant
  )

})


#==========================================================
# One-row data
#==========================================================

test_that("constant_check identifies variables in one-row data as constant", {

  dat <- data.frame(
    x = 1,
    y = "A",
    stringsAsFactors = FALSE
  )

  result <- constant_check(dat)

  expect_true(
    all(result$Constant)
  )

  expect_equal(
    result$UniqueValues,
    c(1L, 1L)
  )

})


#==========================================================
# One-column data
#==========================================================

test_that("constant_check works with one-column data", {

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

  result <- constant_check(dat)

  expect_equal(
    nrow(result),
    1L
  )

  expect_equal(
    result$Variable,
    "x"
  )

  expect_true(
    result$Constant
  )

})


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

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

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

  result <- constant_check(dat)

  expect_s3_class(
    result,
    "ConstantCheck"
  )

  expect_equal(
    nrow(result),
    2L
  )

  expect_equal(
    result$UniqueValues,
    c(0L, 0L)
  )

  expect_equal(
    result$Missing,
    c(0L, 0L)
  )

  expect_true(
    all(result$Constant)
  )

})


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

test_that("constant_check handles tibble", {

  skip_if_not_installed("tibble")

  dat <- tibble::tibble(
    x = c(1, 1, 1),
    y = c("A", "B", "C")
  )

  result <- constant_check(dat)

  expect_s3_class(
    result,
    "ConstantCheck"
  )

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

})


#==========================================================
# Variable names
#==========================================================

test_that("constant_check preserves variable names", {

  dat <- data.frame(
    Animal_ID = 1:3,
    Species = rep("Dog", 3)
  )

  result <- constant_check(dat)

  expect_equal(
    result$Variable,
    c(
      "Animal_ID",
      "Species"
    )
  )

})


#==========================================================
# Invalid input
#==========================================================

test_that("constant_check rejects NULL", {

  expect_error(
    constant_check(NULL),
    "'data' cannot be NULL"
  )

})

test_that("constant_check rejects numeric vector", {

  expect_error(
    constant_check(1:10),
    "'data' must be a data.frame"
  )

})

test_that("constant_check rejects character vector", {

  expect_error(
    constant_check(letters),
    "'data' must be a data.frame"
  )

})

test_that("constant_check rejects list", {

  expect_error(
    constant_check(
      list(
        x = 1:5,
        y = 6:10
      )
    ),
    "'data' must be a data.frame"
  )

})

test_that("constant_check rejects data.frame without variables", {

  expect_error(
    constant_check(
      data.frame()
    ),
    "at least one variable"
  )

})
