#' Cori-style renewal-equation R_t estimator
#'
#' An in-house implementation of the sliding-window renewal-equation
#' \eqn{R_t} estimator following Cori et al. (2013).
#'
#' **Important:** This is *not* a call to the R \pkg{EpiEstim} package and
#' numerical agreement with that package has not been independently verified.
#' It is provided for \emph{illustrative comparison of estimands} only: the
#' renewal-equation \eqn{R_t} and the compartmental \eqn{R_t} target different
#' quantities once susceptible depletion is non-negligible (see Section 6.2 of
#' Feng and Wang 2025).
#'
#' The estimator uses gamma-conjugate updating over a \eqn{\tau}-day sliding
#' window. The posterior is
#' \deqn{R_t \mid \text{data} \sim \mathrm{Gamma}(a + \sum I,\;
#'   [1/b + \Lambda]^{-1})}
#' where \eqn{\Lambda = \sum_{s=t-\tau+1}^{t} \Lambda_s} and
#' \eqn{\Lambda_s = \sum_{k \geq 1} w_k I_{s-k}} is daily infectiousness.
#'
#' @param incidence Numeric vector of length \eqn{T}. Daily case counts.
#' @param tau Integer. Sliding window length (days). Default 7.
#' @param si_mean Positive numeric. Serial interval mean (days). Default 5.5.
#' @param si_sd   Positive numeric. Serial interval SD (days). Default 2.5.
#' @param prior_a Positive numeric. Gamma prior shape on \eqn{R_t}. Default 1.
#' @param prior_b Positive numeric. Gamma prior scale on \eqn{R_t}. Default 5.
#'
#' @return A data frame with columns \code{t}, \code{Rt_mean}, \code{Rt_lo},
#'   \code{Rt_hi} (NaN where incidence is too low to estimate).
#'
#' @references
#' Cori, A., Ferguson, N. M., Fraser, C. and Cauchemez, S. (2013).
#' A new framework and software to estimate time-varying reproduction numbers
#' during epidemics. \emph{American Journal of Epidemiology},
#' 178(9):1505--1512.
#'
#' @export
#' @examples
#' set.seed(1)
#' inc <- rpois(60, lambda = c(rep(50, 20), seq(50, 200, length=20),
#'                             seq(200, 30, length=20)))
#' rt  <- cori_rt(inc, tau = 7, si_mean = 5.5, si_sd = 2.5)
#' plot(rt$t, rt$Rt_mean, type = "l", ylim = c(0, 4),
#'      xlab = "Day", ylab = expression(R[t]),
#'      main = "Cori-style Rt estimate")
#' abline(h = 1, lty = 2)
cori_rt <- function(incidence, tau = 7L, si_mean = 5.5, si_sd = 2.5,
                    prior_a = 1.0, prior_b = 5.0) {
  n_obs   <- length(incidence)
  si_shp  <- (si_mean / si_sd)^2
  si_rate <- si_mean / si_sd^2
  k_max   <- min(n_obs, 30L)

  # Discretised serial interval: w_k = P(SI in (k-0.5, k+0.5)), k=1..k_max
  k_seq  <- seq_len(k_max)
  si_pmf <- pgamma(k_seq + 0.5, si_shp, rate = si_rate) -
            pgamma(k_seq - 0.5, si_shp, rate = si_rate)
  si_pmf <- si_pmf / sum(si_pmf)

  # Pre-compute daily infectiousness Lambda_s for each s
  daily_lambda <- numeric(n_obs)
  for (s in seq_len(n_obs)) {
    for (k in seq_len(min(k_max, s - 1L))) {
      daily_lambda[s] <- daily_lambda[s] + si_pmf[k] * incidence[s - k]
    }
  }
  cum_lambda <- c(0, cumsum(daily_lambda))   # length n_obs+1 (1-indexed offset)

  Rt_mean <- rep(NaN, n_obs)
  Rt_lo   <- rep(NaN, n_obs)
  Rt_hi   <- rep(NaN, n_obs)

  for (t in seq(tau, n_obs)) {          # t is 1-indexed; window [t-tau+1, t]
    num    <- sum(incidence[seq(t - tau + 1L, t)])
    Lambda <- cum_lambda[t + 1L] - cum_lambda[t - tau + 1L]

    if (Lambda < 1e-10) next

    a_post <- prior_a + num
    b_post <- 1 / (1/prior_b + Lambda)

    Rt_mean[t] <- a_post * b_post
    Rt_lo[t]   <- qgamma(0.025, a_post, scale = b_post)
    Rt_hi[t]   <- qgamma(0.975, a_post, scale = b_post)
  }

  data.frame(t = seq_len(n_obs), Rt_mean = Rt_mean, Rt_lo = Rt_lo, Rt_hi = Rt_hi)
}
