library(testthat)
library(DataAudit)

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

test_that("sequence_check returns correct class", {

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

  result <- sequence_check(
    dat,
    "x"
  )

  expect_s3_class(
    result,
    "SequenceCheck"
  )

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

test_that("sequence_check returns expected columns", {

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

  result <- sequence_check(
    dat,
    "x"
  )

  expect_equal(
    names(result),
    c(
      "Row",
      "Value",
      "Missing",
      "Duplicate",
      "PreviousValue",
      "Difference",
      "OrderIssue",
      "Gap",
      "StepMismatch",
      "SequenceIssue",
      "ValidSequence"
    )
  )
})

#==========================================================
# Correct sequence
#==========================================================

test_that("correct sequence is valid", {

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

  result <- sequence_check(
    dat,
    "x"
  )

  expect_false(
    any(result$SequenceIssue)
  )

  expect_true(
    all(result$ValidSequence)
  )

  expect_false(
    any(result$Gap)
  )

  expect_false(
    any(result$OrderIssue)
  )
})

#==========================================================
# Previous values and differences
#==========================================================

test_that("previous values and differences are calculated", {

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

  result <- sequence_check(
    dat,
    "x"
  )

  expect_equal(
    result$PreviousValue,
    c(
      NA,
      1,
      2,
      3
    )
  )

  expect_equal(
    result$Difference,
    c(
      NA,
      1,
      1,
      1
    )
  )
})

#==========================================================
# Gaps
#==========================================================

test_that("sequence_check detects gaps", {

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

  result <- sequence_check(
    dat,
    "x"
  )

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

  expect_true(
    result$StepMismatch[3]
  )

  expect_true(
    result$SequenceIssue[3]
  )
})

test_that("large sequence gaps are detected", {

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

  result <- sequence_check(
    dat,
    "x"
  )

  expect_equal(
    result$Difference,
    c(
      NA,
      9,
      1
    )
  )

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

#==========================================================
# Duplicates
#==========================================================

test_that("sequence_check flags all duplicate occurrences", {

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

  result <- sequence_check(
    dat,
    "x"
  )

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

test_that("duplicate produces ordering problem", {

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

  result <- sequence_check(
    dat,
    "x"
  )

  expect_true(
    result$OrderIssue[3]
  )

  expect_equal(
    result$Difference[3],
    0
  )
})

#==========================================================
# Ordering
#==========================================================

test_that("sequence_check detects decreasing values", {

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

  result <- sequence_check(
    dat,
    "x"
  )

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

  expect_equal(
    result$Difference[3],
    -1
  )
})

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

test_that("sequence_check detects missing values", {

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

  result <- sequence_check(
    dat,
    "x"
  )

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

  expect_true(
    result$SequenceIssue[2]
  )

  expect_false(
    result$ValidSequence[2]
  )
})

test_that("sequence continues across missing observations", {

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

  result <- sequence_check(
    dat,
    "x"
  )

  expect_equal(
    result$PreviousValue,
    c(
      NA,
      NA,
      1,
      NA,
      2
    )
  )

  expect_equal(
    result$Difference,
    c(
      NA,
      NA,
      1,
      NA,
      1
    )
  )
})

test_that("all missing values are handled", {

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

  result <- sequence_check(
    dat,
    "x"
  )

  expect_true(
    all(result$Missing)
  )

  expect_false(
    any(result$Duplicate)
  )

  expect_true(
    all(result$SequenceIssue)
  )
})

#==========================================================
# Custom step
#==========================================================

test_that("sequence_check supports custom step", {

  dat <- data.frame(
    x = c(
      0,
      5,
      10,
      15
    )
  )

  result <- sequence_check(
    dat,
    "x",
    step = 5
  )

  expect_false(
    any(result$StepMismatch)
  )

  expect_false(
    any(result$Gap)
  )

  expect_true(
    all(result$ValidSequence)
  )
})

test_that("custom step detects gap", {

  dat <- data.frame(
    x = c(
      0,
      5,
      15,
      20
    )
  )

  result <- sequence_check(
    dat,
    "x",
    step = 5
  )

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

test_that("difference below expected step is mismatch but not gap", {

  dat <- data.frame(
    x = c(
      0,
      5,
      7
    )
  )

  result <- sequence_check(
    dat,
    "x",
    step = 5
  )

  expect_true(
    result$StepMismatch[3]
  )

  expect_false(
    result$Gap[3]
  )
})

#==========================================================
# Numeric sequences
#==========================================================

test_that("sequence_check supports numeric sequences", {

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

  result <- sequence_check(
    dat,
    "x",
    step = 0.5
  )

  expect_true(
    all(result$ValidSequence)
  )
})

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

test_that("sequence_check handles single observation", {

  dat <- data.frame(
    x = 1
  )

  result <- sequence_check(
    dat,
    "x"
  )

  expect_true(
    is.na(result$PreviousValue)
  )

  expect_true(
    is.na(result$Difference)
  )

  expect_true(
    result$ValidSequence
  )
})

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

test_that("sequence_check rejects invalid variable argument", {

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

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

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

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

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

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

test_that("sequence_check rejects unknown variable", {

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

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

test_that("sequence_check rejects character variable", {

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

  expect_error(
    sequence_check(
      dat,
      "x"
    ),
    "'variable' must refer to a numeric or integer variable"
  )
})

test_that("sequence_check rejects factor variable", {

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

  expect_error(
    sequence_check(
      dat,
      "x"
    ),
    "'variable' must refer to a numeric or integer variable"
  )
})

test_that("sequence_check rejects logical variable", {

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

  expect_error(
    sequence_check(
      dat,
      "x"
    ),
    "'variable' must refer to a numeric or integer variable"
  )
})

#==========================================================
# Step validation
#==========================================================

test_that("sequence_check rejects invalid step", {

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

  expect_error(
    sequence_check(
      dat,
      "x",
      step = 0
    ),
    "'step' must be a single positive finite numeric value"
  )

  expect_error(
    sequence_check(
      dat,
      "x",
      step = -1
    ),
    "'step' must be a single positive finite numeric value"
  )

  expect_error(
    sequence_check(
      dat,
      "x",
      step = NA_real_
    ),
    "'step' must be a single positive finite numeric value"
  )

  expect_error(
    sequence_check(
      dat,
      "x",
      step = Inf
    ),
    "'step' must be a single positive finite numeric value"
  )

  expect_error(
    sequence_check(
      dat,
      "x",
      step = "1"
    ),
    "'step' must be a single positive finite numeric value"
  )

  expect_error(
    sequence_check(
      dat,
      "x",
      step = c(1, 2)
    ),
    "'step' must be a single positive finite numeric value"
  )
})

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

test_that("sequence_check rejects invalid data", {

  expect_error(
    sequence_check(
      NULL,
      "x"
    )
  )

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

  expect_error(
    sequence_check(
      list(x = 1:5),
      "x"
    )
  )

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