#' Evaluate the performance of model predictions
#'
#' This function evaluates classification performance from prediction
#' probabilities and true labels. It is designed for evaluating predictions
#' generated by \code{PredictCrcBiomeScreen()} or other external prediction
#' workflows.
#'
#' The output structure is aligned with the evaluation results produced by
#' \code{EvaluateModel()}, including ROC-AUC, F1 score, balanced accuracy,
#' precision, recall, and the confusion matrix.
#'
#' @param predictions A data frame or matrix of model predictions, typically
#'   containing probability scores for each class.
#' @param true_labels A character vector or factor of the true class labels.
#' @param TrueLabel The positive class label, for example \code{"CRC"}, used for
#'   ROC-AUC calculation and threshold-based classification.
#' @param TaskName A character string used to label the output files.
#' @param PlotAUC A logical value indicating whether to plot the ROC curve.
#' @param outdir The output directory where plots will be saved. The default is
#'   \code{tempdir()}.
#'
#' @importFrom pROC roc auc ci.auc coords plot.roc
#' @importFrom caret confusionMatrix
#'
#' @return A list containing prediction probabilities, ROC curve, AUC, F1 score,
#'   balanced accuracy, precision, recall, confusion matrix, and predicted
#'   classes.
#' @export
#'
#' @examples
#' # Fake prediction probabilities for 4 samples and 2 classes
#' pred <- data.frame(
#'   control = c(0.8, 0.3, 0.7, 0.2),
#'   CRC     = c(0.2, 0.7, 0.3, 0.8)
#' )
#'
#' # True class labels
#' labels <- factor(c("control", "CRC", "control", "CRC"))
#'
#' # Evaluate performance using CRC as the positive class
#' result <- EvaluateCrcBiomeScreen(
#'   predictions = pred,
#'   true_labels = labels,
#'   TrueLabel = "CRC",
#'   PlotAUC = FALSE
#' )
#'
#' result$AUC
#' result$BalancedAccuracy
#' result$conf.matrix
EvaluateCrcBiomeScreen <- function(predictions,
                                   true_labels,
                                   TrueLabel = NULL,
                                   TaskName = "ModelEvaluation",
                                   PlotAUC = FALSE,
                                   outdir = tempdir()) {

  if (is.null(TrueLabel)) {
    stop("Please specify the 'TrueLabel' (the positive class).")
  }

  predictions <- as.data.frame(predictions)

  if (!TrueLabel %in% colnames(predictions)) {
    stop(sprintf(
      "The 'predictions' data frame must contain a column named '%s'.",
      TrueLabel
    ))
  }

  if (length(true_labels) != nrow(predictions)) {
    stop(
      "The length of 'true_labels' must match the number of rows in 'predictions'."
    )
  }

  actual.classes <- as.factor(true_labels)

  if (!TrueLabel %in% levels(actual.classes)) {
    stop(sprintf(
      "The positive class '%s' is not present in 'true_labels'.",
      TrueLabel
    ))
  }

  label <- levels(actual.classes)

  if (length(label) != 2) {
    stop(
      "EvaluateCrcBiomeScreen currently supports binary classification only. ",
      "Please provide true labels with exactly two classes."
    )
  }

  NegativeLabel <- label[!label %in% TrueLabel]

  if (length(NegativeLabel) != 1) {
    stop("Unable to determine the negative class label.")
  }

  # Extract prediction probabilities for the positive class
  test.pred.prob <- predictions[[TrueLabel]]

  if (!is.numeric(test.pred.prob)) {
    test.pred.prob <- as.numeric(as.character(test.pred.prob))
  }

  if (anyNA(test.pred.prob)) {
    stop(
      "Prediction probabilities contain NA values or could not be converted to numeric."
    )
  }

  # Calculate ROC curve and AUC
  roc.curve <- pROC::roc(
    actual.classes,
    test.pred.prob,
    levels = levels(actual.classes)
  )

  auc.value <- pROC::auc(roc.curve)

  # Confidence interval for AUC
  auc.ci <- pROC::ci.auc(
    roc.curve,
    conf.level = 0.95,
    method = "delong"
  )

  # Find optimal threshold using Youden's index
  coords.result <- pROC::coords(
    roc.curve,
    "best",
    ret = "all",
    best.method = "youden"
  )

  optimal.threshold <- coords.result$threshold

  # Make class predictions using the optimal threshold
  test.class.predictions <- as.factor(ifelse(
    test.pred.prob >= optimal.threshold,
    TrueLabel,
    NegativeLabel
  ))

  test.class.predictions <- factor(
    test.class.predictions,
    levels = levels(actual.classes)
  )

  # Confusion matrix
  conf.matrix <- caret::confusionMatrix(
    test.class.predictions,
    actual.classes,
    positive = TrueLabel
  )

  # Extract metrics in the same style as EvaluateRF/EvaluateXGBoost
  f1_score <- conf.matrix$byClass["F1"]
  balanced_accuracy <- conf.matrix$byClass["Balanced Accuracy"]
  precision <- conf.matrix$byClass["Precision"]
  recall <- conf.matrix$byClass["Recall"]

  # Plot ROC curve
  if (isTRUE(PlotAUC)) {
    if (!dir.exists(outdir)) {
      dir.create(outdir, recursive = TRUE)
    }

    grDevices::pdf(file.path(
      outdir,
      paste0("roc.curve.", TaskName, ".pdf")
    ))
    plot(roc.curve, print.auc = TRUE, print.thres = TRUE)
    grDevices::dev.off()
  }

  return(list(
    predictions = predictions,
    roc.curve = roc.curve,
    AUC = auc.value,
    AUC.CI = auc.ci,
    optimal.threshold = optimal.threshold,
    F1 = f1_score,
    BalancedAccuracy = balanced_accuracy,
    Precision = precision,
    Recall = recall,
    conf.matrix = conf.matrix
  ))
}
