#==========================================================
# Case Consistency Check
#==========================================================

#' Check Case Consistency
#'
#' Detects values in a character or factor variable that
#' differ only in letter case.
#'
#' @param data A data.frame.
#' @param variable A single character string specifying the
#'   variable to check.
#'
#' @return A data.frame of class \code{CaseCheck} containing
#'   row-level case-consistency diagnostics.
#'
#' @details
#' Values are compared after conversion to lower case.
#' Therefore, values such as \code{"Dog"}, \code{"dog"},
#' and \code{"DOG"} are treated as belonging to the same
#' normalized category.
#'
#' A value is flagged when more than one distinct original
#' representation exists for the same normalized value.
#'
#' Missing values are reported separately and are not
#' classified as case inconsistencies.
#'
#' The function does not modify the original data.
#'
#' @examples
#' dat <- data.frame(
#'   Species = c("Dog", "dog", "DOG", "Cat", "Cat", NA)
#' )
#'
#' case_check(
#'   dat,
#'   variable = "Species"
#' )
#'
#' @export

case_check <- function(data, variable) {

  #---------------------------------------------
  # 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
    )
  }

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

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

  #---------------------------------------------
  # Convert factor to character
  #---------------------------------------------
  value <- as.character(x)

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

  #---------------------------------------------
  # Normalize case
  #---------------------------------------------
  normalized <- rep(
    NA_character_,
    length(value)
  )

  normalized[!missing_value] <-
    tolower(value[!missing_value])

  #---------------------------------------------
  # Determine inconsistent normalized groups
  #---------------------------------------------
  case_issue <- rep(
    FALSE,
    length(value)
  )

  valid_index <- which(!missing_value)

  if (length(valid_index) > 0L) {

    normalized_groups <- split(
      value[valid_index],
      normalized[valid_index]
    )

    inconsistent_groups <- names(
      normalized_groups
    )[
      vapply(
        normalized_groups,
        function(z) {
          length(unique(z)) > 1L
        },
        logical(1)
      )
    ]

    case_issue[valid_index] <-
      normalized[valid_index] %in%
      inconsistent_groups
  }

  #---------------------------------------------
  # Overall validity
  #---------------------------------------------
  valid_case <-
    !missing_value &
    !case_issue

  #---------------------------------------------
  # Construct result
  #---------------------------------------------
  result <- data.frame(
    Row = seq_along(x),
    Value = value,
    Normalized = normalized,
    Missing = missing_value,
    CaseIssue = case_issue,
    ValidCase = valid_case,
    stringsAsFactors = FALSE,
    check.names = FALSE
  )

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

  result
}
