
#' @title Principal Component Analysis (Custom Scree Style)
#'
#' @description
#' Performs PCA with Bartlett test, KMO measure,
#' and visualizes scree plot and biplot (custom style).
#'
#' @param data A data frame or matrix
#' @param scale. Logical; scale variables
#' @param center Logical; center variables
#' @param verbose Logical; if TRUE, prints Bartlett and KMO results.
#'
#' @return List with Bartlett test, KMO, loadings, scores, variance
#'
#' @importFrom stats cor prcomp complete.cases
#' @importFrom psych cortest.bartlett KMO
#' @importFrom ggplot2 ggplot aes geom_col geom_line geom_point geom_text
#' @importFrom ggplot2 scale_y_continuous labs theme_minimal theme
#' @importFrom ggplot2 element_text element_line element_blank coord_equal
#' @importFrom ggrepel geom_text_repel
#' @importFrom gridExtra grid.arrange
#' @importFrom scales percent_format
#' @importFrom grid unit arrow
#' @export
#' @examples {
#' data(mtcars)
#' Result <- pca(mtcars)
#' }

pca <- function(data, scale. = TRUE, center = TRUE, verbose = FALSE){

  if (!is.data.frame(data) && !is.matrix(data)) {
    stop("data must be a data frame or matrix")
  }

  df <- as.data.frame(data)
  df <- df[, vapply(df, is.numeric, logical(1)), drop = FALSE]

  if (ncol(df) < 2) {
    stop("Need at least 2 numeric variables")
  }

  df <- df[stats::complete.cases(df), , drop = FALSE]

  if (nrow(df) < 2) {
    stop("Not enough complete observations")
  }

  cm <- stats::cor(df)

  if (verbose) {
    message("--- PCA Suitability ---")
  }

  singular <- tryCatch(base::det(cm) < 1e-10, error = function(e) TRUE)

  bart <- NULL
  if (!singular) {
    bart <- tryCatch(
      psych::cortest.bartlett(cm, n = nrow(df)),
      error = function(e) NULL
    )
  }

  if (verbose) {
    if (!is.null(bart)) {
      message("Bartlett Test:")
      message("Chi-square: ", round(bart$chisq, 3))
      message("df: ", bart$df)
      message("p-value: ", format.pval(bart$p.value))
    } else {
      message("Bartlett test not valid")
    }
  }

  kmo <- NULL
  if (!singular) {
    kmo <- tryCatch(psych::KMO(cm), error = function(e) NULL)
  }

  kmo_val <- NULL
  suitability <- NULL

  if (!is.null(kmo)) {
    kmo_val <- round(kmo$MSA, 3)
    suitability <- ifelse(
      kmo_val >= 0.6,
      "Suitable for PCA",
      "Not Suitable for PCA"
    )
  }

  if (verbose) {
    if (!is.null(kmo)) {
      message("KMO: ", kmo_val, " - ", suitability)
    } else {
      message("KMO not valid")
    }
  }

  p <- stats::prcomp(df, scale. = scale., center = center)

  load <- as.data.frame(p$rotation)
  scores <- as.data.frame(p$x)
  ve <- p$sdev^2 / sum(p$sdev^2)

    theme_pretty <- ggplot2::theme_minimal(base_family = "serif", base_size = 16) +
    ggplot2::theme(
      plot.title = ggplot2::element_text(face = "bold", hjust = 0.5),
      axis.title = ggplot2::element_text(face = "bold"),
      axis.text = ggplot2::element_text(face = "bold", color = "black"),
      axis.line = ggplot2::element_line(color = "black"),
      axis.ticks = ggplot2::element_line(color = "black"),
      panel.grid.minor = ggplot2::element_blank()
    )

  scree_data <- data.frame(
    PC = factor(paste0("PC", seq_along(ve)), levels = paste0("PC", seq_along(ve))),
    Var = ve,
    CumVar = cumsum(ve)
  )

  scree <- ggplot2::ggplot(scree_data, ggplot2::aes(x = PC)) +
    ggplot2::geom_col(ggplot2::aes(y = Var), fill = "#4C72B0", width = 0.6, alpha = 0.85) +
    ggplot2::geom_line(ggplot2::aes(y = CumVar, group = 1), color = "#DD8452", linewidth = 1.2) +
    ggplot2::geom_point(ggplot2::aes(y = CumVar), color = "#DD8452", size = 3) +
    ggplot2::geom_text(
      ggplot2::aes(y = Var, label = paste0(round(Var * 100, 1), "%")),
      vjust = -0.5, size = 4, family = "serif", fontface = "bold"
    ) +
    ggplot2::scale_y_continuous(labels = scales::percent_format(), limits = c(0, 1)) +
    ggplot2::labs(
      title = "PCA Scree Plot",
      x = "Principal Components",
      y = "Variance Explained"
    ) +
    theme_pretty

  if (ncol(scores) >= 2) {

    load2 <- load[, 1:2, drop = FALSE]
    load2$v <- rownames(load2)

    score2 <- scores[, 1:2, drop = FALSE]
    sf <- max(abs(score2))

    biplot <- ggplot2::ggplot() +
      ggplot2::geom_point(
        data = score2,
        ggplot2::aes(PC1, PC2),
        size = 3, alpha = 0.75, color = "#2C7BB6"
      ) +
      ggplot2::geom_segment(
        data = load2,
        ggplot2::aes(x = 0, y = 0, xend = PC1 * sf, yend = PC2 * sf),
        arrow = grid::arrow(length = grid::unit(0.3, "cm")),
        color = "#D7191C", linewidth = 1.2
      ) +
      ggrepel::geom_text_repel(
        data = load2,
        ggplot2::aes(PC1 * sf, PC2 * sf, label = v),
        size = 5, color = "#D7191C",
        family = "serif", fontface = "bold"
      ) +
      ggplot2::labs(
        title = "PCA Biplot",
        x = "Component 1",
        y = "Component 2"
      ) +
      theme_pretty +
      ggplot2::coord_equal()

  } else {
    warning("Biplot not created: less than 2 principal components")
  }

  invisible(list(
    bartlett = bart,
    kmo = kmo,
    loadings = load,
    scores = scores,
    variance = ve,
    scree_plot = scree,
    biplot = if (exists("biplot")) biplot else NULL
  ))
}
