#' Compute Process Capability Indices (GPCIs)
#'
#' Computes classical and generalized Process Capability Indices for a given dataset or
#' process distribution parameters under uncensored data.
#'
#' @param data Numeric vector of observed process data (optional if distribution is supplied with parameters).
#' @param distribution A \code{gpci_dist} object.
#' @param USL Upper Specification Limit.
#' @param LSL Lower Specification Limit.
#' @param target Target value of process (defaults to midpoint of USL and LSL).
#' @param indices Character vector of index names to evaluate. Default computes all GPCIs:
#'   \code{c("Cpy", "Cp", "Cpk", "Cpu", "Cpl", "Cpm", "Cpmk", "CpTk", "Spmk", "Cpc", "CNpk", "CNpmc", "CNpmkc", "Cp_uv")}.
#' @param u Parameter \code{u} for Vännman's \code{Cp(u,v)} family (default 1).
#' @param v Parameter \code{v} for Vännman's \code{Cp(u,v)} family (default 1).
#' @param mode Mode of computation: \code{"moments"} (mean/sd) or \code{"quantile"} (quantiles).
#' @param fit Logical. If \code{TRUE} (default) and data is supplied, parameters are estimated from data.
#' @param fit_method Parameter estimation method (\code{"mle"}).
#' @param C0,C1,C2 Parameters for tolerance loss function (defaults: 1, 0, 1).
#' @param tolerance_t Process tolerance span (defaults to \code{USL - LSL}).
#' @param P0 Expected yield baseline (default 0.9973002).
#' @param LDL,UDL Lower and upper desired limits.
#' @return A named numeric vector of evaluated GPCI values.
#' @export
#' @examples
#' dist_n <- dist_normal(mean = 10, sd = 1.5)
#' capability(
#'   distribution = dist_n,
#'   USL = 15, LSL = 5, target = 10,
#'   indices = c("Cpy", "Cp", "Cpk", "Cpm", "Cpmk")
#' )
capability <- function(data = NULL, distribution, USL, LSL, target = (USL + LSL) / 2,
                       indices = NULL,
                       u = 1, v = 1, mode = c("moments", "quantile"),
                       fit = TRUE, fit_method = "mle",
                       C0 = 1, C1 = 0, C2 = 1, tolerance_t = USL - LSL,
                       P0 = 0.9973002, LDL = LSL, UDL = USL) {
  mode <- match.arg(mode)
  
  if (is.null(indices)) {
    indices <- c("Cpy", "Cp", "Cpk", "Cpu", "Cpl", "Cpm", "Cpmk",
                 "CpTk", "Spmk", "Cpc", "CNpk", "CNpmc", "CNpmkc", "Cp_uv")
  }
  
  if (!inherits(distribution, "gpci_dist")) {
    stop("distribution must be of class 'gpci_dist'")
  }
  
  if (missing(USL) || missing(LSL)) {
    stop("Both USL and LSL must be specified.")
  }
  
  if (LSL >= USL) {
    stop("LSL must be strictly less than USL.")
  }
  
  dist_fitted <- distribution
  if (fit && !is.null(data)) {
    dist_fitted <- fit_distribution(data, distribution, method = fit_method)
  }
  
  moments <- compute_theoretical_moments(dist_fitted)
  mu <- moments$mean
  sigma <- sqrt(moments$var)
  
  q_00135 <- do.call(dist_fitted$quantile, c(list(0.00135), dist_fitted$params))
  q_05 <- do.call(dist_fitted$quantile, c(list(0.5), dist_fitted$params))
  q_99865 <- do.call(dist_fitted$quantile, c(list(0.99865), dist_fitted$params))
  
  d <- (USL - LSL) / 2
  M_mid <- (USL + LSL) / 2
  
  CM_t <- C0 + C1 * exp(-C2 * tolerance_t)
  if (is.na(CM_t) || CM_t <= 0) CM_t <- 1
  
  F_USL <- do.call(dist_fitted$cdf, c(list(USL), dist_fitted$params))
  F_LSL <- do.call(dist_fitted$cdf, c(list(LSL), dist_fitted$params))
  F_T <- do.call(dist_fitted$cdf, c(list(target), dist_fitted$params))
  F_mu <- do.call(dist_fitted$cdf, c(list(mu), dist_fitted$params))
  
  yield <- max(1e-10, min(1 - 1e-10, F_USL - F_LSL))
  
  mu_mode <- if (mode == "moments") mu else q_05
  sigma_mode <- if (mode == "moments") sigma else max(1e-6, (q_99865 - q_00135) / 6)
  
  res <- numeric(0)
  
  for (idx in indices) {
    val <- switch(idx,
      "Cpy" = yield / P0,
      "Cp" = d / (3 * sigma_mode),
      "Cpk" = min(USL - mu_mode, mu_mode - LSL) / (3 * sigma_mode),
      "Cpu" = (USL - mu_mode) / (3 * sigma_mode),
      "Cpl" = (mu_mode - LSL) / (3 * sigma_mode),
      "Cpm" = d / (3 * sqrt(sigma_mode^2 + (mu_mode - target)^2)),
      "Cpmk" = min(USL - mu_mode, mu_mode - LSL) / (3 * sqrt(sigma_mode^2 + (mu_mode - target)^2)),
      "Cp_uv" = (d - u * abs(mu_mode - M_mid)) / (3 * sqrt(sigma_mode^2 + v * (mu_mode - target)^2)),
      "CpTk" = min(F_USL - F_T, F_T - F_LSL) / (3 * sqrt((F_T - 0.5)^2 + 1/36)),
      "Spmk" = min(F_USL - F_mu, F_mu - F_LSL) / (3 * sqrt((F_mu - 0.5)^2 + 1/36)),
      "Cpc" = yield / P0,
      "CNpk" = min(F_USL - F_mu, F_mu - F_LSL) / (3 * max(1e-4, sigma / (USL - LSL))),
      "CNpmc" = yield / (P0 * CM_t),
      "CNpmkc" = min(F_USL - F_T, F_T - F_LSL) / (P0 * CM_t),
      NA_real_
    )
    res[[idx]] <- val
  }
  
  res
}
