#' Goodness-of-Fit Testing for Type-II Censored Samples
#'
#' Performs goodness-of-fit tests for arbitrary user-specified continuous parametric distributions
#' under Type-II right- or left-censoring using the Malmquist and Michael-Schucany transformations.
#'
#' @param data Numeric vector of observed order statistics, length r.
#' @param n Total sample size (n >= length(data)).
#' @param pdf Probability density function \code{f(x, params)}.
#' @param cdf Cumulative distribution function \code{F(x, params)}.
#' @param sf Survival function \code{S(x, params)}. If \code{NULL}, computed as \code{1 - cdf(x, params)}.
#' @param params Named list/vector of fixed parameter values (if \code{estimate = FALSE}) or starting values for MLE (if \code{estimate = TRUE}).
#' @param estimate Logical; \code{TRUE} for composite hypothesis (unknown parameters estimated via MLE), \code{FALSE} for simple hypothesis.
#' @param censoring Type of censoring: \code{"right"} or \code{"left"}.
#' @param transform Transformations to compute: \code{"both"}, \code{"malmquist"}, or \code{"ms"}.
#' @param statistics Subset of statistics to compute: \code{"all"}, \code{"direct7"}, \code{"complete_ad"}, \code{"composite_mle"}, or \code{"direct_censored"}.
#' @param pvalue_method Method for p-values: \code{"auto"}, \code{"distribution_free"}, or \code{"parametric_bootstrap"}.
#' @param nsim Number of Monte Carlo or bootstrap simulation replicates.
#' @param seed Optional random seed.
#' @param optim_control Optional control list passed to \code{\link[stats]{optim}}.
#'
#' @return An S3 object of class \code{"gofmalm"}.
#' @export
#'
#' @examples
#' # Simple hypothesis test on Uniform(0,1) with r=5, n=10
#' data_unif <- c(0.03, 0.06, 0.10, 0.11, 0.13)
#' res <- gof_censored(
#'   data = data_unif, n = 10,
#'   pdf = dunif, cdf = punif,
#'   estimate = FALSE, pvalue_method = "distribution_free",
#'   nsim = 500, seed = 123
#' )
#' print(res)
gof_censored <- function(data,
                         n,
                         pdf,
                         cdf,
                         sf = NULL,
                         params = NULL,
                         estimate = TRUE,
                         censoring = c("right", "left"),
                         transform = c("both", "malmquist", "ms"),
                         statistics = c("all", "direct7", "complete_ad", "composite_mle", "direct_censored"),
                         pvalue_method = c("auto", "distribution_free", "parametric_bootstrap"),
                         nsim = 2000,
                         seed = NULL,
                         optim_control = list()) {
  call_obj <- match.call()

  censoring <- match.arg(censoring)
  transform <- match.arg(transform)
  statistics <- match.arg(statistics)
  pvalue_method <- match.arg(pvalue_method)

  if (!is.numeric(data) || length(data) == 0) {
    stop("`data` must be a non-empty numeric vector.")
  }
  if (is.unsorted(data)) {
    stop("`data` must be sorted ascending.")
  }
  r <- length(data)
  if (!is.numeric(n) || length(n) != 1 || n < r) {
    stop("`n` must be an integer >= length(data).")
  }
  if (!is.function(pdf) || !is.function(cdf)) {
    stop("`pdf` and `cdf` must be functions.")
  }
  if (is.null(sf)) {
    sf <- function(x, p = NULL) 1 - eval_func(cdf, x, p)
  }

  if (pvalue_method == "auto") {
    pvalue_method <- if (estimate) "parametric_bootstrap" else "distribution_free"
  }

  # Estimate or check parameters
  theta_hat <- params
  optim_conv <- 0

  if (estimate) {
    if (is.null(params) || length(params) == 0) {
      stop("`params` starting values must be supplied when `estimate = TRUE`.")
    }
    fit <- fit_mle(data = data, n = n, pdf = pdf, cdf = cdf, sf = sf,
                   params = params, censoring = censoring, optim_control = optim_control)
    theta_hat <- fit$par
    optim_conv <- fit$convergence
  } else {
    if (is.null(params)) {
      params <- list()
    }
  }

  # Probability Integral Transform (PIT)
  eps <- .Machine$double.eps^0.5
  u_pit <- tryCatch(eval_func(cdf, data, theta_hat), error = function(e) rep(NA_real_, r))
  if (any(is.na(u_pit))) {
    stop("Evaluation of `cdf(data, params)` returned NA/NaN values.")
  }
  u_pit <- pmin(pmax(u_pit, eps), 1 - eps)

  # Prepare uniform samples
  u_ms <- transform_ms(u_pit, n)
  u_malmquist <- transform_malmquist(u_pit, n, censoring = censoring)$W

  # Compute statistics & p-values
  stats_df <- data.frame(
    statistic = character(0),
    value = numeric(0),
    p_value = numeric(0),
    method = character(0),
    stringsAsFactors = FALSE
  )

  if (!estimate) {
    # Simple hypothesis mode
    edf_stats <- stat_edf_censored(u_pit, n, u_ms = u_ms, u_malmquist = u_malmquist)
    dir_stats <- stat_direct_censored(u_pit, n)
    all_raw <- c(edf_stats, dir_stats)

    p_vals <- pvalue_distribution_free(u_pit, n, r, censoring = censoring, nsim = nsim, seed = seed)

    stats_df <- data.frame(
      statistic = names(all_raw),
      value = as.numeric(all_raw),
      p_value = as.numeric(p_vals),
      method = "Monte Carlo (distribution-free)",
      stringsAsFactors = FALSE
    )
  } else {
    # Composite hypothesis mode
    comp_res <- pvalue_parametric_bootstrap(
      data = data, n = n, pdf = pdf, cdf = cdf, sf = sf,
      params_hat = theta_hat, censoring = censoring, transform = transform,
      nsim = nsim, seed = seed, optim_control = optim_control
    )

    rows <- list()
    if (transform %in% c("both", "ms")) {
      rows[[length(rows) + 1]] <- data.frame(
        statistic = "mW2",
        value = comp_res$obs_ms$stat_W2,
        p_value = comp_res$p_values$mW2,
        method = "Parametric Bootstrap (MS)",
        stringsAsFactors = FALSE
      )
      rows[[length(rows) + 1]] <- data.frame(
        statistic = "mA2",
        value = comp_res$obs_ms$stat_A2,
        p_value = comp_res$p_values$mA2,
        method = "Parametric Bootstrap (MS)",
        stringsAsFactors = FALSE
      )
    }
    if (transform %in% c("both", "malmquist")) {
      rows[[length(rows) + 1]] <- data.frame(
        statistic = "lW2",
        value = comp_res$obs_malmquist$stat_W2,
        p_value = comp_res$p_values$lW2,
        method = "Parametric Bootstrap (Malmquist)",
        stringsAsFactors = FALSE
      )
      rows[[length(rows) + 1]] <- data.frame(
        statistic = "lA2",
        value = comp_res$obs_malmquist$stat_A2,
        p_value = comp_res$p_values$lA2,
        method = "Parametric Bootstrap (Malmquist)",
        stringsAsFactors = FALSE
      )
    }

    stats_df <- do.call(rbind, rows)
  }

  res <- list(
    call = call_obj,
    n = n,
    r = r,
    censoring = censoring,
    estimate = estimate,
    theta_hat = theta_hat,
    optim_convergence = optim_conv,
    u_direct = u_pit,
    u_ms = u_ms,
    u_malmquist = u_malmquist,
    statistics = stats_df,
    settings = list(
      transform = transform,
      pvalue_method = pvalue_method,
      nsim = nsim
    )
  )

  class(res) <- "gofmalm"
  return(res)
}

#' @export
print.gofmalm <- function(x, ...) {
  cat("\n--- Goodness-of-Fit Test for Type-II Censored Samples ---\n\n")
  cat("Sample size (n):", x$n, "\n")
  cat("Observed failures (r):", x$r, "\n")
  cat("Censoring type:", x$censoring, "\n")
  cat("Hypothesis:", if (x$estimate) "Composite (parameters estimated)" else "Simple (fixed parameters)", "\n")

  if (x$estimate) {
    cat("\nEstimated Parameters:\n")
    print(unlist(x$theta_hat))
  }

  cat("\nTest Statistics:\n")
  print(x$statistics, row.names = FALSE)
  cat("\n")
  invisible(x)
}

#' @export
summary.gofmalm <- function(object, ...) {
  cat("\n=== Goodness-of-Fit Summary (gofmalm) ===\n")
  print.gofmalm(object, ...)
}

#' @export
plot.gofmalm <- function(x, ...) {
  r <- x$r
  theoretical_q <- (1:r - 0.5) / r

  old_par <- graphics::par(mfrow = c(1, 2))
  on.exit(graphics::par(old_par), add = TRUE)

  graphics::plot(theoretical_q, x$u_ms,
                 xlab = "Theoretical Uniform Quantiles",
                 ylab = "Transformed Sample (MS)",
                 main = "PP-Plot: Michael & Schucany",
                 pch = 19, col = "blue", xlim = c(0, 1), ylim = c(0, 1))
  graphics::abline(0, 1, lty = 2, col = "red")

  graphics::plot(theoretical_q, x$u_malmquist,
                 xlab = "Theoretical Uniform Quantiles",
                 ylab = "Transformed Sample (Malmquist)",
                 main = "PP-Plot: Malmquist",
                 pch = 19, col = "darkgreen", xlim = c(0, 1), ylim = c(0, 1))
  graphics::abline(0, 1, lty = 2, col = "red")

  invisible(x)
}
