library(testthat)
library(DataAudit)

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

test_that("unique_check returns UniqueCheck", {

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

  result <- unique_check(
    dat,
    "ID"
  )

  expect_s3_class(
    result,
    "UniqueCheck"
  )

})

test_that("unique_check returns data.frame", {

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

  result <- unique_check(
    dat,
    "ID"
  )

  expect_s3_class(
    result,
    "data.frame"
  )

})

test_that("unique_check contains expected columns", {

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

  result <- unique_check(
    dat,
    "ID"
  )

  expect_equal(
    names(result),
    c(
      "Row",
      "Value",
      "Missing",
      "Duplicate",
      "ValidUnique"
    )
  )

})

test_that("unique_check returns one row per observation", {

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

  result <- unique_check(
    dat,
    "ID"
  )

  expect_equal(
    nrow(result),
    10L
  )

})


#==========================================================
# Unique identifiers
#==========================================================

test_that("unique_check identifies unique values", {

  dat <- data.frame(
    ID = 101:105
  )

  result <- unique_check(
    dat,
    "ID"
  )

  expect_false(
    any(result$Missing)
  )

  expect_false(
    any(result$Duplicate)
  )

  expect_true(
    all(result$ValidUnique)
  )

})


#==========================================================
# Duplicate identifiers
#==========================================================

test_that("unique_check detects duplicated values", {

  dat <- data.frame(
    ID = c(
      101,
      102,
      102,
      103
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

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

})

test_that("unique_check flags all occurrences of duplicate value", {

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

  result <- unique_check(
    dat,
    "ID"
  )

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

})

test_that("unique_check handles multiple duplicate groups", {

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

  result <- unique_check(
    dat,
    "ID"
  )

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

})


#==========================================================
# Missing identifiers
#==========================================================

test_that("unique_check detects missing values", {

  dat <- data.frame(
    ID = c(
      101,
      NA,
      103
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

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

})

test_that("missing identifiers are not classified as duplicates", {

  dat <- data.frame(
    ID = c(
      NA,
      NA,
      101
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

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

  expect_false(
    any(result$Duplicate)
  )

})

test_that("missing identifiers are not valid unique identifiers", {

  dat <- data.frame(
    ID = c(
      101,
      NA,
      102
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

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

})


#==========================================================
# Combined missing and duplicate
#==========================================================

test_that("unique_check handles missing and duplicated IDs", {

  dat <- data.frame(
    ID = c(
      101,
      102,
      102,
      NA,
      103
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

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

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

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

})


#==========================================================
# Character identifiers
#==========================================================

test_that("unique_check handles character IDs", {

  dat <- data.frame(
    ID = c(
      "A001",
      "A002",
      "A002",
      "A003"
    ),
    stringsAsFactors = FALSE
  )

  result <- unique_check(
    dat,
    "ID"
  )

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

})


#==========================================================
# Factor identifiers
#==========================================================

test_that("unique_check handles factor IDs", {

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

  result <- unique_check(
    dat,
    "ID"
  )

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

  expect_true(
    is.factor(result$Value)
  )

})


#==========================================================
# Logical identifiers
#==========================================================

test_that("unique_check handles logical values", {

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

  result <- unique_check(
    dat,
    "ID"
  )

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

})


#==========================================================
# Date identifiers
#==========================================================

test_that("unique_check handles Date values", {

  dat <- data.frame(
    ID = as.Date(
      c(
        "2026-01-01",
        "2026-01-02",
        "2026-01-02"
      )
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

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

  expect_s3_class(
    result$Value,
    "Date"
  )

})


#==========================================================
# POSIXct identifiers
#==========================================================

test_that("unique_check handles POSIXct values", {

  dat <- data.frame(
    ID = as.POSIXct(
      c(
        "2026-01-01 10:00:00",
        "2026-01-01 11:00:00",
        "2026-01-01 11:00:00"
      ),
      tz = "UTC"
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

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

  expect_s3_class(
    result$Value,
    "POSIXct"
  )

})


#==========================================================
# Single observation
#==========================================================

test_that("unique_check handles one observation", {

  dat <- data.frame(
    ID = 101
  )

  result <- unique_check(
    dat,
    "ID"
  )

  expect_false(
    result$Missing
  )

  expect_false(
    result$Duplicate
  )

  expect_true(
    result$ValidUnique
  )

})


#==========================================================
# All duplicated
#==========================================================

test_that("unique_check handles all duplicated values", {

  dat <- data.frame(
    ID = rep(
      101,
      5
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

  expect_true(
    all(result$Duplicate)
  )

  expect_false(
    any(result$ValidUnique)
  )

})


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

test_that("unique_check handles all missing values", {

  dat <- data.frame(
    ID = rep(
      NA_integer_,
      5
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

  expect_true(
    all(result$Missing)
  )

  expect_false(
    any(result$Duplicate)
  )

  expect_false(
    any(result$ValidUnique)
  )

})


#==========================================================
# Zero rows
#==========================================================

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

  dat <- data.frame(
    ID = integer(0)
  )

  result <- unique_check(
    dat,
    "ID"
  )

  expect_s3_class(
    result,
    "UniqueCheck"
  )

  expect_equal(
    nrow(result),
    0L
  )

  expect_equal(
    length(result$Duplicate),
    0L
  )

})


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

test_that("unique_check handles tibble", {

  skip_if_not_installed("tibble")

  dat <- tibble::tibble(
    ID = c(
      1,
      2,
      2,
      3
    )
  )

  result <- unique_check(
    dat,
    "ID"
  )

  expect_s3_class(
    result,
    "UniqueCheck"
  )

  expect_equal(
    sum(result$Duplicate),
    2L
  )

})


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

test_that("unique_check rejects missing variable", {

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

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

})

test_that("unique_check rejects NULL variable", {

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

})

test_that("unique_check rejects numeric variable argument", {

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

})

test_that("unique_check rejects multiple variable names", {

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

})

test_that("unique_check rejects NA variable name", {

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

})

test_that("unique_check rejects empty variable name", {

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

})

test_that("unique_check rejects unknown variable", {

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

})


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

test_that("unique_check rejects NULL data", {

  expect_error(
    unique_check(
      NULL,
      "ID"
    ),
    "'data' cannot be NULL"
  )

})

test_that("unique_check rejects numeric vector", {

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

})

test_that("unique_check rejects character vector", {

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

})

test_that("unique_check rejects list", {

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

})

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

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

})
