library(testthat)
library(DataAudit)

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

test_that("blank_check returns BlankCheck object", {

  result <- blank_check(iris)

  expect_s3_class(
    result,
    "BlankCheck"
  )

})

test_that("blank_check returns data.frame", {

  result <- blank_check(iris)

  expect_s3_class(
    result,
    "data.frame"
  )

})

test_that("blank_check contains expected columns", {

  result <- blank_check(iris)

  expect_equal(
    names(result),
    c(
      "Variable",
      "TextVariable",
      "Missing",
      "Blank",
      "BlankPercent",
      "HasBlank"
    )
  )

})

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

  result <- blank_check(iris)

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

})


#==========================================================
# Character variables
#==========================================================

test_that("blank_check detects empty strings", {

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

  result <- blank_check(dat)

  expect_true(
    result$TextVariable
  )

  expect_equal(
    result$Blank,
    2L
  )

  expect_equal(
    result$BlankPercent,
    50
  )

  expect_true(
    result$HasBlank
  )

})

test_that("blank_check detects whitespace strings by default", {

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

  result <- blank_check(dat)

  expect_equal(
    result$Blank,
    2L
  )

  expect_true(
    result$HasBlank
  )

})

test_that("blank_check detects tab as blank when trimming", {

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

  result <- blank_check(dat)

  expect_equal(
    result$Blank,
    1L
  )

})


#==========================================================
# trim = FALSE
#==========================================================

test_that("blank_check with trim FALSE counts only empty strings", {

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

  result <- blank_check(
    dat,
    trim = FALSE
  )

  expect_equal(
    result$Blank,
    1L
  )

})

test_that("blank_check with trim FALSE does not classify whitespace as blank", {

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

  result <- blank_check(
    dat,
    trim = FALSE
  )

  expect_equal(
    result$Blank,
    0L
  )

  expect_false(
    result$HasBlank
  )

})


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

test_that("blank_check separates NA from blank values", {

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

  result <- blank_check(dat)

  expect_equal(
    result$Missing,
    1L
  )

  expect_equal(
    result$Blank,
    2L
  )

})

test_that("blank_check handles all missing character variable", {

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

  result <- blank_check(dat)

  expect_equal(
    result$Missing,
    5L
  )

  expect_equal(
    result$Blank,
    0L
  )

  expect_false(
    result$HasBlank
  )

})


#==========================================================
# Factor variables
#==========================================================

test_that("blank_check handles factors", {

  dat <- data.frame(
    x = factor(
      c(
        "A",
        "",
        "B",
        ""
      )
    )
  )

  result <- blank_check(dat)

  expect_true(
    result$TextVariable
  )

  expect_equal(
    result$Blank,
    2L
  )

  expect_true(
    result$HasBlank
  )

})

test_that("blank_check handles whitespace in factors", {

  dat <- data.frame(
    x = factor(
      c(
        "A",
        " ",
        "B",
        "   "
      )
    )
  )

  result <- blank_check(dat)

  expect_equal(
    result$Blank,
    2L
  )

})


#==========================================================
# Numeric variables
#==========================================================

test_that("blank_check ignores numeric variables", {

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

  result <- blank_check(dat)

  expect_false(
    result$TextVariable
  )

  expect_equal(
    result$Missing,
    1L
  )

  expect_equal(
    result$Blank,
    0L
  )

  expect_false(
    result$HasBlank
  )

})


#==========================================================
# Logical variables
#==========================================================

test_that("blank_check ignores logical variables", {

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

  result <- blank_check(dat)

  expect_false(
    result$TextVariable
  )

  expect_equal(
    result$Missing,
    1L
  )

  expect_equal(
    result$Blank,
    0L
  )

})


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

test_that("blank_check ignores Date variables", {

  dat <- data.frame(
    x = as.Date(
      c(
        "2026-01-01",
        NA,
        "2026-01-03"
      )
    )
  )

  result <- blank_check(dat)

  expect_false(
    result$TextVariable
  )

  expect_equal(
    result$Missing,
    1L
  )

  expect_equal(
    result$Blank,
    0L
  )

})


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

test_that("blank_check ignores POSIXct variables", {

  x <- as.POSIXct(
    c(
      "2026-01-01 10:00:00",
      "2026-01-02 10:00:00",
      "2026-01-03 10:00:00"
    ),
    tz = "UTC"
  )

  x[2] <- NA

  dat <- data.frame(
    x = x
  )

  result <- blank_check(dat)

  expect_false(
    result$TextVariable
  )

  expect_equal(
    result$Missing,
    1L
  )

  expect_equal(
    result$Blank,
    0L
  )

})


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

test_that("blank_check handles mixed variable types", {

  dat <- data.frame(
    numeric = c(1, 2, NA, 4),
    character = c("A", "", "B", " "),
    factor = factor(
      c("X", "", "Y", "Z")
    ),
    logical = c(
      TRUE,
      FALSE,
      TRUE,
      NA
    ),
    stringsAsFactors = FALSE
  )

  result <- blank_check(dat)

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

  expect_equal(
    result$Blank,
    c(
      0L,
      2L,
      1L,
      0L
    )
  )

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

})


#==========================================================
# Percentage calculation
#==========================================================

test_that("blank_check calculates percentage correctly", {

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

  result <- blank_check(dat)

  expect_equal(
    result$BlankPercent,
    50
  )

})

test_that("blank_check rounds percentage to two decimals", {

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

  result <- blank_check(dat)

  expect_equal(
    result$BlankPercent,
    33.33
  )

})


#==========================================================
# No blanks
#==========================================================

test_that("blank_check reports no blanks when absent", {

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

  result <- blank_check(dat)

  expect_equal(
    result$Blank,
    0L
  )

  expect_false(
    result$HasBlank
  )

})


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

test_that("blank_check handles one-column data", {

  dat <- data.frame(
    Species = c(
      "Dog",
      "",
      "Cat"
    ),
    stringsAsFactors = FALSE
  )

  result <- blank_check(dat)

  expect_equal(
    nrow(result),
    1L
  )

  expect_equal(
    result$Variable,
    "Species"
  )

  expect_equal(
    result$Blank,
    1L
  )

})


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

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

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

  result <- blank_check(dat)

  expect_s3_class(
    result,
    "BlankCheck"
  )

  expect_equal(
    nrow(result),
    2L
  )

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

  expect_true(
    all(is.na(result$BlankPercent))
  )

  expect_false(
    any(result$HasBlank)
  )

})


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

test_that("blank_check handles tibble", {

  skip_if_not_installed("tibble")

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

  result <- blank_check(dat)

  expect_s3_class(
    result,
    "BlankCheck"
  )

  expect_equal(
    result$Blank,
    c(
      1L,
      0L
    )
  )

})


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

test_that("blank_check preserves variable names", {

  dat <- data.frame(
    Animal_ID = 1:3,
    Species = c(
      "Dog",
      "",
      "Cat"
    ),
    stringsAsFactors = FALSE
  )

  result <- blank_check(dat)

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

})


#==========================================================
# trim validation
#==========================================================

test_that("blank_check rejects non-logical trim", {

  expect_error(
    blank_check(
      iris,
      trim = "TRUE"
    ),
    "'trim' must be TRUE or FALSE"
  )

})

test_that("blank_check rejects numeric trim", {

  expect_error(
    blank_check(
      iris,
      trim = 1
    ),
    "'trim' must be TRUE or FALSE"
  )

})

test_that("blank_check rejects vector trim", {

  expect_error(
    blank_check(
      iris,
      trim = c(TRUE, FALSE)
    ),
    "'trim' must be TRUE or FALSE"
  )

})

test_that("blank_check rejects NA trim", {

  expect_error(
    blank_check(
      iris,
      trim = NA
    ),
    "'trim' must be TRUE or FALSE"
  )

})


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

test_that("blank_check rejects NULL", {

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

})

test_that("blank_check rejects numeric vector", {

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

})

test_that("blank_check rejects character vector", {

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

})

test_that("blank_check rejects list", {

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

})

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

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

})
