#' Compute Bootstrap Confidence Intervals for Process Capability Indices (Interval-Censored Data)
#'
#' Runs parametric or non-parametric bootstrapping to compute confidence intervals
#' for generalized process capability indices at multiple significance levels (90%, 95%, 99%)
#' under interval-censored data.
#'
#' @param fit A \code{gpcifit_censor} object returned by \code{\link{capability_censor}}.
#' @param B Number of bootstrap replicates (default is 2000).
#' @param alpha Vector of significance levels (default is \code{c(0.10, 0.05, 0.01)} corresponding to 90%, 95%, and 99% confidence levels).
#' @param method Confidence interval method. Choices are \code{"percentile"}, \code{"normal"},
#'   \code{"basic"}, \code{"BCa"}, \code{"BCp"} (Bias-Corrected Percentile), or \code{"studentized"}.
#' @param type Bootstrap type: \code{"parametric"} or \code{"nonparametric"}.
#' @param parallel Logical. If \code{TRUE}, bootstrap replicates are run in parallel.
#' @param ncpus Integer. Number of CPUs to use if \code{parallel = TRUE}.
#' @return An object of class \code{gpci_ci_censor} containing a tidy data frame of confidence intervals,
#'   the raw bootstrap output, and the original fit object.
#' @export
#' @examples
#' \dontrun{
#' dist_norm <- dist_normal()
#' left <- c(9.5, 10.2, 8.8, 11.1, 9.9)
#' right <- c(10.5, 11.2, 9.8, 12.1, 10.9)
#' fit <- capability_censor(left, right, dist_norm, USL = 13, LSL = 7)
#' ci <- boot_ci_censor(fit, B = 500, alpha = c(0.10, 0.05, 0.01), method = "percentile")
#' print(ci)
#' }
boot_ci_censor <- function(fit, B = 2000, alpha = c(0.10, 0.05, 0.01),
                           method = c("percentile", "normal", "basic", "BCa", "BCp", "studentized"),
                           type = c("parametric", "nonparametric"),
                           parallel = FALSE, ncpus = 1) {
  method <- match.arg(method)
  type <- match.arg(type)
  
  if (!inherits(fit, "gpcifit_censor")) {
    stop("fit must be of class 'gpcifit_censor'")
  }
  
  if (is.null(fit$data_left) || is.null(fit$data_right)) {
    stop("Bootstrap confidence intervals require interval-censored data to resample.")
  }
  
  n <- length(fit$data_left)
  indices <- fit$indices
  
  p_type <- "no"
  if (parallel) {
    p_type <- if (.Platform$OS.type == "windows") "snow" else "multicore"
  }
  
  # Function to generate nonparametric bootstrap samples for interval-censored pairs
  generate_bootstrap_sample <- function(data_left, data_right) {
    n_samp <- length(data_left)
    sample_idx <- sample(1:n_samp, n_samp, replace = TRUE)
    list(left = data_left[sample_idx], right = data_right[sample_idx])
  }
  
  # Function to generate parametric bootstrap samples
  generate_parametric_sample <- function(n_samp, dist) {
    u <- stats::runif(n_samp)
    samples <- tryCatch(do.call(dist$quantile, c(list(u), dist$params)), error = function(e) {
      mu <- mean(unlist(dist$params))
      stats::qnorm(u, mu, 1)
    })
    samples[is.na(samples) | !is.finite(samples)] <- mean(unlist(dist$params))
    
    vcov_mat <- attr(dist, "vcov")
    interval_width <- 0.1
    if (!is.null(vcov_mat) && is.matrix(vcov_mat) && nrow(vcov_mat) > 0) {
      diag_max <- suppressWarnings(max(diag(vcov_mat), na.rm = TRUE))
      if (is.finite(diag_max) && diag_max > 0) {
        interval_width <- 0.1 * sqrt(diag_max)
      }
    }
    interval_width <- max(interval_width, 0.01)
    list(left = samples - interval_width / 2, right = samples + interval_width / 2)
  }
  
  # Jackknife standard error vector for studentized bootstrap
  jackknife_se_vector <- function(left, right) {
    n_y <- length(left)
    jack_est <- matrix(0, nrow = n_y, ncol = length(indices))
    for (i in 1:n_y) {
      left_j <- left[-i]
      right_j <- right[-i]
      fit_j <- tryCatch({
        capability_censor(
          data_left = left_j, data_right = right_j,
          distribution = fit$distribution,
          USL = fit$USL, LSL = fit$LSL, target = fit$target,
          indices = indices, u = fit$options$u, v = fit$options$v,
          mode = fit$mode, fit = TRUE,
          C0 = fit$options$C0, C1 = fit$options$C1, C2 = fit$options$C2,
          tolerance_t = fit$options$tolerance_t, P0 = fit$options$P0,
          LDL = fit$options$LDL, UDL = fit$options$UDL
        )
      }, error = function(e) NULL)
      
      if (!is.null(fit_j)) {
        jack_est[i, ] <- fit_j$estimates
      }
    }
    col_vars <- apply(jack_est, 2, function(col) {
      m_col <- mean(col, na.rm = TRUE)
      (n_y - 1) / n_y * sum((col - m_col)^2, na.rm = TRUE)
    })
    sqrt(pmax(1e-12, col_vars))
  }
  
  # Statistic function for boot::boot
  stat_fn <- function(d, i) {
    if (type == "nonparametric") {
      boot_data <- generate_bootstrap_sample(fit$data_left, fit$data_right)
      left_boot <- boot_data$left
      right_boot <- boot_data$right
    } else {
      boot_data <- generate_parametric_sample(n, fit$distribution)
      left_boot <- boot_data$left
      right_boot <- boot_data$right
    }
    
    fit_star <- tryCatch({
      capability_censor(
        data_left = left_boot, data_right = right_boot,
        distribution = fit$distribution,
        USL = fit$USL, LSL = fit$LSL, target = fit$target,
        indices = indices, u = fit$options$u, v = fit$options$v,
        mode = fit$mode, fit = TRUE,
        C0 = fit$options$C0, C1 = fit$options$C1, C2 = fit$options$C2,
        tolerance_t = fit$options$tolerance_t, P0 = fit$options$P0,
        LDL = fit$options$LDL, UDL = fit$options$UDL
      )
    }, error = function(e) NULL)
    
    if (is.null(fit_star)) {
      return(rep(0, length(indices)))
    }
    
    estimates <- fit_star$estimates
    estimates[is.na(estimates) | !is.finite(estimates)] <- 0
    
    if (method == "studentized") {
      se <- jackknife_se_vector(left_boot, right_boot)
      se[is.na(se) | !is.finite(se)] <- 0
      return(c(estimates, se^2))
    }
    
    return(estimates)
  }
  
  # Run bootstrap
  boot_out <- boot::boot(
    data = data.frame(left = fit$data_left, right = fit$data_right),
    statistic = stat_fn,
    R = B,
    parallel = p_type,
    ncpus = ncpus
  )
  
  ci_list <- list()
  for (idx_name in indices) {
    idx_num <- which(indices == idx_name)
    point_est <- fit$estimates[idx_name]
    reps <- boot_out$t[, idx_num]
    reps_clean <- reps[is.finite(reps)]
    if (length(reps_clean) < 5) reps_clean <- rep(point_est, 10)
    
    for (a in alpha) {
      conf <- 1 - a
      
      bounds <- tryCatch({
        if (method == "BCp") {
          z0 <- stats::qnorm(mean(reps_clean < point_est, na.rm = TRUE))
          if (!is.finite(z0)) z0 <- 0
          z_alpha_lower <- stats::qnorm(a / 2)
          z_alpha_upper <- stats::qnorm(1 - a / 2)
          p_lower <- stats::pnorm(2 * z0 + z_alpha_lower)
          p_upper <- stats::pnorm(2 * z0 + z_alpha_upper)
          stats::quantile(reps_clean, probs = c(p_lower, p_upper), na.rm = TRUE)
        } else {
          b_type <- switch(method,
            "normal" = "norm",
            "basic" = "basic",
            "percentile" = "perc",
            "studentized" = "stud",
            "BCa" = "bca"
          )
          
          ci_res <- boot::boot.ci(boot_out, conf = conf, type = b_type, index = idx_num)
          switch(b_type,
            "norm" = ci_res$normal[2:3],
            "basic" = ci_res$basic[4:5],
            "perc" = ci_res$percent[4:5],
            "stud" = ci_res$student[4:5],
            "bca" = ci_res$bca[4:5]
          )
        }
      }, error = function(e) {
        stats::quantile(reps_clean, probs = c(a / 2, 1 - a / 2), na.rm = TRUE)
      })
      
      bounds[is.na(bounds) | !is.finite(bounds)] <- point_est
      
      ci_list[[length(ci_list) + 1]] <- data.frame(
        index = idx_name,
        estimate = point_est,
        method = method,
        type = type,
        alpha = a,
        conf_level = paste0(round((1 - a) * 100, 1), "%"),
        lower = bounds[1],
        upper = bounds[2],
        width = bounds[2] - bounds[1],
        stringsAsFactors = FALSE
      )
    }
  }
  
  ci_table <- do.call(rbind, ci_list)
  rownames(ci_table) <- NULL
  
  structure(
    list(
      ci_table = ci_table,
      boot_out = boot_out,
      fit = fit
    ),
    class = "gpci_ci_censor"
  )
}

#' @rdname boot_ci_censor
#' @export
gpci_boot_censor <- boot_ci_censor

#' Print Method for gpci_ci_censor
#'
#' @param x An object of class \code{gpci_ci_censor}.
#' @param ... Additional print arguments.
#' @export
print.gpci_ci_censor <- function(x, ...) {
  cat("--- Bootstrap Confidence Intervals (Interval-Censored Data) ---\n")
  cat("Bootstrap Type:   ", x$ci_table$type[1], "\n")
  cat("CI Method:        ", x$ci_table$method[1], "\n")
  cat("Replicates (B):   ", x$boot_out$R, "\n\n")
  
  print(round_df_censor(x$ci_table, 4))
  invisible(x)
}

#' Summary Method for gpci_ci_censor
#'
#' @param object An object of class \code{gpci_ci_censor}.
#' @param ... Additional arguments.
#' @export
summary.gpci_ci_censor <- function(object, ...) {
  print(object, ...)
  invisible(object)
}
