library(testthat)
library(DataAudit)

test_that("group_sequence_check returns correct class", {

  dat <- data.frame(
    ID = c("A", "A", "B", "B"),
    Visit = c(1, 2, 1, 2)
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

  expect_s3_class(
    result,
    "GroupSequenceCheck"
  )

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

test_that("group_sequence_check returns expected columns", {

  dat <- data.frame(
    ID = c("A", "A"),
    Visit = c(1, 2)
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

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

test_that("valid sequences within groups pass", {

  dat <- data.frame(
    ID = c(
      "A", "A", "A",
      "B", "B", "B"
    ),
    Visit = c(
      1, 2, 3,
      1, 2, 3
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

  expect_true(
    all(result$ValidSequence)
  )

  expect_false(
    any(result$SequenceIssue)
  )
})

test_that("sequence restarts within each group", {

  dat <- data.frame(
    ID = c(
      "A", "A",
      "B", "B"
    ),
    Visit = c(
      1, 2,
      1, 2
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

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

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

test_that("gaps are detected within groups", {

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

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

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

  expect_true(
    result$StepMismatch[3]
  )
})

test_that("duplicates are evaluated within groups only", {

  dat <- data.frame(
    ID = c(
      "A", "A",
      "B", "B"
    ),
    Visit = c(
      1, 1,
      1, 2
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

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

test_that("same sequence value across groups is not duplicate", {

  dat <- data.frame(
    ID = c(
      "A",
      "B"
    ),
    Visit = c(
      1,
      1
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

  expect_false(
    any(result$Duplicate)
  )
})

test_that("decreasing sequence is detected within group", {

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

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

  expect_true(
    result$OrderIssue[3]
  )

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

test_that("missing sequence values are detected", {

  dat <- data.frame(
    ID = c(
      "A", "A", "A"
    ),
    Visit = c(
      1, NA, 2
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

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

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

  expect_true(
    result$SequenceIssue[2]
  )
})

test_that("missing groups are flagged", {

  dat <- data.frame(
    ID = c(
      "A",
      NA,
      NA
    ),
    Visit = c(
      1,
      1,
      2
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

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

  expect_true(
    result$SequenceIssue[2]
  )

  expect_true(
    result$SequenceIssue[3]
  )
})

test_that("missing group rows can still be diagnosed as a group", {

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

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

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

  expect_true(
    result$Gap[3]
  )
})

test_that("custom step works within groups", {

  dat <- data.frame(
    ID = c(
      "A", "A", "A",
      "B", "B", "B"
    ),
    Time = c(
      0, 5, 10,
      0, 5, 10
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Time",
    step = 5
  )

  expect_true(
    all(result$ValidSequence)
  )
})

test_that("custom step detects group-specific gap", {

  dat <- data.frame(
    ID = c(
      "A", "A", "A"
    ),
    Time = c(
      0, 5, 15
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Time",
    step = 5
  )

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

test_that("single observation groups are handled", {

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

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

  expect_true(
    all(is.na(result$PreviousValue))
  )

  expect_true(
    all(is.na(result$Difference))
  )

  expect_true(
    all(result$ValidSequence)
  )
})

test_that("all missing sequence values are handled", {

  dat <- data.frame(
    ID = c(
      "A",
      "A"
    ),
    Visit = c(
      NA_real_,
      NA_real_
    )
  )

  result <- group_sequence_check(
    dat,
    "ID",
    "Visit"
  )

  expect_true(
    all(result$Missing)
  )

  expect_false(
    any(result$Duplicate)
  )

  expect_true(
    all(result$SequenceIssue)
  )
})

test_that("group is required", {

  dat <- data.frame(
    ID = "A",
    Visit = 1
  )

  expect_error(
    group_sequence_check(
      dat,
      variable = "Visit"
    ),
    "'group' must be supplied"
  )
})

test_that("variable is required", {

  dat <- data.frame(
    ID = "A",
    Visit = 1
  )

  expect_error(
    group_sequence_check(
      dat,
      group = "ID"
    ),
    "'variable' must be supplied"
  )
})

test_that("group argument is validated", {

  dat <- data.frame(
    ID = "A",
    Visit = 1
  )

  expect_error(
    group_sequence_check(
      dat,
      NULL,
      "Visit"
    ),
    "'group' must be a single non-empty character string"
  )

  expect_error(
    group_sequence_check(
      dat,
      "",
      "Visit"
    ),
    "'group' must be a single non-empty character string"
  )

  expect_error(
    group_sequence_check(
      dat,
      1,
      "Visit"
    ),
    "'group' must be a single non-empty character string"
  )

  expect_error(
    group_sequence_check(
      dat,
      NA_character_,
      "Visit"
    ),
    "'group' must be a single non-empty character string"
  )

  expect_error(
    group_sequence_check(
      dat,
      c("ID", "Visit"),
      "Visit"
    ),
    "'group' must be a single non-empty character string"
  )
})

test_that("variable argument is validated", {

  dat <- data.frame(
    ID = "A",
    Visit = 1
  )

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

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

  expect_error(
    group_sequence_check(
      dat,
      "ID",
      1
    ),
    "'variable' must be a single non-empty character string"
  )
})

test_that("unknown variables are rejected", {

  dat <- data.frame(
    ID = "A",
    Visit = 1
  )

  expect_error(
    group_sequence_check(
      dat,
      "Unknown",
      "Visit"
    ),
    "Variable 'Unknown' was not found in 'data'"
  )

  expect_error(
    group_sequence_check(
      dat,
      "ID",
      "Unknown"
    ),
    "Variable 'Unknown' was not found in 'data'"
  )
})

test_that("group and variable must differ", {

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

  expect_error(
    group_sequence_check(
      dat,
      "ID",
      "ID"
    ),
    "must refer to different variables"
  )
})

test_that("sequence variable must be numeric", {

  dat <- data.frame(
    ID = c("A", "A"),
    Visit = c("1", "2")
  )

  expect_error(
    group_sequence_check(
      dat,
      "ID",
      "Visit"
    ),
    "'variable' must refer to a numeric or integer variable"
  )
})

test_that("factor sequence variable is rejected", {

  dat <- data.frame(
    ID = c("A", "A"),
    Visit = factor(
      c("1", "2")
    )
  )

  expect_error(
    group_sequence_check(
      dat,
      "ID",
      "Visit"
    ),
    "'variable' must refer to a numeric or integer variable"
  )
})

test_that("invalid step is rejected", {

  dat <- data.frame(
    ID = c("A", "A"),
    Visit = c(1, 2)
  )

  invalid_steps <- list(
    0,
    -1,
    NA_real_,
    Inf,
    -Inf,
    "1",
    c(1, 2)
  )

  for (s in invalid_steps) {

    expect_error(
      group_sequence_check(
        dat,
        "ID",
        "Visit",
        step = s
      ),
      "'step' must be a single positive finite numeric value"
    )
  }
})

test_that("invalid data is rejected", {

  expect_error(
    group_sequence_check(
      NULL,
      "ID",
      "Visit"
    )
  )

  expect_error(
    group_sequence_check(
      1:5,
      "ID",
      "Visit"
    )
  )

  expect_error(
    group_sequence_check(
      list(
        ID = "A",
        Visit = 1
      ),
      "ID",
      "Visit"
    )
  )

  expect_error(
    group_sequence_check(
      data.frame(),
      "ID",
      "Visit"
    )
  )
})
