#' Classical Estimation Methods for the Modified Topp-Leone Distribution
#'
#' Point and interval estimation of the shape parameter \code{alpha} of the
#' Modified Topp-Leone (MTL) distribution using classical methods: Maximum Likelihood (MLE),
#' Ordinary Least Squares (OLS), Weighted Least Squares (WLS), Cramér-von Mises (CVM),
#' and Maximum Product of Spacings (MPS).
#'
#' @param x Vector of sample observations (\code{x > 0}).
#' @param conf.level Confidence level for interval estimation (default: 0.95).
#' @param method Estimation method: \code{"mle"}, \code{"ols"}, \code{"wls"}, \code{"cvm"}, \code{"mps"}, or \code{"all"}.
#'
#' @return
#' A list or data frame containing point estimate, standard error, asymptotic confidence interval,
#' log-likelihood, AIC, BIC, CAIC, Kolmogorov-Smirnov test statistic, and associated p-value.
#'
#' @references
#' Singh, B., Tyagi, S., Singh, R. P., and Tyagi, A. (2025). Modified Topp-Leone
#' Distribution: Properties, Classical and Bayesian Estimation with Application to COVID-19
#' and Reliability Data. \emph{Thailand Statistician}, 23(1), 72-96.
#'
#' Cheng, R. C. H., and Amin, N. A. K. (1983). Estimating parameters in continuous univariate
#' distributions with a shifted origin. \emph{Journal of the Royal Statistical Society: Series B}, 45(3), 394-403.
#'
#' Swain, J. J., Venkatraman, S., and Wilson, J. R. (1988). Least-squares estimation of distribution
#' functions in Johnson's translation system. \emph{Journal of Statistical Computation and Simulation}, 29(4), 271-297.
#'
#' @author Shikhar Tyagi, Abhishek Tyagi, Arvind Pandey, Bhupendra Singh, Vrijesh Tripathi
#'
#' @examples
#' set.seed(123)
#' sample_data <- rmtl(n = 50, alpha = 2.0)
#'
#' # MLE Estimation
#' mle_mtl(x = sample_data)
#'
#' # OLS and WLS Estimation
#' ols_mtl(x = sample_data)
#' wls_mtl(x = sample_data)
#'
#' # CVM and MPS Estimation
#' cvm_mtl(x = sample_data)
#' mps_mtl(x = sample_data)
#'
#' # Fit all classical methods simultaneously
#' fit_mtl(x = sample_data, method = "all")
#'
#' @name classical_mtl
#' @rdname classical_mtl
#' @export
mle_mtl <- function(x, conf.level = 0.95) {
  x <- x[!is.na(x) & x > 0]
  n <- length(x)
  if (n == 0) stop("Vector 'x' must contain positive numeric values.")
  
  # Closed-form formula for MLE (Equation 17)
  denom <- sum(log(((1 + x)^2) / (x * (2 + x))))
  alpha_hat <- n / denom
  
  se <- alpha_hat / sqrt(n)
  
  z_crit <- stats::qnorm(1 - (1 - conf.level) / 2)
  ci_lower <- max(1e-5, alpha_hat - z_crit * se)
  ci_upper <- alpha_hat + z_crit * se
  
  loglik <- sum(dmtl(x, alpha_hat, log = TRUE))
  aic <- 2 * 1 - 2 * loglik
  bic <- log(n) * 1 - 2 * loglik
  caic <- aic + (2 * 1 * (1 + 1)) / (n - 1 - 1)
  
  ks_res <- stats::ks.test(x, "pmtl", alpha = alpha_hat)
  
  res <- list(
    method = "Maximum Likelihood Estimation (MLE)",
    estimate = alpha_hat,
    se = se,
    conf.level = conf.level,
    ci = c(Lower = ci_lower, Upper = ci_upper),
    loglik = loglik,
    AIC = aic,
    BIC = bic,
    CAIC = caic,
    KS_stat = as.numeric(ks_res$statistic),
    p.value = ks_res$p.value
  )
  class(res) <- "mtl_fit"
  return(res)
}

#' @rdname classical_mtl
#' @export
ols_mtl <- function(x) {
  x_sort <- sort(x[!is.na(x) & x > 0])
  n <- length(x_sort)
  if (n == 0) stop("Vector 'x' must contain positive numeric values.")
  
  i_seq <- 1:n
  target_p <- i_seq / (n + 1)
  
  obj_fn <- function(alpha) {
    p_fit <- pmtl(x_sort, alpha)
    sum((p_fit - target_p)^2)
  }
  
  init_alpha <- mle_mtl(x_sort)$estimate
  opt <- stats::optim(par = init_alpha, fn = obj_fn, method = "L-BFGS-B", lower = 1e-5)
  alpha_hat <- opt$par
  
  loglik <- sum(dmtl(x_sort, alpha_hat, log = TRUE))
  aic <- 2 - 2 * loglik
  bic <- log(n) - 2 * loglik
  caic <- aic + (4) / (n - 2)
  
  ks_res <- stats::ks.test(x_sort, "pmtl", alpha = alpha_hat)
  
  res <- list(
    method = "Ordinary Least Squares (OLS)",
    estimate = alpha_hat,
    se = NA,
    loglik = loglik,
    AIC = aic,
    BIC = bic,
    CAIC = caic,
    KS_stat = as.numeric(ks_res$statistic),
    p.value = ks_res$p.value
  )
  class(res) <- "mtl_fit"
  return(res)
}

#' @rdname classical_mtl
#' @export
wls_mtl <- function(x) {
  x_sort <- sort(x[!is.na(x) & x > 0])
  n <- length(x_sort)
  if (n == 0) stop("Vector 'x' must contain positive numeric values.")
  
  i_seq <- 1:n
  target_p <- i_seq / (n + 1)
  weights <- ((n + 1)^2 * (n + 2)) / (i_seq * (n - i_seq + 1))
  
  obj_fn <- function(alpha) {
    p_fit <- pmtl(x_sort, alpha)
    sum(weights * (p_fit - target_p)^2)
  }
  
  init_alpha <- mle_mtl(x_sort)$estimate
  opt <- stats::optim(par = init_alpha, fn = obj_fn, method = "L-BFGS-B", lower = 1e-5)
  alpha_hat <- opt$par
  
  loglik <- sum(dmtl(x_sort, alpha_hat, log = TRUE))
  aic <- 2 - 2 * loglik
  bic <- log(n) - 2 * loglik
  caic <- aic + 4 / (n - 2)
  
  ks_res <- stats::ks.test(x_sort, "pmtl", alpha = alpha_hat)
  
  res <- list(
    method = "Weighted Least Squares (WLS)",
    estimate = alpha_hat,
    se = NA,
    loglik = loglik,
    AIC = aic,
    BIC = bic,
    CAIC = caic,
    KS_stat = as.numeric(ks_res$statistic),
    p.value = ks_res$p.value
  )
  class(res) <- "mtl_fit"
  return(res)
}

#' @rdname classical_mtl
#' @export
cvm_mtl <- function(x) {
  x_sort <- sort(x[!is.na(x) & x > 0])
  n <- length(x_sort)
  if (n == 0) stop("Vector 'x' must contain positive numeric values.")
  
  i_seq <- 1:n
  target_p <- (2 * i_seq - 1) / (2 * n)
  
  obj_fn <- function(alpha) {
    p_fit <- pmtl(x_sort, alpha)
    (1 / (12 * n)) + sum((p_fit - target_p)^2)
  }
  
  init_alpha <- mle_mtl(x_sort)$estimate
  opt <- stats::optim(par = init_alpha, fn = obj_fn, method = "L-BFGS-B", lower = 1e-5)
  alpha_hat <- opt$par
  
  loglik <- sum(dmtl(x_sort, alpha_hat, log = TRUE))
  aic <- 2 - 2 * loglik
  bic <- log(n) - 2 * loglik
  caic <- aic + 4 / (n - 2)
  
  ks_res <- stats::ks.test(x_sort, "pmtl", alpha = alpha_hat)
  
  res <- list(
    method = "Cramer-von Mises (CVM)",
    estimate = alpha_hat,
    se = NA,
    loglik = loglik,
    AIC = aic,
    BIC = bic,
    CAIC = caic,
    KS_stat = as.numeric(ks_res$statistic),
    p.value = ks_res$p.value
  )
  class(res) <- "mtl_fit"
  return(res)
}

#' @rdname classical_mtl
#' @export
mps_mtl <- function(x) {
  x_sort <- sort(x[!is.na(x) & x > 0])
  n <- length(x_sort)
  if (n == 0) stop("Vector 'x' must contain positive numeric values.")
  
  obj_fn <- function(alpha) {
    cdf_vals <- c(0, pmtl(x_sort, alpha), 1)
    spacings <- diff(cdf_vals)
    spacings <- pmax(spacings, 1e-12)
    -mean(log(spacings))
  }
  
  init_alpha <- mle_mtl(x_sort)$estimate
  opt <- stats::optim(par = init_alpha, fn = obj_fn, method = "L-BFGS-B", lower = 1e-5)
  alpha_hat <- opt$par
  
  loglik <- sum(dmtl(x_sort, alpha_hat, log = TRUE))
  aic <- 2 - 2 * loglik
  bic <- log(n) - 2 * loglik
  caic <- aic + 4 / (n - 2)
  
  ks_res <- stats::ks.test(x_sort, "pmtl", alpha = alpha_hat)
  
  res <- list(
    method = "Maximum Product of Spacings (MPS)",
    estimate = alpha_hat,
    se = NA,
    loglik = loglik,
    AIC = aic,
    BIC = bic,
    CAIC = caic,
    KS_stat = as.numeric(ks_res$statistic),
    p.value = ks_res$p.value
  )
  class(res) <- "mtl_fit"
  return(res)
}

#' @rdname classical_mtl
#' @export
fit_mtl <- function(x, method = c("mle", "ols", "wls", "cvm", "mps", "all"), conf.level = 0.95) {
  method <- match.arg(method)
  
  if (method == "mle") {
    return(mle_mtl(x = x, conf.level = conf.level))
  } else if (method == "ols") {
    return(ols_mtl(x = x))
  } else if (method == "wls") {
    return(wls_mtl(x = x))
  } else if (method == "cvm") {
    return(cvm_mtl(x = x))
  } else if (method == "mps") {
    return(mps_mtl(x = x))
  } else if (method == "all") {
    res_mle <- mle_mtl(x = x, conf.level = conf.level)
    res_ols <- ols_mtl(x = x)
    res_wls <- wls_mtl(x = x)
    res_cvm <- cvm_mtl(x = x)
    res_mps <- mps_mtl(x = x)
    
    df <- data.frame(
      Method = c("MLE", "OLS", "WLS", "CVM", "MPS"),
      Estimate = c(res_mle$estimate, res_ols$estimate, res_wls$estimate, res_cvm$estimate, res_mps$estimate),
      SE = c(res_mle$se, res_ols$se, res_wls$se, res_cvm$se, res_mps$se),
      LogLik = c(res_mle$loglik, res_ols$loglik, res_wls$loglik, res_cvm$loglik, res_mps$loglik),
      AIC = c(res_mle$AIC, res_ols$AIC, res_wls$AIC, res_cvm$AIC, res_mps$AIC),
      BIC = c(res_mle$BIC, res_ols$BIC, res_wls$BIC, res_cvm$BIC, res_mps$BIC),
      CAIC = c(res_mle$CAIC, res_ols$CAIC, res_wls$CAIC, res_cvm$CAIC, res_mps$CAIC),
      KS_stat = c(res_mle$KS_stat, res_ols$KS_stat, res_wls$KS_stat, res_cvm$KS_stat, res_mps$KS_stat),
      p.value = c(res_mle$p.value, res_ols$p.value, res_wls$p.value, res_cvm$p.value, res_mps$p.value)
    )
    return(df)
  }
}
