library(testthat)
library(DataAudit)

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

test_that("length_check returns LengthCheck", {

  dat <- data.frame(
    ID = c("A01", "B02", "C03")
  )

  result <- length_check(
    dat,
    "ID"
  )

  expect_s3_class(result, "LengthCheck")
  expect_s3_class(result, "data.frame")
})

test_that("length_check returns expected columns", {

  dat <- data.frame(
    ID = c("A", "BB")
  )

  result <- length_check(
    dat,
    "ID"
  )

  expect_equal(
    names(result),
    c(
      "Row",
      "Value",
      "Length",
      "Missing",
      "BelowMin",
      "AboveMax",
      "ExactMismatch",
      "ValidLength"
    )
  )
})

#==========================================================
# Length calculation
#==========================================================

test_that("length_check calculates character lengths", {

  dat <- data.frame(
    ID = c("A", "AB", "ABC", "")
  )

  result <- length_check(dat, "ID")

  expect_equal(
    result$Length,
    c(1L, 2L, 3L, 0L)
  )
})

#==========================================================
# Exact length
#==========================================================

test_that("length_check detects exact length mismatch", {

  dat <- data.frame(
    ID = c("A01", "A002", "B12")
  )

  result <- length_check(
    dat,
    "ID",
    exact_length = 3
  )

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

  expect_equal(
    result$ValidLength,
    c(TRUE, FALSE, TRUE)
  )
})

test_that("exact length accepts boundary exactly", {

  dat <- data.frame(
    ID = "ABC"
  )

  result <- length_check(
    dat,
    "ID",
    exact_length = 3
  )

  expect_true(result$ValidLength)
})

#==========================================================
# Minimum length
#==========================================================

test_that("length_check detects values below minimum", {

  dat <- data.frame(
    ID = c("A", "AB", "ABC")
  )

  result <- length_check(
    dat,
    "ID",
    min_length = 2
  )

  expect_equal(
    result$BelowMin,
    c(TRUE, FALSE, FALSE)
  )
})

#==========================================================
# Maximum length
#==========================================================

test_that("length_check detects values above maximum", {

  dat <- data.frame(
    ID = c("A", "ABC", "ABCDE")
  )

  result <- length_check(
    dat,
    "ID",
    max_length = 3
  )

  expect_equal(
    result$AboveMax,
    c(FALSE, FALSE, TRUE)
  )
})

#==========================================================
# Range
#==========================================================

test_that("length_check handles minimum and maximum", {

  dat <- data.frame(
    ID = c("A", "ABC", "ABCDE")
  )

  result <- length_check(
    dat,
    "ID",
    min_length = 2,
    max_length = 4
  )

  expect_equal(
    result$ValidLength,
    c(FALSE, TRUE, FALSE)
  )
})

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

test_that("length_check handles missing values", {

  dat <- data.frame(
    ID = c("ABC", NA, "DEF")
  )

  result <- length_check(
    dat,
    "ID",
    exact_length = 3
  )

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

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

  expect_false(
    result$ValidLength[2]
  )

  expect_false(
    result$ExactMismatch[2]
  )
})

#==========================================================
# Factor
#==========================================================

test_that("length_check handles factors", {

  dat <- data.frame(
    ID = factor(
      c("A", "BB", "CCC")
    )
  )

  result <- length_check(
    dat,
    "ID"
  )

  expect_equal(
    result$Length,
    c(1L, 2L, 3L)
  )
})

#==========================================================
# Unicode
#==========================================================

test_that("length_check counts Unicode characters", {

  dat <- data.frame(
    ID = c("ABC", "café")
  )

  result <- length_check(
    dat,
    "ID"
  )

  expect_equal(
    result$Length,
    c(3L, 4L)
  )
})

#==========================================================
# Invalid variable
#==========================================================

test_that("length_check rejects missing variable", {

  expect_error(
    length_check(iris),
    "'variable' must be a single non-empty character string"
  )
})

test_that("length_check rejects invalid variable argument", {

  expect_error(
    length_check(iris, NULL),
    "'variable' must be a single non-empty character string"
  )

  expect_error(
    length_check(iris, ""),
    "'variable' must be a single non-empty character string"
  )

  expect_error(
    length_check(iris, 1),
    "'variable' must be a single non-empty character string"
  )

  expect_error(
    length_check(
      iris,
      c("Species", "Sepal.Length")
    ),
    "'variable' must be a single non-empty character string"
  )

  expect_error(
    length_check(iris, NA_character_),
    "'variable' must be a single non-empty character string"
  )
})

test_that("length_check rejects unknown variable", {

  expect_error(
    length_check(
      iris,
      "Unknown"
    ),
    "Variable 'Unknown' was not found in 'data'"
  )
})

#==========================================================
# Invalid variable type
#==========================================================

test_that("length_check rejects numeric variable", {

  expect_error(
    length_check(
      iris,
      "Sepal.Length"
    ),
    "'variable' must refer to a character or factor variable"
  )
})

test_that("length_check rejects logical variable", {

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

  expect_error(
    length_check(dat, "x"),
    "'variable' must refer to a character or factor variable"
  )
})

#==========================================================
# Invalid limits
#==========================================================

test_that("length_check rejects invalid min_length", {

  dat <- data.frame(x = "ABC")

  expect_error(
    length_check(dat, "x", min_length = -1),
    "'min_length' must be a single non-negative integer"
  )

  expect_error(
    length_check(dat, "x", min_length = 1.5),
    "'min_length' must be a single non-negative integer"
  )

  expect_error(
    length_check(dat, "x", min_length = NA_real_),
    "'min_length' must be a single non-negative integer"
  )

  expect_error(
    length_check(dat, "x", min_length = Inf),
    "'min_length' must be a single non-negative integer"
  )

  expect_error(
    length_check(dat, "x", min_length = "2"),
    "'min_length' must be a single non-negative integer"
  )

  expect_error(
    length_check(dat, "x", min_length = c(1, 2)),
    "'min_length' must be a single non-negative integer"
  )
})

test_that("length_check rejects invalid max_length", {

  dat <- data.frame(x = "ABC")

  expect_error(
    length_check(dat, "x", max_length = -1),
    "'max_length' must be a single non-negative integer"
  )

  expect_error(
    length_check(dat, "x", max_length = 2.5),
    "'max_length' must be a single non-negative integer"
  )
})

test_that("length_check rejects invalid exact_length", {

  dat <- data.frame(x = "ABC")

  expect_error(
    length_check(dat, "x", exact_length = -1),
    "'exact_length' must be a single non-negative integer"
  )

  expect_error(
    length_check(dat, "x", exact_length = NA_real_),
    "'exact_length' must be a single non-negative integer"
  )
})

#==========================================================
# Invalid combinations
#==========================================================

test_that("exact_length cannot be combined with min_length", {

  dat <- data.frame(x = "ABC")

  expect_error(
    length_check(
      dat,
      "x",
      min_length = 2,
      exact_length = 3
    ),
    "'exact_length' cannot be combined"
  )
})

test_that("exact_length cannot be combined with max_length", {

  dat <- data.frame(x = "ABC")

  expect_error(
    length_check(
      dat,
      "x",
      max_length = 4,
      exact_length = 3
    ),
    "'exact_length' cannot be combined"
  )
})

test_that("minimum cannot exceed maximum", {

  dat <- data.frame(x = "ABC")

  expect_error(
    length_check(
      dat,
      "x",
      min_length = 5,
      max_length = 2
    ),
    "'min_length' cannot be greater than 'max_length'"
  )
})

#==========================================================
# Zero length
#==========================================================

test_that("zero is a valid length limit", {

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

  result <- length_check(
    dat,
    "x",
    exact_length = 0
  )

  expect_equal(
    result$ValidLength,
    c(TRUE, FALSE)
  )
})

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

test_that("length_check rejects invalid data", {

  expect_error(
    length_check(NULL, "x")
  )

  expect_error(
    length_check(1:5, "x")
  )

  expect_error(
    length_check(list(x = "A"), "x")
  )

  expect_error(
    length_check(data.frame(), "x")
  )
})
