mixed <- function(base) {
  return(any(sapply(base, is.factor)) && any(sapply(base, is.numeric)))
}

numerical <- function(base) {
  return(all(sapply(base, is.numeric)))
}

categorical <- function(base) {
  return(all(sapply(base, is.factor)))
}


validate_input <- function(base,
                           treatment_name,
                           outcome_names,
                           win_function,
                           n_trees_drf = NULL,
                           n_of_folds = NULL,
                           seed = NULL,
                           distance = NULL) {
  # Check for Missing Data ----------------------------------------
  if (any(is.na(base[, outcome_names, drop = FALSE]))) {
    stop("The data frame contains missing outcomes. Please remove or impute NAs before using this function.")
  }


  # Check type --------------------------------------------------------------

  if (!is.data.frame(base)) stop("Argument 'base' must be a data.frame.")
  if (!is.character(treatment_name) || length(treatment_name) != 1) {
    stop("Argument 'treatment_name' must be a single string (character vector of length 1).")
  }
  if (!is.character(outcome_names)) stop("Argument 'outcome_names' must be a character.")

  covariate_names <- setdiff(colnames(base), c(outcome_names, treatment_name))

  if (length(covariate_names) == 0) {
    stop("No covariates found. This method requires covariates.")
  }

  if (is.character(win_function) && length(win_function) == 1 && win_function != "lexicographic") {
    stop("Argument 'win_function' is either 'lexicographic' or a function.")
  }
  if (is.character(win_function) && length(win_function) != 1) {
    stop("Argument 'win_function' is either 'lexicographic' or a function.")
  }

  if (!is.character(win_function)) {
    if (!is.function(win_function) || length(formals(win_function)) != 2) {
      stop("Argument 'win_function' is either 'lexicographic' or a function of exactly two arguments.")
    }
  }

  # Check Input Conformity --------------------------------------------------
  columns <- colnames(base)

  if (!all(treatment_name %in% columns)) stop("Argument 'treatment_name' must be the name of a 'data' column.")
  if (!all(outcome_names %in% columns)) stop("Argument 'outcome_names' must be a subset of the 'data' columns.")

  if (!all(vapply(base[outcome_names], function(x) is.numeric(x), FUN.VALUE = logical(1)))) {
    stop("Elements of 'base[outcome_names]' must all be numeric.")
  }

  if (!all(is.numeric(base[[treatment_name]])) || !all(base[[treatment_name]] %in% c(0, 1))) {
    stop("Elements of 'base[[treatment_name]]' must all be numeric and binary.")
  }

  if (!is.null(n_trees_drf)) {
    check_positive_integer(n_trees_drf)
  }

  if (!is.null(n_of_folds)) {
    check_positive_integer(n_of_folds)
  }

  if (!is.null(seed)) {
    check_positive_integer(seed)
  }

  return(invisible(TRUE))
}

check_positive_integer <- function(x) {
  arg_name <- deparse(substitute(x)) # Capture the argument name

  # Check if x is numeric
  if (!is.numeric(x)) {
    stop(sprintf("Argument '%s' must be numeric.", arg_name))
  }

  # Check if x is a whole number
  if (x != floor(x)) {
    stop(sprintf("Argument '%s' must be an integer (no decimals).", arg_name))
  }

  # Check if x is positive
  if (x <= 0) {
    stop(sprintf("Argument '%s' must be a positive integer.", arg_name))
  }

  return(invisible(TRUE))
}

validate_propensity_input <- function(propensity_model, n_trees_propensity, propensity_scores, clip_propensity) {
  # Check propensity_model
  if (!is.character(propensity_model) || length(propensity_model) != 1) {
    stop("Argument 'propensity_model' must be a single character string.")
  }
  if (!propensity_model %in% c("probability_forest", "glm")) {
    stop("Argument 'propensity_model' must be either 'probability_forest' or 'regression_forest'.")
  }

  # Check propensity_scores
  if (!is.null(propensity_scores)) {
    if (!is.numeric(propensity_scores)) {
      stop("Argument 'propensity_scores' must be a numeric vector.")
    }
    if (any(is.na(propensity_scores))) {
      stop("The vector 'propensity_scores' must not contain missing values.")
    }
    if (any(propensity_scores < 0 | propensity_scores > 1)) {
      stop("All entries in 'propensity_scores' must be between 0 and 1.")
    }
  }

  # Check clip_propensity
  if (!is.numeric(clip_propensity) || length(clip_propensity) != 1) {
    stop("Argument 'clip_propensity' must be a single numeric value.")
  }
  if (clip_propensity < 0 || clip_propensity > 1) {
    stop("Argument 'clip_propensity' must be between 0 and 1.")
  }

  if (!is.null(n_trees_propensity)) {
    check_positive_integer(n_trees_propensity)
  }

  return(invisible(TRUE))
}
