#==========================================================
# Dependency Check
#==========================================================

#' Check Conditional Variable Dependency
#'
#' Checks whether a target variable satisfies a requirement
#' when a specified condition is met in another variable.
#'
#' @param data A data.frame.
#' @param condition_variable A single character string
#'   specifying the variable defining the condition.
#' @param condition_value The value of \code{condition_variable}
#'   that activates the dependency rule.
#' @param target_variable A single character string specifying
#'   the variable that is required when the condition is met.
#' @param allow_blank Logical. If FALSE, blank character values
#'   in the target variable are treated as missing. Default is
#'   FALSE.
#'
#' @return A data.frame of class \code{DependencyCheck}
#'   containing row-level dependency diagnostics.
#'
#' @details
#' The dependency rule is interpreted as:
#'
#' If \code{condition_variable == condition_value}, then
#' \code{target_variable} must contain an acceptable value.
#'
#' Missing values in the condition variable do not activate
#' the dependency rule.
#'
#' When \code{allow_blank = FALSE}, empty strings and strings
#' containing only whitespace are treated as unavailable target
#' values.
#'
#' The function does not modify the original data.
#'
#' @examples
#' dat <- data.frame(
#'   Disease_Status = c(
#'     "Positive",
#'     "Negative",
#'     "Positive",
#'     "Positive"
#'   ),
#'   Test_Result = c(
#'     "PCR positive",
#'     NA,
#'     NA,
#'     "Detected"
#'   )
#' )
#'
#' dependency_check(
#'   dat,
#'   condition_variable = "Disease_Status",
#'   condition_value = "Positive",
#'   target_variable = "Test_Result"
#' )
#'
#' @export

dependency_check <- function(
    data,
    condition_variable,
    condition_value,
    target_variable,
    allow_blank = FALSE
) {

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

  #---------------------------------------------
  # Helper: validate variable name
  #---------------------------------------------
  validate_variable <- function(variable, argument) {

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

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

    invisible(TRUE)
  }

  #---------------------------------------------
  # Validate required arguments
  #---------------------------------------------
  if (missing(condition_variable)) {
    stop(
      "'condition_variable' must be supplied.",
      call. = FALSE
    )
  }

  if (missing(condition_value)) {
    stop(
      "'condition_value' must be supplied.",
      call. = FALSE
    )
  }

  if (missing(target_variable)) {
    stop(
      "'target_variable' must be supplied.",
      call. = FALSE
    )
  }

  validate_variable(
    condition_variable,
    "condition_variable"
  )

  validate_variable(
    target_variable,
    "target_variable"
  )

  #---------------------------------------------
  # Variables must differ
  #---------------------------------------------
  if (identical(
    condition_variable,
    target_variable
  )) {
    stop(
      paste0(
        "'condition_variable' and 'target_variable' ",
        "must refer to different variables."
      ),
      call. = FALSE
    )
  }

  #---------------------------------------------
  # Validate condition value
  #---------------------------------------------
  if (
    length(condition_value) != 1L ||
    is.na(condition_value)
  ) {
    stop(
      "'condition_value' must contain exactly one non-missing value.",
      call. = FALSE
    )
  }

  #---------------------------------------------
  # Validate allow_blank
  #---------------------------------------------
  if (
    !is.logical(allow_blank) ||
    length(allow_blank) != 1L ||
    is.na(allow_blank)
  ) {
    stop(
      "'allow_blank' must be TRUE or FALSE.",
      call. = FALSE
    )
  }

  #---------------------------------------------
  # Extract variables
  #---------------------------------------------
  condition <- data[[condition_variable]]
  target <- data[[target_variable]]

  #---------------------------------------------
  # Determine activated rows
  #---------------------------------------------
  condition_missing <- is.na(condition)

  condition_met <- rep(
    FALSE,
    length(condition)
  )

  comparable <- which(
    !condition_missing
  )

  if (length(comparable) > 0L) {

    condition_met[comparable] <-
      condition[comparable] ==
      condition_value

    condition_met[
      is.na(condition_met)
    ] <- FALSE
  }

  #---------------------------------------------
  # Target missing values
  #---------------------------------------------
  target_missing <- is.na(target)

  #---------------------------------------------
  # Blank target values
  #---------------------------------------------
  target_blank <- rep(
    FALSE,
    length(target)
  )

  if (
    is.character(target) ||
    is.factor(target)
  ) {

    target_character <-
      as.character(target)

    non_missing_target <-
      !target_missing

    target_blank[non_missing_target] <-
      grepl(
        "^[[:space:]]*$",
        target_character[
          non_missing_target
        ]
      )
  }

  #---------------------------------------------
  # Determine target availability
  #---------------------------------------------
  target_available <-
    !target_missing

  if (!allow_blank) {
    target_available <-
      target_available &
      !target_blank
  }

  #---------------------------------------------
  # Dependency violation
  #---------------------------------------------
  dependency_violation <-
    condition_met &
    !target_available

  #---------------------------------------------
  # Valid dependency
  #---------------------------------------------
  valid_dependency <-
    !condition_met |
    target_available

  #---------------------------------------------
  # Construct result
  #---------------------------------------------
  result <- data.frame(
    Row = seq_along(condition),
    ConditionValue = condition,
    TargetValue = target,
    ConditionMissing = condition_missing,
    ConditionMet = condition_met,
    TargetMissing = target_missing,
    TargetBlank = target_blank,
    DependencyViolation = dependency_violation,
    ValidDependency = valid_dependency,
    stringsAsFactors = FALSE,
    check.names = FALSE
  )

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

  result
}
