#==========================================================
# Sequence Check
#==========================================================

#' Check Numeric Sequence Integrity
#'
#' Checks a numeric or integer variable for missing values,
#' duplicated values, ordering problems, and gaps in an
#' expected sequence.
#'
#' @param data A data.frame.
#' @param variable A single character string specifying the
#'   numeric or integer variable to check.
#' @param step A positive numeric value specifying the
#'   expected increment between consecutive values.
#'   Default is 1.
#'
#' @return A data.frame of class \code{SequenceCheck}
#'   containing row-level sequence diagnostics.
#'
#' @details
#' The sequence is evaluated in the original row order.
#'
#' A duplicate is a non-missing value appearing more than
#' once in the variable. All occurrences of duplicated
#' values are flagged.
#'
#' For each non-missing observation after the first available
#' observation, the difference from the previous non-missing
#' value is calculated.
#'
#' An ordering problem occurs when this difference is less
#' than or equal to zero. Duplicate values are therefore also
#' ordering problems.
#'
#' A gap occurs when the difference from the previous
#' non-missing value is greater than \code{step}.
#'
#' Differences smaller than \code{step} but greater than zero
#' are classified as step mismatches.
#'
#' Missing observations are reported separately.
#'
#' @examples
#' dat <- data.frame(
#'   Visit = c(1, 2, 4, 4, 3, NA, 5)
#' )
#'
#' sequence_check(
#'   dat,
#'   variable = "Visit"
#' )
#'
#' sequence_check(
#'   data.frame(Time = c(0, 5, 10, 20)),
#'   variable = "Time",
#'   step = 5
#' )
#'
#' @export

sequence_check <- function(
    data,
    variable,
    step = 1
) {

  #---------------------------------------------
  # Validate data
  #---------------------------------------------
  validate_data(data)

  #---------------------------------------------
  # Validate variable
  #---------------------------------------------
  if (
    missing(variable) ||
    !is.character(variable) ||
    length(variable) != 1L ||
    is.na(variable) ||
    !nzchar(variable)
  ) {
    stop(
      "'variable' must be a single non-empty character string.",
      call. = FALSE
    )
  }

  #---------------------------------------------
  # Check variable exists
  #---------------------------------------------
  if (!variable %in% names(data)) {
    stop(
      sprintf(
        "Variable '%s' was not found in 'data'.",
        variable
      ),
      call. = FALSE
    )
  }

  #---------------------------------------------
  # Validate step
  #---------------------------------------------
  if (
    !is.numeric(step) ||
    length(step) != 1L ||
    is.na(step) ||
    !is.finite(step) ||
    step <= 0
  ) {
    stop(
      "'step' must be a single positive finite numeric value.",
      call. = FALSE
    )
  }

  #---------------------------------------------
  # Extract variable
  #---------------------------------------------
  x <- data[[variable]]

  #---------------------------------------------
  # Validate variable type
  #---------------------------------------------
  if (!is.numeric(x)) {
    stop(
      "'variable' must refer to a numeric or integer variable.",
      call. = FALSE
    )
  }

  #---------------------------------------------
  # Missing values
  #---------------------------------------------
  missing_value <- is.na(x)

  #---------------------------------------------
  # Duplicate values
  #---------------------------------------------
  duplicate <- rep(
    FALSE,
    length(x)
  )

  available <- which(!missing_value)

  if (length(available) > 0L) {

    duplicate[available] <-
      duplicated(x[available]) |
      duplicated(
        x[available],
        fromLast = TRUE
      )
  }

  #---------------------------------------------
  # Previous non-missing value
  #---------------------------------------------
  previous_value <- rep(
    NA_real_,
    length(x)
  )

  difference <- rep(
    NA_real_,
    length(x)
  )

  if (length(available) >= 2L) {

    current_index <-
      available[-1L]

    previous_index <-
      available[-length(available)]

    previous_value[current_index] <-
      x[previous_index]

    difference[current_index] <-
      x[current_index] -
      x[previous_index]
  }

  #---------------------------------------------
  # Ordering problems
  #---------------------------------------------
  order_issue <- rep(
    FALSE,
    length(x)
  )

  comparable <-
    !is.na(difference)

  order_issue[comparable] <-
    difference[comparable] <= 0

  #---------------------------------------------
  # Gaps
  #---------------------------------------------
  gap <- rep(
    FALSE,
    length(x)
  )

  gap[comparable] <-
    difference[comparable] > step

  #---------------------------------------------
  # Step mismatch
  #---------------------------------------------
  step_mismatch <- rep(
    FALSE,
    length(x)
  )

  step_mismatch[comparable] <-
    difference[comparable] > 0 &
    difference[comparable] != step

  #---------------------------------------------
  # Overall sequence issue
  #---------------------------------------------
  sequence_issue <-
    missing_value |
    duplicate |
    order_issue |
    step_mismatch

  #---------------------------------------------
  # Valid sequence
  #---------------------------------------------
  valid_sequence <-
    !sequence_issue

  #---------------------------------------------
  # Construct result
  #---------------------------------------------
  result <- data.frame(
    Row = seq_along(x),
    Value = x,
    Missing = missing_value,
    Duplicate = duplicate,
    PreviousValue = previous_value,
    Difference = difference,
    OrderIssue = order_issue,
    Gap = gap,
    StepMismatch = step_mismatch,
    SequenceIssue = sequence_issue,
    ValidSequence = valid_sequence,
    stringsAsFactors = FALSE,
    check.names = FALSE
  )

  #---------------------------------------------
  # Assign class
  #---------------------------------------------
  class(result) <- c(
    "SequenceCheck",
    "data.frame"
  )

  result
}
