#' Johnson-Neyman Analysis
#'
#' Computes regions of significance for an interaction effect.
#'
#' @param object A fitted model (modlr object)
#' @param alpha Significance level
#' @param robust Logical; use HC3 robust standard errors
#'
#' @return A data.frame of Johnson-Neyman results
#'
#' @importFrom stats qt qnorm coef vcov df.residual
#' @importFrom sandwich vcovHC
#'
#' @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")
#'
#' johnson_neyman(result)
#' johnson_neyman(result, robust = TRUE)
#'
#' @export

johnson_neyman <- function(object, alpha = 0.05, robust = NULL) {

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

  if (object$type_moderator != "continuous") {
    stop("Johnson-Neyman technique is only for continuous moderators")
  }

  data <- object$data

  # -------------------------------
  # Variable names (ALIGN WITH CENTERING)
  # -------------------------------
  if (object$center) {
    x_var <- "x_center"
    z_var <- "z_center"
  } else {
    x_var <- object$iv
    z_var <- object$moderator
  }

  if (is.null(robust)) {
    robust <- object$robust_se
  }

  # ✅ Use original model
  model <- object$model
  b <- coef(model)

  vc <- if (robust) {
    sandwich::vcovHC(model, type = "HC3")
  } else {
    vcov(model)
  }

  # ✅ Centering constant from model (exact)
  zc <- data[[z_var]]
  z  <- data[[object$moderator]]

  z_mean <- mean(z - zc, na.rm = TRUE)

  # ✅ Recover RAW-scale coefficients (PROCESS logic)

  b1 <- b[x_var] - b["int_term"] * z_mean


  b3 <- b["int_term"]



  # Transform variance-covariance matrix

  var_xc  <- vc[x_var, x_var]
  var_int <- vc["int_term", "int_term"]
  cov_xc_int <- vc[x_var, "int_term"]


  var_b1 <- var_xc + z_mean^2 * var_int - 2 * z_mean * cov_xc_int
  var_b3 <- var_int
  cov_b1b3 <- cov_xc_int - z_mean * var_int

  # ✅ Critical value

   tcrit <- qt(1 - alpha / 2, df = df.residual(model))


  # ✅ ANALYTIC J–N SOLUTION (raw scale)
  A <- b3^2 - tcrit^2 * var_b3
  B <- 2 * b1 * b3 - 2 * tcrit^2 * cov_b1b3
  C <- b1^2 - tcrit^2 * var_b1

  disc <- B^2 - 4 * A * C

  if (disc < 0) {
    message("\nNo Johnson-Neyman regions found.\n\n")
    return(NULL)
  }

  z1_raw <- (-B - sqrt(disc)) / (2 * A)
  z2_raw <- (-B + sqrt(disc)) / (2 * A)

  z_original <- sort(c(z1_raw, z2_raw))

  # ✅ Convert back to CENTERED scale for your output
  z_centered <- z_original - z_mean

  results <- data.frame(
    Region = c("Lower", "Upper"),
    z_centered = round(z_centered, 3),
    z_original = round(z_original, 4)
  )

  # -------------------------------
  # 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)


}
