#' Compute a Process Capability Index
#'
#' Computes a single process capability or performance index from the supplied
#' parameters.
#'
#' @details
#' The twelve supported indices are:
#' \describe{
#'   \item{Cp}{\eqn{(USL - LSL) / (6\sigma)}}
#'   \item{Cpk}{\eqn{\min\bigl((USL-\mu)/(3\sigma),\;(\mu-LSL)/(3\sigma)\bigr)}}
#'   \item{Cpu}{\eqn{(USL - \mu) / (3\sigma)}}
#'   \item{Cpl}{\eqn{(\mu - LSL) / (3\sigma)}}
#'   \item{Cpm}{\eqn{(USL - LSL) / \bigl(6\sqrt{\sigma^2+(\mu-T)^2}\bigr)}}
#'   \item{Cpmk}{\eqn{\min\bigl((USL-\mu),\;(\mu-LSL)\bigr) /
#'         \bigl(3\sqrt{\sigma^2+(\mu-T)^2}\bigr)}}
#'   \item{Pp}{\eqn{(USL - LSL) / (6s)}}
#'   \item{Ppk}{\eqn{\min\bigl((USL-\bar x)/(3s),\;(\bar x-LSL)/(3s)\bigr)}}
#'   \item{Ppu}{\eqn{(USL - \bar x) / (3s)}}
#'   \item{Ppl}{\eqn{(\bar x - LSL) / (3s)}}
#'   \item{Z}{\eqn{\min\bigl((USL-\mu)/\sigma,\;(\mu-LSL)/\sigma\bigr)}}
#'   \item{Cpy}{\eqn{p / p_0 = \bigl(F(USL)-F(LSL)\bigr) /
#'         \bigl(F(UDL)-F(LDL)\bigr)}}
#' }
#'
#' @param index Character string: one of \code{"Cp"}, \code{"Cpk"},
#'   \code{"Cpu"}, \code{"Cpl"}, \code{"Cpm"}, \code{"Cpmk"},
#'   \code{"Pp"}, \code{"Ppk"}, \code{"Ppu"}, \code{"Ppl"},
#'   \code{"Z"}, or \code{"Cpy"}.
#' @param dist A \code{\link{pci_dist}} object (required for \code{"Cpy"};
#'   optional otherwise).
#' @param LSL,USL Lower and upper specification limits.
#' @param target Target value (required for \code{Cpm} and \code{Cpmk}).
#' @param mu,sigma Short-term (within-subgroup) mean and standard deviation.
#' @param xbar,s Long-term (overall) mean and standard deviation.
#' @param LDL,UDL Lower and upper desirable limits for \code{Cpy}.
#' @param p0 Desired yield for \code{Cpy}. If \code{LDL}/\code{UDL} are not
#'   supplied, they are computed from \code{p0} using
#'   \code{\link{compute_desirable_limits}}.
#'
#' @return A single numeric value, or \code{NA} with a warning when a
#'   one-sided index is requested but the relevant specification limit is
#'   \code{NULL}.
#'
#' @examples
#' pci("Cp",  LSL = 57, USL = 63, sigma = 1)            # 1.0
#' pci("Cpk", LSL = 57, USL = 63, mu = 60, sigma = 1)   # 1.0
#' pci("Z",   LSL = 57, USL = 63, mu = 60, sigma = 1)   # 3.0
#'
#' # Generalized Cpy
#' d <- pci_dist_normal(60, 1)
#' pci("Cpy", dist = d, LSL = 57, USL = 63, LDL = 57, UDL = 63) # 1.0
#'
#' @seealso \code{\link{pci_dist}}, \code{\link{pci_ci}}, \code{\link{pci_grid}}
#' @export
pci <- function(index,
                dist   = NULL,
                LSL    = NULL, USL  = NULL, target = NULL,
                mu     = NULL, sigma = NULL,
                xbar   = NULL, s     = NULL,
                LDL    = NULL, UDL   = NULL, p0 = NULL) {

  # --- validate index ---
  valid_indices <- c("Cp", "Cpk", "Cpu", "Cpl", "Cpm", "Cpmk",
                     "Pp", "Ppk", "Ppu", "Ppl", "Z", "Cpy")
  index <- match.arg(index, valid_indices)

  # --- dispatch ---
  switch(index,
    Cp   = .calc_cp(LSL, USL, sigma),
    Cpk  = .calc_cpk(LSL, USL, mu, sigma),
    Cpu  = .calc_cpu(USL, mu, sigma),
    Cpl  = .calc_cpl(LSL, mu, sigma),
    Cpm  = .calc_cpm(LSL, USL, mu, sigma, target),
    Cpmk = .calc_cpmk(LSL, USL, mu, sigma, target),
    Pp   = .calc_pp(LSL, USL, s),
    Ppk  = .calc_ppk(LSL, USL, xbar, s),
    Ppu  = .calc_ppu(USL, xbar, s),
    Ppl  = .calc_ppl(LSL, xbar, s),
    Z    = .calc_z(LSL, USL, mu, sigma),
    Cpy  = .calc_cpy(dist, LSL, USL, LDL, UDL, p0)
  )
}

# ==================================================================
# Internal calculators
# ==================================================================

.calc_cp <- function(LSL, USL, sigma) {
  require_param(LSL, "LSL", "Cp")
  require_param(USL, "USL", "Cp")
  require_param(sigma, "sigma", "Cp")
  validate_spec_limits(LSL, USL)
  validate_positive(sigma, "sigma")
  (USL - LSL) / (6 * sigma)
}

.calc_cpk <- function(LSL, USL, mu, sigma) {
  require_param(LSL, "LSL", "Cpk")
  require_param(USL, "USL", "Cpk")
  require_param(mu, "mu", "Cpk")
  require_param(sigma, "sigma", "Cpk")
  validate_spec_limits(LSL, USL)
  validate_positive(sigma, "sigma")
  min((USL - mu) / (3 * sigma), (mu - LSL) / (3 * sigma))
}

.calc_cpu <- function(USL, mu, sigma) {
  require_param(USL, "USL", "Cpu")
  require_param(mu, "mu", "Cpu")
  require_param(sigma, "sigma", "Cpu")
  validate_positive(sigma, "sigma")
  (USL - mu) / (3 * sigma)
}

.calc_cpl <- function(LSL, mu, sigma) {
  require_param(LSL, "LSL", "Cpl")
  require_param(mu, "mu", "Cpl")
  require_param(sigma, "sigma", "Cpl")
  validate_positive(sigma, "sigma")
  (mu - LSL) / (3 * sigma)
}

.calc_cpm <- function(LSL, USL, mu, sigma, target) {
  require_param(LSL, "LSL", "Cpm")
  require_param(USL, "USL", "Cpm")
  require_param(mu, "mu", "Cpm")
  require_param(sigma, "sigma", "Cpm")
  require_param(target, "target", "Cpm")
  validate_spec_limits(LSL, USL)
  validate_positive(sigma, "sigma")
  denom <- 6 * sqrt(sigma^2 + (mu - target)^2)
  (USL - LSL) / denom
}

.calc_cpmk <- function(LSL, USL, mu, sigma, target) {
  require_param(LSL, "LSL", "Cpmk")
  require_param(USL, "USL", "Cpmk")
  require_param(mu, "mu", "Cpmk")
  require_param(sigma, "sigma", "Cpmk")
  require_param(target, "target", "Cpmk")
  validate_spec_limits(LSL, USL)
  validate_positive(sigma, "sigma")
  tau <- 3 * sqrt(sigma^2 + (mu - target)^2)
  min((USL - mu) / tau, (mu - LSL) / tau)
}

.calc_pp <- function(LSL, USL, s) {
  require_param(LSL, "LSL", "Pp")
  require_param(USL, "USL", "Pp")
  require_param(s, "s", "Pp")
  validate_spec_limits(LSL, USL)
  validate_positive(s, "s")
  (USL - LSL) / (6 * s)
}

.calc_ppk <- function(LSL, USL, xbar, s) {
  require_param(LSL, "LSL", "Ppk")
  require_param(USL, "USL", "Ppk")
  require_param(xbar, "xbar", "Ppk")
  require_param(s, "s", "Ppk")
  validate_spec_limits(LSL, USL)
  validate_positive(s, "s")
  min((USL - xbar) / (3 * s), (xbar - LSL) / (3 * s))
}

.calc_ppu <- function(USL, xbar, s) {
  require_param(USL, "USL", "Ppu")
  require_param(xbar, "xbar", "Ppu")
  require_param(s, "s", "Ppu")
  validate_positive(s, "s")
  (USL - xbar) / (3 * s)
}

.calc_ppl <- function(LSL, xbar, s) {
  require_param(LSL, "LSL", "Ppl")
  require_param(xbar, "xbar", "Ppl")
  require_param(s, "s", "Ppl")
  validate_positive(s, "s")
  (xbar - LSL) / (3 * s)
}

.calc_z <- function(LSL, USL, mu, sigma) {
  require_param(LSL, "LSL", "Z")
  require_param(USL, "USL", "Z")
  require_param(mu, "mu", "Z")
  require_param(sigma, "sigma", "Z")
  validate_spec_limits(LSL, USL)
  validate_positive(sigma, "sigma")
  min((USL - mu) / sigma, (mu - LSL) / sigma)
}

.calc_cpy <- function(dist, LSL, USL, LDL, UDL, p0) {
  if (is.null(dist) || !inherits(dist, "pci_dist")) {
    stop("Index 'Cpy' requires a 'pci_dist' object via the 'dist' argument.")
  }
  require_param(LSL, "LSL", "Cpy")
  require_param(USL, "USL", "Cpy")
  validate_spec_limits(LSL, USL)

  cdf_fn <- dist$cdf_fn

  # Compute actual yield
  p <- cdf_fn(USL) - cdf_fn(LSL)

  # Determine desirable limits
  if (!is.null(LDL) && !is.null(UDL)) {
    if (UDL <= LDL) {
      stop("UDL must be greater than LDL.")
    }
    p0_val <- cdf_fn(UDL) - cdf_fn(LDL)
  } else if (!is.null(p0)) {
    validate_p0(p0)
    limits <- compute_desirable_limits(dist, p0)
    p0_val <- p0
  } else {
    stop("Index 'Cpy' requires either 'LDL'/'UDL' or 'p0' to be specified.")
  }

  if (p0_val == 0) {
    warning("Desired yield (p0) is 0; Cpy is undefined.")
    return(NA_real_)
  }

  p / p0_val
}
