library(testthat)
library(DataAudit)

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

test_that("duplicate_check returns DuplicateCheck object", {

  result <- duplicate_check(iris)

  expect_s3_class(
    result,
    "DuplicateCheck"
  )

})

test_that("duplicate_check returns data.frame", {

  result <- duplicate_check(iris)

  expect_s3_class(
    result,
    "data.frame"
  )

})

test_that("duplicate_check contains expected columns", {

  result <- duplicate_check(iris)

  expect_equal(
    names(result),
    c(
      "Row",
      "Duplicate"
    )
  )

})

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

  result <- duplicate_check(iris)

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

})


#==========================================================
# No duplicates
#==========================================================

test_that("duplicate_check reports no duplicates when absent", {

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

  result <- duplicate_check(dat)

  expect_false(
    any(result$Duplicate)
  )

})

test_that("duplicate_check reports FALSE for every unique row", {

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

  result <- duplicate_check(dat)

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

})


#==========================================================
# Duplicate rows
#==========================================================

test_that("duplicate_check detects one duplicate", {

  dat <- data.frame(
    x = c(1, 2, 2, 3),
    y = c("A", "B", "B", "C")
  )

  result <- duplicate_check(dat)

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

})

test_that("duplicate_check identifies correct duplicate row", {

  dat <- data.frame(
    x = c(1, 2, 2, 3),
    y = c("A", "B", "B", "C")
  )

  result <- duplicate_check(dat)

  expect_equal(
    result$Row[result$Duplicate],
    3L
  )

})

test_that("duplicate_check does not mark first occurrence", {

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

  result <- duplicate_check(dat)

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

})


#==========================================================
# Multiple duplicates
#==========================================================

test_that("duplicate_check detects multiple duplicate rows", {

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

  result <- duplicate_check(dat)

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

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

})


#==========================================================
# Completely duplicated data
#==========================================================

test_that("duplicate_check handles repeated identical rows", {

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

  result <- duplicate_check(dat)

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

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

})


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

test_that("duplicate_check handles missing values", {

  dat <- data.frame(
    x = c(
      1,
      NA,
      NA,
      2
    ),
    y = c(
      "A",
      "B",
      "B",
      "C"
    )
  )

  result <- duplicate_check(dat)

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

})


#==========================================================
# Character data
#==========================================================

test_that("duplicate_check handles character variables", {

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

  result <- duplicate_check(dat)

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

})


#==========================================================
# Factor data
#==========================================================

test_that("duplicate_check handles factor variables", {

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

  result <- duplicate_check(dat)

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

})


#==========================================================
# Logical data
#==========================================================

test_that("duplicate_check handles logical variables", {

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

  result <- duplicate_check(dat)

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

})


#==========================================================
# Mixed data
#==========================================================

test_that("duplicate_check handles mixed variable types", {

  dat <- data.frame(
    x = c(1, 2, 2),
    y = c("A", "B", "B"),
    z = factor(
      c("Low", "High", "High")
    ),
    flag = c(
      TRUE,
      FALSE,
      FALSE
    ),
    stringsAsFactors = FALSE
  )

  result <- duplicate_check(dat)

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

})


#==========================================================
# Date
#==========================================================

test_that("duplicate_check handles Date variables", {

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

  result <- duplicate_check(dat)

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

})


#==========================================================
# POSIXct
#==========================================================

test_that("duplicate_check handles POSIXct variables", {

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

  result <- duplicate_check(dat)

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

})


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

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

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

  result <- duplicate_check(dat)

  expect_equal(
    nrow(result),
    4L
  )

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

})


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

test_that("duplicate_check generates correct row numbers", {

  dat <- data.frame(
    x = 11:15
  )

  result <- duplicate_check(dat)

  expect_equal(
    result$Row,
    1:5
  )

})


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

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

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

  result <- duplicate_check(dat)

  expect_s3_class(
    result,
    "DuplicateCheck"
  )

  expect_equal(
    nrow(result),
    0L
  )

  expect_length(
    result$Row,
    0L
  )

  expect_length(
    result$Duplicate,
    0L
  )

})


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

test_that("duplicate_check handles tibble", {

  skip_if_not_installed("tibble")

  dat <- tibble::as_tibble(
    data.frame(
      x = c(1, 2, 2),
      y = c("A", "B", "B")
    )
  )

  result <- duplicate_check(dat)

  expect_s3_class(
    result,
    "DuplicateCheck"
  )

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

})


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

test_that("duplicate_check rejects NULL", {

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

})

test_that("duplicate_check rejects numeric vector", {

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

})

test_that("duplicate_check rejects character vector", {

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

})

test_that("duplicate_check rejects list", {

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

})

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

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

})
