test_that("HeckmanSK analytical gradient agrees with numDeriv at MEPS2001 fit", {
  testthat::skip_on_cran()
  testthat::skip_if_not_installed("numDeriv")
  testthat::skip_if_not_installed("sn")

  data("MEPS2001", package = "ssmodels")
  dados <- as.data.frame(MEPS2001)
  dados$dambexp_num <- as.integer(dados$dambexp == 1)
  dados$lnambx_obs  <- ifelse(dados$dambexp_num == 1, dados$lnambx, NA_real_)

  selectEq  <- dambexp_num ~ age + female + educ + blhisp + totchr + ins + income
  outcomeEq <- lnambx_obs  ~ age + female + educ + blhisp + totchr + ins

  fit <- ssmodels::HeckmanSK(selection = selectEq, outcome = outcomeEq, data = dados)

  comp <- ssmodels::extract_model_components(selection = selectEq,
                                             outcome = outcomeEq,
                                             data = dados)
  comp$YO[is.na(comp$YO)] <- 0

  sk_loglik <- function(theta, comp) {
    XS <- comp$XS
    XO <- comp$XO
    YS <- comp$YS
    YO <- comp$YO
    NXS <- ncol(XS)
    NXO <- ncol(XO)

    gamma <- theta[seq_len(NXS)]
    beta <- theta[seq(from = NXS + 1L, length.out = NXO)]
    sigma <- theta[NXS + NXO + 1L]
    rho <- theta[NXS + NXO + 2L]
    lambda <- theta[NXS + NXO + 3L]

    if (!is.finite(sigma) || sigma <= 0) return(NA_real_)
    if (!is.finite(rho) || abs(rho) >= 1) return(NA_real_)
    if (!is.finite(lambda)) return(NA_real_)

    eta <- as.numeric(XS %*% gamma)
    mu <- as.numeric(XO %*% beta)
    z <- (YO - mu) / sigma
    r <- 1 - rho^2
    u <- 1 + lambda^2 - (lambda * rho)^2
    q <- lambda * rho / sqrt(u)
    omega <- (eta + rho * z) / sqrt(r)

    ll_selected <- log(2) - log(sigma) +
      stats::dnorm(z, log = TRUE) +
      stats::pnorm(lambda * z, log.p = TRUE) +
      stats::pnorm(omega, log.p = TRUE)

    F0 <- sn::psn(-eta, xi = 0, omega = 1, alpha = q)
    F0 <- pmax(F0, .Machine$double.xmin)
    ll_censored <- log(F0)

    sum(ifelse(YS == 1, ll_selected, ll_censored))
  }

  theta_hat <- fit$coefficients
  g_num <- numDeriv::grad(func = sk_loglik, x = theta_hat, comp = comp,
                          method = "Richardson")

  gradient_error <- max(abs(as.numeric(fit$gradient) - as.numeric(g_num)))

  testthat::expect_true(all(is.finite(fit$gradient)))
  testthat::expect_lt(gradient_error, 1e-4)
})
