#' Gamma Process Step-Stress Accelerated Degradation Model Estimation
#'
#' Fits a Gamma process step-stress accelerated degradation model using Maximum Likelihood Estimation (MLE)
#' or Bayesian Markov Chain Monte Carlo (MCMC) based on the Birnbaum-Saunders approximation for stress elevation times
#' as described in Pan and Balakrishnan (2010).
#'
#' @param data A data frame containing inspection times in the first column and unit degradation measurements in subsequent columns,
#'   or a long-format data frame with columns \code{unit}, \code{time}, \code{degradation}.
#' @param stress_levels Vector of stress levels S = (S1, S2, ..., SK).
#' @param thresholds Vector of degradation thresholds (omega_1, ..., omega_K-1) at which stress levels are elevated.
#' @param model Acceleration model: \code{"arrhenius"} or \code{"power"}.
#' @param method Estimation method: \code{"mle"} or \code{"mcmc"}.
#' @param init Initial parameter values \code{c(a, b, u)}.
#' @param n_iter Number of MCMC iterations (default: 5000).
#' @param burnin Number of burn-in iterations (default: 1000).
#' @param thin Thinning interval for MCMC (default: 1).
#'
#' @return An object of class \code{"ssadfit"} containing fit results, parameter estimates, log-likelihood, MCMC samples (if applicable), and metadata.
#'
#' @references
#' Pan, Z., & Balakrishnan, N. (2010). Multiple-Steps Step-Stress Accelerated Degradation Modeling Based on Wiener and Gamma Processes.
#' \emph{Communications in Statistics - Simulation and Computation}, 39(7), 1384-1402. \doi{10.1080/03610918.2010.496060}
#'
#' @export
#' @examples
#' data(gamma_arrhenius)
#' fit_g <- ssad_gamma(
#'   data = gamma_arrhenius,
#'   stress_levels = c(45, 65, 85),
#'   thresholds = c(90, 160),
#'   model = "arrhenius",
#'   method = "mle"
#' )
#' print(fit_g)
ssad_gamma <- function(data, stress_levels = c(45, 65, 85), thresholds = c(90, 160),
                       model = c("arrhenius", "power"), method = c("mle", "mcmc"),
                       init = NULL, n_iter = 5000, burnin = 1000, thin = 1) {
  model <- match.arg(model)
  method <- match.arg(method)
  
  parsed_data <- parse_ssad_data(data)
  units_data <- parsed_data$units
  
  K <- length(stress_levels)
  if (length(thresholds) != K - 1) {
    stop("Length of thresholds must equal length(stress_levels) - 1.")
  }
  
  tau_mat <- estimate_ssad_taus(units_data, thresholds)
  
  eval_acc <- function(S, m_type, par) {
    if (is.function(m_type)) return(m_type(S, par))
    a <- par[1]; b <- par[2]
    if (m_type == "arrhenius") return(exp(a + b / (273 + S)))
    if (m_type == "power") return(a * (S^b))
    stop("Unsupported acceleration model.")
  }
  
  loglik_fn <- function(par) {
    a <- par[1]; b <- par[2]; u <- par[3]
    if (is.na(u) || u <= 1e-6) return(-1e12)
    
    v_k <- sapply(stress_levels, function(s) eval_acc(s, model, c(a, b)))
    if (any(is.na(v_k)) || any(v_k <= 1e-8)) return(-1e12)
    
    logL <- 0
    N <- length(units_data)
    
    # 1. Stress elevation time likelihood g(tau_i) via Birnbaum-Saunders approximation
    d_omega <- c(thresholds[1], diff(thresholds))
    for (i in seq_len(N)) {
      tau_i <- tau_mat[i, ]
      d_tau <- c(tau_i[1], diff(tau_i))
      for (k in seq_len(K - 1)) {
        dtk <- max(d_tau[k], 1e-6)
        dok <- d_omega[k]
        vk <- v_k[k]
        
        ratio <- (vk * dtk) / (u * dok)
        if (is.na(ratio) || ratio <= 1e-10) return(-1e12)
        
        term1 <- log(vk / (2 * sqrt(2 * pi * u * dok)))
        term2 <- log((ratio)^(-0.5) + (ratio)^(-1.5))
        term3 <- -(u * dok / 2) * (ratio - 2 + 1 / ratio)
        
        logL <- logL + term1 + term2 + term3
      }
    }
    
    # 2. Degradation increment likelihood f(dx_{i,j}) via Gamma process
    for (i in seq_len(N)) {
      u_df <- units_data[[i]]
      t_vec <- u_df$time
      x_vec <- u_df$degradation
      tau_i <- c(0, tau_mat[i, ], max(t_vec) * 10)
      
      M <- length(t_vec)
      for (j in seq_len(M - 1)) {
        t1 <- t_vec[j]; t2 <- t_vec[j + 1]
        dx <- max(x_vec[j + 1] - x_vec[j], 1e-8)
        
        # Calculate shape parameter upsilon_ij based on stress level overlaps
        upsilon_ij <- 0
        for (k in seq_len(K)) {
          t_start_k <- tau_i[k]
          t_end_k <- tau_i[k + 1]
          overlap <- max(0, min(t2, t_end_k) - max(t1, t_start_k))
          if (overlap > 0) {
            upsilon_ij <- upsilon_ij + v_k[k] * overlap
          }
        }
        
        upsilon_ij <- max(upsilon_ij, 1e-6)
        
        term1 <- upsilon_ij * log(u) - lgamma(upsilon_ij)
        term2 <- (upsilon_ij - 1) * log(dx) - u * dx
        
        logL <- logL + term1 + term2
      }
    }
    
    if (is.na(logL) || is.nan(logL)) return(-1e12)
    return(logL)
  }
  
  if (is.null(init)) {
    if (model == "arrhenius") {
      init <- c(a = 6.7, b = -2500, u = 4.0)
    } else {
      init <- c(a = 3e-3, b = 1.2, u = 4.0)
    }
  }
  
  if (method == "mle") {
    neg_loglik <- function(p) -loglik_fn(p)
    opt <- stats::optim(init, neg_loglik, method = "Nelder-Mead", control = list(maxit = 2000))
    opt <- stats::optim(opt$par, neg_loglik, method = "BFGS", hessian = TRUE, control = list(maxit = 2000))
    
    par_mle <- opt$par
    names(par_mle) <- c("a", "b", "u")
    
    se_mle <- rep(NA, 3)
    if (!is.null(opt$hessian) && !any(is.na(opt$hessian))) {
      cov_mat <- tryCatch(solve(opt$hessian), error = function(e) NULL)
      if (!is.null(cov_mat)) {
        se_mle <- sqrt(pmax(diag(cov_mat), 0))
      }
    }
    names(se_mle) <- c("a", "b", "u")
    
    res <- list(
      par = par_mle,
      se = se_mle,
      loglik = -opt$value,
      process = "gamma",
      model = model,
      method = "mle",
      convergence = opt$convergence,
      stress_levels = stress_levels,
      thresholds = thresholds,
      tau_mat = tau_mat,
      units_data = units_data
    )
  } else { # MCMC
    chain <- matrix(0, nrow = n_iter, ncol = 3)
    colnames(chain) <- c("a", "b", "u")
    curr_par <- init
    curr_loglik <- loglik_fn(curr_par)
    
    scale_vec <- abs(init) * 0.05
    scale_vec[scale_vec == 0] <- 0.01
    
    acc_count <- 0
    for (iter in seq_len(n_iter)) {
      prop_par <- curr_par + stats::rnorm(3, mean = 0, sd = scale_vec)
      if (prop_par[3] > 1e-6) {
        prop_loglik <- loglik_fn(prop_par)
        
        prior_curr <- stats::dnorm(curr_par[1], 0, 100, log = TRUE) +
                      stats::dnorm(curr_par[2], 0, 10000, log = TRUE) - log(curr_par[3])
        prior_prop <- stats::dnorm(prop_par[1], 0, 100, log = TRUE) +
                      stats::dnorm(prop_par[2], 0, 10000, log = TRUE) - log(prop_par[3])
        
        log_alpha <- (prop_loglik + prior_prop) - (curr_loglik + prior_curr)
        if (!is.na(log_alpha) && log(stats::runif(1)) < log_alpha) {
          curr_par <- prop_par
          curr_loglik <- prop_loglik
          acc_count <- acc_count + 1
        }
      }
      chain[iter, ] <- curr_par
    }
    
    post_chain <- chain[(burnin + 1):n_iter, , drop = FALSE]
    if (thin > 1) {
      post_chain <- post_chain[seq(1, nrow(post_chain), by = thin), , drop = FALSE]
    }
    
    par_mean <- colMeans(post_chain)
    par_sd <- apply(post_chain, 2, stats::sd)
    par_quant <- t(apply(post_chain, 2, stats::quantile, probs = c(0.025, 0.5, 0.975)))
    
    res <- list(
      par = par_mean,
      sd = par_sd,
      quantiles = par_quant,
      chain = post_chain,
      acceptance_rate = acc_count / n_iter,
      loglik = loglik_fn(par_mean),
      process = "gamma",
      model = model,
      method = "mcmc",
      stress_levels = stress_levels,
      thresholds = thresholds,
      tau_mat = tau_mat,
      units_data = units_data
    )
  }
  
  class(res) <- "ssadfit"
  return(res)
}
