library(testthat)
library(DataAudit)

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

test_that("consistency_check returns correct class", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "<="
  )

  expect_s3_class(
    result,
    "ConsistencyCheck"
  )

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

test_that("consistency_check returns expected columns", {

  dat <- data.frame(
    x = 1:3,
    y = 3:1
  )

  result <- consistency_check(
    dat,
    "x",
    "y",
    "<"
  )

  expect_equal(
    names(result),
    c(
      "Row",
      "Value1",
      "Value2",
      "Missing",
      "Consistent",
      "Inconsistent"
    )
  )
})

#==========================================================
# Less than
#==========================================================

test_that("consistency_check evaluates less than", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "<"
  )

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

#==========================================================
# Less than or equal
#==========================================================

test_that("consistency_check evaluates less than or equal", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "<="
  )

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

#==========================================================
# Greater than
#==========================================================

test_that("consistency_check evaluates greater than", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    ">"
  )

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

#==========================================================
# Greater than or equal
#==========================================================

test_that("consistency_check evaluates greater than or equal", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    ">="
  )

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

#==========================================================
# Equal
#==========================================================

test_that("consistency_check evaluates equality", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "=="
  )

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

#==========================================================
# Not equal
#==========================================================

test_that("consistency_check evaluates inequality", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "!="
  )

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

#==========================================================
# Inconsistent
#==========================================================

test_that("inconsistent is inverse for non-missing rows", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "<="
  )

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

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

test_that("consistency_check handles missing values", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "<"
  )

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

  expect_false(
    result$Consistent[2]
  )

  expect_false(
    result$Consistent[3]
  )

  expect_false(
    result$Inconsistent[2]
  )

  expect_false(
    result$Inconsistent[3]
  )
})

test_that("consistency_check handles all missing comparisons", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "<="
  )

  expect_true(
    all(result$Missing)
  )

  expect_false(
    any(result$Consistent)
  )

  expect_false(
    any(result$Inconsistent)
  )
})

#==========================================================
# Date comparisons
#==========================================================

test_that("consistency_check compares Date variables", {

  dat <- data.frame(
    Start = as.Date(
      c(
        "2026-01-01",
        "2026-02-10",
        "2026-03-01"
      )
    ),
    End = as.Date(
      c(
        "2026-01-05",
        "2026-02-01",
        "2026-03-01"
      )
    )
  )

  result <- consistency_check(
    dat,
    "End",
    "Start",
    ">="
  )

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

#==========================================================
# Character comparisons
#==========================================================

test_that("consistency_check evaluates character equality", {

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

  result <- consistency_check(
    dat,
    "x",
    "y",
    "=="
  )

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

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

test_that("consistency_check requires variable1", {

  dat <- data.frame(
    x = 1:3,
    y = 1:3
  )

  expect_error(
    consistency_check(
      dat,
      variable2 = "y",
      relationship = "=="
    ),
    "'variable1' must be supplied"
  )
})

test_that("consistency_check requires variable2", {

  dat <- data.frame(
    x = 1:3,
    y = 1:3
  )

  expect_error(
    consistency_check(
      dat,
      variable1 = "x",
      relationship = "=="
    ),
    "'variable2' must be supplied"
  )
})

test_that("consistency_check validates variable names", {

  dat <- data.frame(
    x = 1:3,
    y = 1:3
  )

  expect_error(
    consistency_check(
      dat,
      NULL,
      "y",
      "=="
    ),
    "'variable1' must be a single non-empty character string"
  )

  expect_error(
    consistency_check(
      dat,
      "",
      "y",
      "=="
    ),
    "'variable1' must be a single non-empty character string"
  )

  expect_error(
    consistency_check(
      dat,
      1,
      "y",
      "=="
    ),
    "'variable1' must be a single non-empty character string"
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      NA_character_,
      "=="
    ),
    "'variable2' must be a single non-empty character string"
  )

  expect_error(
    consistency_check(
      dat,
      c("x", "y"),
      "y",
      "=="
    ),
    "'variable1' must be a single non-empty character string"
  )
})

test_that("consistency_check rejects unknown variables", {

  dat <- data.frame(
    x = 1:3,
    y = 1:3
  )

  expect_error(
    consistency_check(
      dat,
      "unknown",
      "y",
      "=="
    ),
    "Variable 'unknown' was not found in 'data'"
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "unknown",
      "=="
    ),
    "Variable 'unknown' was not found in 'data'"
  )
})

test_that("consistency_check rejects same variable", {

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

  expect_error(
    consistency_check(
      dat,
      "x",
      "x",
      "=="
    ),
    "'variable1' and 'variable2' must refer to different variables"
  )
})

#==========================================================
# Relationship validation
#==========================================================

test_that("consistency_check requires relationship", {

  dat <- data.frame(
    x = 1:3,
    y = 1:3
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "y"
    ),
    "'relationship' must be one of"
  )
})

test_that("consistency_check rejects unsupported relationship", {

  dat <- data.frame(
    x = 1:3,
    y = 1:3
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "y",
      "="
    ),
    "'relationship' must be one of"
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "y",
      "approximately"
    ),
    "'relationship' must be one of"
  )
})

test_that("consistency_check rejects malformed relationship", {

  dat <- data.frame(
    x = 1:3,
    y = 1:3
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "y",
      NULL
    ),
    "'relationship' must be one of"
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "y",
      NA_character_
    ),
    "'relationship' must be one of"
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "y",
      c("<", ">")
    ),
    "'relationship' must be one of"
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "y",
      1
    ),
    "'relationship' must be one of"
  )
})

#==========================================================
# Incompatible variables
#==========================================================

test_that("consistency_check rejects incompatible comparison", {

  dat <- data.frame(
    x = as.Date(
      c(
        "2026-01-01",
        "2026-01-02"
      )
    ),
    y = c(
      "Dog",
      "Cat"
    )
  )

  expect_error(
    consistency_check(
      dat,
      "x",
      "y",
      "<"
    ),
    "cannot be compared"
  )
})

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

test_that("consistency_check rejects invalid data", {

  expect_error(
    consistency_check(
      NULL,
      "x",
      "y",
      "=="
    )
  )

  expect_error(
    consistency_check(
      1:5,
      "x",
      "y",
      "=="
    )
  )

  expect_error(
    consistency_check(
      list(x = 1, y = 1),
      "x",
      "y",
      "=="
    )
  )

  expect_error(
    consistency_check(
      data.frame(),
      "x",
      "y",
      "=="
    )
  )
})
