#' Compare Moderation Models
#'
#' Compares candidate moderation models using information criteria (AIC/AICc).
#'
#' @param object A "modlr" object
#' @param models Optional numeric vector specifying which models to compare.
#'   If NULL (default), all candidate models are evaluated.
#' @param corrected Logical; whether to use AICc
#'
#' @return A data frame with model comparison results
#'
#' @examples
#' set.seed(123)
#'
#' n <- 100
#' x <- rnorm(n)
#' z <- 0.5 * x + sqrt(1 - 0.5^2) * rnorm(n)
#'
#' y <- 0.3 * x + 0.3 * z + 0.8 * x * z + rnorm(n)
#'
#' data <- data.frame(x, z, y)
#'
#' result <- moderated_regression(data, iv = "x", moderator = "z", dv = "y")
#'
#' compare_models(result)
#' compare_models(result, models = c(1, 2))
#'
#' @export

compare_models <- function(object, models = NULL, corrected = TRUE) {

  if (!inherits(object, "modlr"))
    stop("object must be of class modlr")

  data <- object$data
  dv <- as.character(object$call$dv)

  if (object$center) {
    x_var <- "x_center"
    z_var <- "z_center"
  } else {
    x_var <- object$iv
    z_var <- object$moderator
  }


  n <- nrow(data)

  # ✅ Available models
  all_models <- 1:8
  if (is.null(models)) models <- all_models

  if (!all(models %in% all_models)) {
    stop("Invalid model indices. Must be between 1 and 8.")
  }

  # ✅ IC decision
  k_max <- 8
  use_aicc <- corrected && ((n / k_max) < 40)
  ic_name <- ifelse(use_aicc, "AICc", "AIC")


  # Covariates (aligned with moderated_regression)

  covariates <- object$covariates

  if (is.null(covariates) || length(covariates) == 0) {
    cov_part <- NULL
  } else {
    if (object$center) {
      cov_names <- paste0(covariates, "_center")
    } else {
      cov_names <- covariates
    }
    cov_part <- paste(cov_names, collapse = " + ")
  }

  # Construct terms
  data$x_z   <- data[[x_var]] * data[[z_var]]
  data$x_sq  <- data[[x_var]]^2
  data$z_sq  <- data[[z_var]]^2
  data$z_xsq <- data[[z_var]] * data$x_sq
  data$x_zsq <- data[[x_var]] * data$z_sq

  # Base formula
  base_formula <- if (!is.null(cov_part)) {
    paste(dv, "~", cov_part)
  } else paste(dv, "~ 1")

  # Define once (used by multiple models)
  terms5 <- paste(base_formula, "+", x_var, "+", z_var)
  if (object$type_iv == "continuous") terms5 <- paste(terms5, "+ x_sq")
  if (object$type_moderator == "continuous") terms5 <- paste(terms5, "+ z_sq")

  # IC function
  calc_ic <- function(model) {
    k <- length(coef(model)) + 1
    aic <- AIC(model)
    if (use_aicc) {
      aic + (2 * k^2 + 2 * k) / (n - k - 1)
    } else {
      aic
    }
  }

  # Initialize
  model_names <- character(8)
  ic_vals <- params <- k_vals <- rep(NA, 8)

  # Model 1
  model_names[1] <- "1: x + z"
  if (1 %in% models) {
    m1 <- lm(as.formula(paste(base_formula, "+", x_var, "+", z_var)), data)
    ic_vals[1] <- calc_ic(m1)
    params[1] <- length(coef(m1))
    k_vals[1] <- params[1] + 1
  }

  # Model 2
  model_names[2] <- "2: x + x^2"
  if (2 %in% models && object$type_iv == "continuous") {
    m2 <- lm(as.formula(paste(base_formula, "+", x_var, "+ x_sq")), data)
    ic_vals[2] <- calc_ic(m2)
    params[2] <- length(coef(m2))
    k_vals[2] <- params[2] + 1
  }

  # Model 3
  model_names[3] <- "3: z + z^2"
  if (3 %in% models && object$type_moderator == "continuous") {
    m3 <- lm(as.formula(paste(base_formula, "+", z_var, "+ z_sq")), data)
    ic_vals[3] <- calc_ic(m3)
    params[3] <- length(coef(m3))
    k_vals[3] <- params[3] + 1
  }

  # Model 4
  model_names[4] <- "4: x + z + x*z"
  if (4 %in% models) {
    m4 <- lm(as.formula(paste(base_formula, "+", x_var, "+", z_var, "+ x_z")), data)
    ic_vals[4] <- calc_ic(m4)
    params[4] <- length(coef(m4))
    k_vals[4] <- params[4] + 1
  }

  # Model 5
  model_names[5] <- "5: x + z + x^2"
  if (5 %in% models) {
    m5 <- lm(as.formula(terms5), data)
    ic_vals[5] <- calc_ic(m5)
    params[5] <- length(coef(m5))
    k_vals[5] <- params[5] + 1
  }

  # Model 6
  model_names[6] <- "6: x + z + x*z + x^2"
  if (6 %in% models) {
    m6 <- lm(as.formula(paste(terms5, "+ x_z")), data)
    ic_vals[6] <- calc_ic(m6)
    params[6] <- length(coef(m6))
    k_vals[6] <- params[6] + 1
  }

  # Model 7
  model_names[7] <- "7: x + z + x*z + x^2 + z*x^2"
  if (7 %in% models && object$type_iv == "continuous") {
    m7 <- lm(as.formula(paste(
      base_formula, "+", x_var, "+", z_var,
      "+ x_z + x_sq + z_xsq"
    )), data)

    ic_vals[7] <- calc_ic(m7)
    params[7] <- length(coef(m7))
    k_vals[7] <- params[7] + 1
  }

  # Model 8
  model_names[8] <- "8: x + z + x*z + x^2 + x*z^2"
  if (8 %in% models && object$type_moderator == "continuous") {
    m8 <- lm(as.formula(paste(terms5, "+ x_z + x_zsq")), data)
    ic_vals[8] <- calc_ic(m8)
    params[8] <- length(coef(m8))
    k_vals[8] <- params[8] + 1
  }

  # Compute IC metrics
  valid <- which(!is.na(ic_vals))
  min_ic <- min(ic_vals[valid])

  delta <- likelihood <- weight <- rep(NA, 8)
  delta[valid] <- ic_vals[valid] - min_ic
  likelihood[valid] <- exp(-0.5 * delta[valid])
  weight[valid] <- likelihood[valid] / sum(likelihood[valid])

  rank_vals <- rep(NA, 8)
  rank_vals[valid] <- rank(ic_vals[valid], ties.method = "first")

  results <- data.frame(
    Model = model_names,
    Rank = rank_vals,
    Params = params,
    K = k_vals,
    IC = round(ic_vals, 2),
    Delta = round(delta, 2),
    Likelihood = round(likelihood, 3),
    Weight = round(weight, 3),
    Status = ifelse(is.na(ic_vals), "Skipped", "Estimated"),
    stringsAsFactors = FALSE
  )

  colnames(results)[colnames(results) == "IC"] <- ic_name
  colnames(results)[colnames(results) == "Delta"] <- paste0("Delta_", ic_name)

  width <- max(nchar(results$Model))
  results$Model <- format(results$Model, width = width, justify = "left")

  best_idx <- which.min(results[[ic_name]])
  best_row <- results[best_idx, ]

  message("\nBest model (by ", ic_name, "):")
  message(best_row$Model)
  message(ic_name, " = ", best_row[[ic_name]],
          " | Weight = ", round(best_row$Weight, 3), "\n")

  # -------------------------------
  # Covariate note
  # -------------------------------
  if (!is.null(object$covariates) && length(object$covariates) > 0) {
    message("Note: Results are conditional on the included covariates (held constant).\n")
  }

  return(results)


}
