library(testthat)
library(DataAudit)

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

test_that("date_check returns DateCheck", {

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

  result <- date_check(
    dat,
    "Date"
  )

  expect_s3_class(
    result,
    "DateCheck"
  )
})

test_that("date_check returns data.frame", {

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

  result <- date_check(
    dat,
    "Date"
  )

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

test_that("date_check returns expected columns", {

  dat <- data.frame(
    Date = as.Date("2025-01-01")
  )

  result <- date_check(
    dat,
    "Date"
  )

  expect_equal(
    names(result),
    c(
      "Row",
      "Value",
      "Missing",
      "BelowMin",
      "AboveMax",
      "Future",
      "ValidDate"
    )
  )
})


#==========================================================
# Valid dates
#==========================================================

test_that("date_check identifies valid dates", {

  dat <- data.frame(
    Date = as.Date(c(
      "2025-01-01",
      "2025-06-01",
      "2025-12-31"
    ))
  )

  result <- date_check(
    dat,
    "Date",
    min_date = "2025-01-01",
    max_date = "2025-12-31"
  )

  expect_true(
    all(result$ValidDate)
  )

  expect_false(
    any(result$BelowMin)
  )

  expect_false(
    any(result$AboveMax)
  )
})


#==========================================================
# Minimum date
#==========================================================

test_that("date_check detects dates below minimum", {

  dat <- data.frame(
    Date = as.Date(c(
      "2024-12-31",
      "2025-01-01",
      "2025-06-01"
    ))
  )

  result <- date_check(
    dat,
    "Date",
    min_date = "2025-01-01"
  )

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

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


#==========================================================
# Maximum date
#==========================================================

test_that("date_check detects dates above maximum", {

  dat <- data.frame(
    Date = as.Date(c(
      "2025-01-01",
      "2025-12-31",
      "2026-01-01"
    ))
  )

  result <- date_check(
    dat,
    "Date",
    max_date = "2025-12-31"
  )

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


#==========================================================
# Minimum and maximum
#==========================================================

test_that("date_check handles both date limits", {

  dat <- data.frame(
    Date = as.Date(c(
      "2024-12-31",
      "2025-06-01",
      "2026-01-01"
    ))
  )

  result <- date_check(
    dat,
    "Date",
    min_date = "2025-01-01",
    max_date = "2025-12-31"
  )

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

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

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


#==========================================================
# Boundary values
#==========================================================

test_that("date_check includes boundary dates", {

  dat <- data.frame(
    Date = as.Date(c(
      "2025-01-01",
      "2025-12-31"
    ))
  )

  result <- date_check(
    dat,
    "Date",
    min_date = "2025-01-01",
    max_date = "2025-12-31"
  )

  expect_true(
    all(result$ValidDate)
  )
})


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

test_that("date_check detects missing dates", {

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

  result <- date_check(
    dat,
    "Date"
  )

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

test_that("missing dates are not valid", {

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

  result <- date_check(
    dat,
    "Date"
  )

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

test_that("missing dates are not out of range", {

  dat <- data.frame(
    Date = as.Date(c(
      NA,
      "2025-06-01"
    ))
  )

  result <- date_check(
    dat,
    "Date",
    min_date = "2025-01-01",
    max_date = "2025-12-31"
  )

  expect_false(
    result$BelowMin[1]
  )

  expect_false(
    result$AboveMax[1]
  )
})


#==========================================================
# Future dates
#==========================================================

test_that("date_check allows future dates by default", {

  dat <- data.frame(
    Date = Sys.Date() + c(
      0,
      10
    )
  )

  result <- date_check(
    dat,
    "Date"
  )

  expect_false(
    any(result$Future)
  )

  expect_true(
    all(result$ValidDate)
  )
})

test_that("date_check detects future dates", {

  dat <- data.frame(
    Date = Sys.Date() + c(
      -1,
      0,
      10
    )
  )

  result <- date_check(
    dat,
    "Date",
    allow_future = FALSE
  )

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

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


#==========================================================
# Date limits
#==========================================================

test_that("date_check accepts Date limits", {

  dat <- data.frame(
    Date = as.Date("2025-01-01") + 0:2
  )

  result <- date_check(
    dat,
    "Date",
    min_date = as.Date("2025-01-01"),
    max_date = as.Date("2025-01-03")
  )

  expect_true(
    all(result$ValidDate)
  )
})

test_that("date_check accepts POSIXct limits", {

  dat <- data.frame(
    Date = as.Date("2025-01-01") + 0:2
  )

  result <- date_check(
    dat,
    "Date",
    min_date = as.POSIXct(
      "2025-01-01",
      tz = "UTC"
    ),
    max_date = as.POSIXct(
      "2025-01-03",
      tz = "UTC"
    )
  )

  expect_true(
    all(result$ValidDate)
  )
})

test_that("date_check accepts character limits", {

  dat <- data.frame(
    Date = as.Date("2025-01-01") + 0:2
  )

  expect_no_error(
    date_check(
      dat,
      "Date",
      min_date = "2025-01-01",
      max_date = "2025-01-03"
    )
  )
})


#==========================================================
# POSIXct variable
#==========================================================

test_that("date_check handles POSIXct", {

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

  result <- date_check(
    dat,
    "Date",
    min_date = "2025-01-01",
    max_date = "2025-01-02"
  )

  expect_s3_class(
    result,
    "DateCheck"
  )

  expect_s3_class(
    result$Value,
    "POSIXct"
  )

  expect_true(
    all(result$ValidDate)
  )
})


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

test_that("date_check handles all missing Date values", {

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

  result <- date_check(
    dat,
    "Date"
  )

  expect_true(
    all(result$Missing)
  )

  expect_false(
    any(result$ValidDate)
  )
})


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

test_that("date_check handles zero-row Date data", {

  dat <- data.frame(
    Date = as.Date(
      character(0)
    )
  )

  result <- date_check(
    dat,
    "Date"
  )

  expect_s3_class(
    result,
    "DateCheck"
  )

  expect_equal(
    nrow(result),
    0L
  )
})


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

test_that("date_check handles tibble", {

  skip_if_not_installed("tibble")

  dat <- tibble::tibble(
    Date = as.Date(
      c(
        "2025-01-01",
        "2025-06-01"
      )
    )
  )

  result <- date_check(
    dat,
    "Date"
  )

  expect_s3_class(
    result,
    "DateCheck"
  )
})


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

test_that("date_check rejects missing variable", {

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

test_that("date_check rejects NULL variable", {

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

test_that("date_check rejects numeric variable argument", {

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

test_that("date_check rejects multiple variable names", {

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

test_that("date_check rejects NA variable", {

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

test_that("date_check rejects empty variable", {

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

test_that("date_check rejects unknown variable", {

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


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

test_that("date_check rejects numeric variable", {

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

  expect_error(
    date_check(
      dat,
      "x"
    ),
    "'variable' must refer to a Date or POSIXct variable"
  )
})

test_that("date_check rejects character variable", {

  dat <- data.frame(
    x = c(
      "2025-01-01",
      "2025-01-02"
    )
  )

  expect_error(
    date_check(
      dat,
      "x"
    ),
    "'variable' must refer to a Date or POSIXct variable"
  )
})

test_that("date_check rejects factor variable", {

  dat <- data.frame(
    x = factor(
      c(
        "2025-01-01",
        "2025-01-02"
      )
    )
  )

  expect_error(
    date_check(
      dat,
      "x"
    ),
    "'variable' must refer to a Date or POSIXct variable"
  )
})


#==========================================================
# allow_future validation
#==========================================================

test_that("date_check rejects character allow_future", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      allow_future = "FALSE"
    ),
    "'allow_future' must be TRUE or FALSE"
  )
})

test_that("date_check rejects numeric allow_future", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      allow_future = 0
    ),
    "'allow_future' must be TRUE or FALSE"
  )
})

test_that("date_check rejects NA allow_future", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      allow_future = NA
    ),
    "'allow_future' must be TRUE or FALSE"
  )
})

test_that("date_check rejects vector allow_future", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      allow_future = c(
        TRUE,
        FALSE
      )
    ),
    "'allow_future' must be TRUE or FALSE"
  )
})


#==========================================================
# min_date validation
#==========================================================

test_that("date_check rejects invalid min_date", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      min_date = "not-a-date"
    ),
    "'min_date' could not be converted to a valid date"
  )
})

test_that("date_check rejects numeric min_date", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      min_date = 1
    ),
    "'min_date' must be a Date, POSIXct, character date, or NULL"
  )
})

test_that("date_check rejects multiple min_date values", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      min_date = c(
        "2025-01-01",
        "2025-02-01"
      )
    ),
    "'min_date' must contain exactly one non-missing date"
  )
})

test_that("date_check rejects NA min_date", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      min_date = NA_character_
    ),
    "'min_date' must contain exactly one non-missing date"
  )
})


#==========================================================
# max_date validation
#==========================================================

test_that("date_check rejects invalid max_date", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      max_date = "invalid"
    ),
    "'max_date' could not be converted to a valid date"
  )
})

test_that("date_check rejects numeric max_date", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      max_date = 1
    ),
    "'max_date' must be a Date, POSIXct, character date, or NULL"
  )
})

test_that("date_check rejects multiple max_date values", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      max_date = c(
        "2025-01-01",
        "2025-02-01"
      )
    ),
    "'max_date' must contain exactly one non-missing date"
  )
})


#==========================================================
# Limit relationship
#==========================================================

test_that("date_check rejects min_date after max_date", {

  dat <- data.frame(
    Date = Sys.Date()
  )

  expect_error(
    date_check(
      dat,
      "Date",
      min_date = "2025-12-31",
      max_date = "2025-01-01"
    ),
    "'min_date' cannot be later than 'max_date'"
  )
})


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

test_that("date_check rejects NULL data", {

  expect_error(
    date_check(
      NULL,
      "Date"
    ),
    "'data' cannot be NULL"
  )
})

test_that("date_check rejects vector data", {

  expect_error(
    date_check(
      1:10,
      "Date"
    ),
    "'data' must be a data.frame"
  )
})

test_that("date_check rejects list data", {

  expect_error(
    date_check(
      list(
        Date = Sys.Date()
      ),
      "Date"
    ),
    "'data' must be a data.frame"
  )
})

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

  expect_error(
    date_check(
      data.frame(),
      "Date"
    ),
    "at least one variable"
  )
})
