#' One-Factor ANOVA for Agricultural Designs
#'
#' Performs analysis of variance for one-factor experiments under
#' Completely Randomized Design (CRD), Randomized Block Design (RBD),
#' and Latin Square Design (LSD). Supports multiple response variables,
#' data transformation, and mean comparison tests.
#'
#' @param data A data frame containing experimental data.
#' @param design Type of design. One of "CRD", "RBD", or "LSD".
#' @param treatment Name of treatment column in the dataset.
#' @param replication Name of replication/block column (required for RBD).
#' @param row Name of row factor (required for LSD).
#' @param column Name of column factor (required for LSD).
#' @param responsevar Specific variables c("v1" ,"v2", "v3") or "all".
#' @param test Mean comparison test. Options include "lsd", "tukey", or "duncan".
#' @param transform List specifying transformations for variables.
#' Options include "log", "sqrt", and "arcsine".
#' @param narc Numeric or list specifying denominator for arcsine transformation.
#' @param alpha Significance level (e.g., 0.05).
#'
#' @details
#' This function supports:
#' \itemize{
#'   \item Analysis for CRD, RBD, and LSD designs
#'   \item Multiple response variables
#'   \item Data transformations (log, square root, arcsine)
#'   \item Mean separation tests (LSD, Tukey, Duncan)
#'   \item Combined summary table output
#' }
#'
#' @return A list containing:
#' \describe{
#'   \item{Individual outputs}{
#'     Analysis results for each response variable separately.
#'   }
#'   \item{Summary statistics}{
#'     Arithmetic mean, standard deviation, minimum, and maximum values for each variable.
#'   }
#'   \item{Shapiro-Wilk test}{
#'     Results of the Shapiro-Wilk test for normality.
#'   }
#'   \item{ANOVA and mean comparison}{
#'     ANOVA table along with mean comparison based on the selected test for each response variable.
#'   }
#'   \item{Combined table}{
#'     A summary table including mean ± SD, grouping letters, SEm, SEd, CD, and CV values for all variables.
#'   }
#' }
#'
#' @importFrom stats anova as.formula ave sd setNames
#' @importFrom stats aov reformulate pf qt qtukey shapiro.test na.omit
#' @importFrom multcompView multcompLetters
#' @importFrom utils combn as.roman
#'
#' @examples
#'
#' # Example
#' data(Gnutdataset)
#' head(Gnutdataset)
#'Out_rbd <- aov_of(
#'            Gnutdataset,
#'            design = "RBD",
#'            treatment = "Trt",
#'            replication = "Rep",
#'            responsevar = "all",
#'            transform = list(Noflwr="sqrt", Germ="arcsine", Disease="arcsine"),
#'            narc = list(Germ=10, Disease=5),
#'            alpha = 0.05 )
#'
#' # Single variable result
#' Out_rbd$Germ
#'
#' # Combined table for all varaibles
#' Out_rbd$Combined_table
#'
#' # Entire output
#'Out_rbd
#'
#' #Example without transformation
#' Out_rbd2 <- aov_of(
#'   Soildataset,
#'   design = "RBD",
#'   treatment = "TRT",
#'   replication = "REP",
#'   responsevar = "all",
#'   test = "lsd",
#'   alpha = 0.05
#' )
#'
#' Out_rbd2$Combined_table

#' @export
aov_of <- function(data,
                             design = c("CRD", "RBD", "LSD"),
                             treatment,
                             replication = NULL,
                             row = NULL,
                             column = NULL,
                             responsevar = "all",
                             test = c("lsd", "tukey", "duncan"),
                             transform = NULL,
                             narc = 1,
                             alpha = 0.05) {



  design <- match.arg(design)
  test   <- match.arg(test)

  fmt <- function(x) format(round(x, 2), nsmall = 2)

  #Select response variables
  if (identical(responsevar, "all") || responsevar[1] == "all") {
    responses <- names(data)[sapply(data, is.numeric)]
  } else {
    responses <- responsevar
  }

  # Make sure requested variables exist and are numeric
  missing_vars <- setdiff(responses, names(data))
  if (length(missing_vars) > 0) {
    stop("Variables not found in data: ", paste(missing_vars, collapse = ", "))
  }
  non_num <- names(which(!sapply(data[responses], is.numeric)))
  if (length(non_num) > 0) {
    stop("Variables are not numeric: ", paste(non_num, collapse = ", "))
  }

  # Prepare transform_list (named list, missing = NULL)
  transform_list <- setNames(rep(list(NULL), length(responses)), responses)

  if (!is.null(transform)) {
    if (is.character(transform) && length(transform) == 1) {
# single method  for all variables
      transform_list[] <- transform
    } else if (is.list(transform)) {
      if (is.null(names(transform)) || any(names(transform) == "")) {
        stop("'transform' list must have valid variable names", call. = FALSE)
      }
      known <- intersect(names(transform), responses)
      for (v in known) {
        val <- transform[[v]]
        if (!is.character(val) || length(val) != 1 || !nzchar(val)) {
          stop("Invalid transformation for '", v, "': must be single non-empty string")
        }
        transform_list[[v]] <- val
      }
      unknown <- setdiff(names(transform), responses)
      if (length(unknown) > 0) {
        warning("Transform specified for non-analyzed variables: ",
                paste(unknown, collapse = ", "), call. = FALSE)
      }
    } else {
      stop("'transform' must be NULL, single string, or named list", call. = FALSE)
    }
  }

#Prepare narc_list (named list, missing  default)
  default_narc <- 1L   # fallback if nothing specified

  if (is.list(narc)) {
    if (is.null(names(narc)) || any(names(narc) == "")) {
      stop("'narc' list must have valid variable names", call. = FALSE)
    }
    narc_list <- setNames(rep(list(default_narc), length(responses)), responses)
    for (v in intersect(names(narc), responses)) {
      val <- narc[[v]]
      if (!is.numeric(val) || length(val) != 1 || val != floor(val)) {
        stop("narc for '", v, "' must be integer, got: ", val, call. = FALSE)
      }
      if (val < 1) {
        stop("narc for '", v, "' must be >= 1, got: ", val, call. = FALSE)
      }
      narc_list[[v]] <- as.integer(val)
    }
    unknown <- setdiff(names(narc), responses)
    if (length(unknown) > 0) {
      warning("narc specified for non-analyzed variables: ",
              paste(unknown, collapse = ", "), call. = FALSE)
    }
  } else if (is.numeric(narc) && length(narc) == 1 && narc == floor(narc) && narc >= 1) {
    narc_list <- setNames(rep(list(as.integer(narc)), length(responses)), responses)
  } else {
    stop("'narc' must be single positive integer or named list of positive integers",
         call. = FALSE)
  }

# Main loop over responses
  results_all <- list()
  combined_trait_table <- list()

  for (resp in responses) {

    y_original <- data[[resp]]
    y <- y_original
    transformed <- FALSE

    this_trans <- transform_list[[resp]]
    this_narc  <- narc_list[[resp]]

#Apply transformation
    if (!is.null(this_trans)) {
      method <- this_trans

      if (method == "log") {
        y <- log(y_original + if (any(y_original == 0, na.rm = TRUE)) 0.5 else 0)
      } else if (method == "sqrt") {
        y <- sqrt(y_original + if (any(y_original == 0, na.rm = TRUE)) 0.5 else 0)
      } else if (method == "arcsine") {
        y2 <- y_original
        # Use per-variable narc for arcsine adjustment
        adj <- 1 / (4 * this_narc)
        y2[y2 == 0]   <- adj
        y2[y2 == 100] <- 100 - adj
        y <- asin(sqrt(y2 / 100)) * 180 / pi
      } else {
        stop("Unsupported transformation for ", resp, ": ", method, call. = FALSE)
      }
      transformed <- TRUE
    }

    data$Ytemp <- y   # temporary column for modeling

# Summary statistics (original + transformed)
    summary_table <- data.frame(
      Data = "Original",
      Mean = round(mean(y_original, na.rm = TRUE), 4),
      SD   = round(sd(y_original, na.rm = TRUE),   4),
      Min  = round(min(y_original, na.rm = TRUE),  4),
      Max  = round(max(y_original, na.rm = TRUE),  4),
      stringsAsFactors = FALSE
    )

    if (transformed) {
      summary_table <- rbind(summary_table,
                             data.frame(
                               Data = "Transformed",
                               Mean = round(mean(y, na.rm = TRUE), 4),
                               SD   = round(sd(y, na.rm = TRUE),   4),
                               Min  = round(min(y, na.rm = TRUE),  4),
                               Max  = round(max(y, na.rm = TRUE),  4),
                               stringsAsFactors = FALSE
                             )
      )
    }

#Normality tests
    sh1 <- shapiro.test(y_original)
    normalitytest <- data.frame(
      Data    = "Original",
      W       = round(sh1$statistic, 4),
      P_value = round(sh1$p.value, 4),
      Normal  = ifelse(sh1$p.value > alpha, "YES", "NO"),
      stringsAsFactors = FALSE
    )

    if (transformed) {
      sh2 <- shapiro.test(y)
      normalitytest <- rbind(normalitytest,
                             data.frame(
                               Data    = "Transformed",
                               W       = round(sh2$statistic, 4),
                               P_value = round(sh2$p.value, 4),
                               Normal  = ifelse(sh2$p.value > alpha, "YES", "NO"),
                               stringsAsFactors = FALSE
                             )
      )
    }

# ANOVA model
    if (design == "CRD")   form <- as.formula(paste("Ytemp ~", treatment))
    if (design == "RBD")   form <- as.formula(paste("Ytemp ~", replication, "+", treatment))
    if (design == "LSD")   form <- as.formula(paste("Ytemp ~", row, "+", column, "+", treatment))

    model    <- aov(form, data = data)
    anovtab  <- anova(model)
    anovtable <- summary(model)

    MSerror  <- anovtab["Residuals", "Mean Sq"]
    df_error <- anovtab["Residuals", "Df"]
    r        <- as.numeric(table(data[[treatment]])[1])   # replication count

#Treatment means (original & transformed scale)
    grp <- data[[treatment]]
    means_orig <- tapply(y_original, grp, mean, na.rm = TRUE)
    sd_orig    <- tapply(y_original, grp, sd,   na.rm = TRUE)
    means_trans <- tapply(y, grp, mean, na.rm = TRUE)
    sd_trans    <- tapply(y, grp, sd,   na.rm = TRUE)

#  Statistics
    SEm <- sqrt(MSerror / r)
    SEd <- sqrt(2 * MSerror / r)
    CD  <- qt(1 - alpha/2, df_error) * SEd
    HSD <- qtukey(1 - alpha, length(means_trans), df_error) * sqrt(MSerror / r)
    CV  <- (sqrt(MSerror) / (if (transformed) mean(y) else mean(y_original))) * 100

 #Treatment significance check
    p_treatment <- anovtab[treatment, "Pr(>F)"]

    if (p_treatment > alpha) {
      letters_grp <- rep("", length(means_orig))
      crit_val    <- "NS"
    } else {
 # Multiple comparison letters
      means <- means_trans
      t_len <- length(means)
      diffmat <- outer(means, means, function(x,y) abs(x - y))
      sig <- matrix(FALSE, t_len, t_len)

      if (test == "lsd")   sig <- diffmat > CD
      if (test == "tukey") sig <- diffmat > HSD
      if (test == "duncan") {
        R <- numeric(t_len)
        for (k in 2:t_len) {
          qv <- qtukey(1 - alpha, k, df_error) / sqrt(2)
          R[k] <- qv * sqrt(MSerror / r)
        }
        for (i in 1:(t_len-1)) {
          for (j in (i+1):t_len) {
            dist <- j - i + 1
            if (diffmat[i,j] > R[dist]) sig[i,j] <- TRUE
          }
        }
        sig <- sig | t(sig)
      }
      diag(sig) <- FALSE

      pairs <- sig[lower.tri(sig)]
      names(pairs) <- combn(names(means), 2, paste, collapse = "-")
      letters_grp <- multcompView::multcompLetters(pairs)$Letters
      letters_grp <- letters_grp[match(names(means_orig), names(letters_grp))]

      crit_val <- if (test == "lsd") fmt(CD) else if (test == "tukey") fmt(HSD) else " "
    }

#  Build mean +- SD string
    if (transformed) {
      values <- paste0(fmt(means_orig), " +- ", fmt(sd_orig),
                       " (", fmt(means_trans), " +- ", fmt(sd_trans), ") ",
                       letters_grp)
    } else {
      values <- paste0(fmt(means_orig), " +- ", fmt(sd_orig), " ", letters_grp)
    }

    out <- data.frame(
      Level = names(means_orig),
      Value = values,
      stringsAsFactors = FALSE
    )
    names(out)[2] <- resp

# Statistics rows
    stat_rows <- data.frame(
      Level = c("SEm +-", "SEd +-", if (test == "lsd") "CD(0.05)" else if (test == "tukey") "HSD" else " ", "CV%"),
      Value = c(fmt(SEm), fmt(SEd), crit_val, fmt(CV)),
      stringsAsFactors = FALSE
    )
    names(stat_rows)[2] <- resp

    final_table <- rbind(out, stat_rows)

    # Store results
    results_all[[resp]] <- list(
      Summarydata  = summary_table,
      Normality    = normalitytest,
      ANOVA        = anovtable,
      Means_table  = final_table
    )

    combined_trait_table[[resp]] <- final_table[, 2, drop = FALSE]
    rownames(combined_trait_table[[resp]]) <- final_table$Level
  }

  # Combined table
  combined_df <- do.call(cbind, combined_trait_table)
  combined_df <- data.frame(Level = rownames(combined_df), combined_df, row.names = NULL)
  results_all$Combined_table <- combined_df

  return(results_all)
}






