#' Almost-exact inference for the DerSimonian-Laird test statistic
#'
#' `aedl()` computes a DerSimonian-Laird point estimate and performs
#' almost-exact inference for the DerSimonian-Laird test statistic in the
#' normal-normal random-effects meta-analysis model.
#'
#' @param yi Numeric vector of study-level effect estimates.
#' @param vi Numeric vector of known within-study variances.
#' @param alpha Significance level. The confidence level is `1 - alpha`.
#' @param method Method used to choose the heterogeneity value for constructing
#'   the almost-exact distribution. `"plugin"` corresponds to `exactDL` in the
#'   supplemental program of Hanada and Sugimoto (2023) and uses the DerSimonian-Laird
#'   estimate \eqn{\hat{\tau}_{DL}^2}. `"i2c"` uses a corrected heterogeneity
#'   measure \eqn{I_c^2}.
#' @param side Test side, either `1` or `2`.
#' @param a,b Lower and upper bounds of the uniform prior on `I^2` used when
#'   `method = "i2c"`.
#' @param t_grid Grid for the DerSimonian-Laird test statistic.
#' @param nsim Number of Monte Carlo samples for conditional quantities.
#' @param nsim_chi Number of Monte Carlo samples for the chi-square mixture.
#' @param n_tau Number of grid intervals for the untruncated DL estimator.
#' @param I2_grid Grid for `I^2` calculations.
#' @param normalize_cdf Logical; if `TRUE`, normalize the CDF constructed on
#'   the finite numerical grid so that its maximum is one.
#' @param seed Optional random seed. If supplied, it is set for this call.
#' @param ... Reserved for future extensions.
#'
#' @return An object of class `"aedl"` with elements `estimate`, `se`,
#'   `ci.lb`, `ci.ub`, `pval`, `tau2`, `tau2_for_dist`, `I2hat`, `I2c`,
#'   `tau2c`, `crit`, `method`, and `call`.
#'
#' @details
#' For `method = "plugin"`, the distribution of the DL test statistic is
#' constructed using \eqn{\hat{\tau}_{DL}^2} as the value of \eqn{\tau^2}.
#'
#' For `method = "i2c"`, the implementation evaluates
#' \eqn{p(\hat I^2_{\mathrm{obs}} \mid I^2)} on `I2_grid` by taking the row of
#' the simulated conditional distribution corresponding to the observed
#' \eqn{\hat I^2}. It then computes \eqn{I_c^2} under a uniform prior on
#' `[a, b]` and uses the corresponding \eqn{\tau_c^2} to construct the
#' almost-exact distribution.
#'
#' The numerical implementation is a streamlined version of the computational
#' procedure shown in the supplemental material of Hanada and Sugimoto (2023). It has been
#' checked against the original supplemental program in development scripts.
#' @export
#'
#' @examples
#' yi <- c(0.10, 0.25, 0.05, 0.30)
#' vi <- c(0.04, 0.05, 0.03, 0.06)
#' res <- aedl(
#'   yi, vi,
#'   t_grid = seq(-6, 6, by = 0.2),
#'   nsim = 100,
#'   nsim_chi = 500,
#'   n_tau = 50,
#'   seed = 1
#' )
#' res
#' confint(res)
aedl <- function(
    yi,
    vi,
    alpha = 0.05,
    method = c("plugin", "i2c"),
    side = 2,
    a = 0,
    b = 1,
    t_grid = seq(-20, 20, by = 0.01),
    nsim = 10000,
    nsim_chi = 100000,
    n_tau = 1000,
    I2_grid = seq(0, 0.99, by = 0.01),
    normalize_cdf = TRUE,
    seed = NULL,
    ...) {
  call <- match.call()
  method <- match.arg(method)

  aedl_validate_inputs(
    yi = yi,
    vi = vi,
    alpha = alpha,
    side = side,
    a = a,
    b = b,
    t_grid = t_grid,
    nsim = nsim,
    nsim_chi = nsim_chi,
    n_tau = n_tau,
    I2_grid = I2_grid,
    normalize_cdf = normalize_cdf
  )

  fit_call <- quote(aedl_fit_fast(
    yi = yi,
    vi = vi,
    alpha = alpha,
    method = method,
    side = side,
    a = a,
    b = b,
    t_grid = t_grid,
    nsim = nsim,
    nsim_chi = nsim_chi,
    n_tau = n_tau,
    I2_grid = I2_grid,
    normalize_cdf = normalize_cdf,
    ...
  ))

  fit <- if (is.null(seed)) {
    eval(fit_call)
  } else {
    withr::with_seed(seed, eval(fit_call))
  }

  aedl_new_result(fit, call = call, method = method)
}

aedl_fit_fast <- function(
    yi,
    vi,
    alpha = 0.05,
    method = c("plugin", "i2c"),
    side = 2,
    a = 0,
    b = 1,
    t_grid = seq(-20, 20, by = 0.01),
    nsim = 10000,
    nsim_chi = 100000,
    n_tau = 1000,
    I2_grid = seq(0, 0.99, by = 0.01),
    normalize_cdf = TRUE,
    ...) {
  method <- match.arg(method)

  dl <- aedl_dl_summary(yi, vi)
  tau2_for_dist <- dl$tau2
  I2c <- NA_real_
  tau2c <- dl$tau2

  if (method == "i2c") {
    i2c <- aedl_i2c(
      I2hat = dl$I2hat,
      vi = vi,
      sig2t = dl$sig2t,
      I2_grid = I2_grid,
      a = a,
      b = b,
      nsim = nsim
    )
    I2c <- i2c$I2c
    tau2c <- i2c$tau2c
    tau2_for_dist <- tau2c
  }

  tdl_density <- aedl_density_tdl(
    t_grid = t_grid,
    K = dl$K,
    sig2i = vi,
    tau2 = tau2_for_dist,
    theta = 0,
    nsim = nsim,
    nsim_chi = nsim_chi,
    n_tau = n_tau
  )

  dx <- tdl_density$x[2] - tdl_density$x[1]
  Ftdl <- cumsum(tdl_density$y) * dx

  if (normalize_cdf) {
    Ftdl <- Ftdl / max(Ftdl)
  }

  crit <- tdl_density$x[which.min(abs(Ftdl - (1 - alpha / 2)))]

  ci_lb <- dl$estimate - crit * dl$se
  ci_ub <- dl$estimate + crit * dl$se

  idx_p <- which.min(abs(tdl_density$x - abs(dl$zval)))
  pval <- side * (1 - Ftdl[idx_p])
  pval <- min(max(pval, 0), 1)

  list(
    estimate = dl$estimate,
    theta = dl$estimate,
    se = dl$se,
    sd = dl$se,
    ci.lb = ci_lb,
    ci.ub = ci_ub,
    pval = pval,
    tau2 = dl$tau2,
    tau2dl0 = dl$tau2dl0,
    tau2_for_dist = tau2_for_dist,
    I2hat = dl$I2hat,
    I2c = I2c,
    tau2c = tau2c,
    crit = crit,
    zval = dl$zval,
    conf.level = 1 - alpha,
    Ftdl_max = max(Ftdl),
    t_grid = tdl_density$x,
    ftdl = tdl_density$y,
    Ftdl = Ftdl,
    tau_grid = tdl_density$tau_grid,
    tau_density = tdl_density$tau_density,
    v2 = tdl_density$v2
  )
}
