### hhh4() model with shared overdispersion parameters

## use a small subset of districts from the fluBYBW data
data("fluBYBW")
fluBWsub <- fluBYBW[, substr(colnames(fluBYBW), 1, 2) %in% "81"]
## stsplot_space(fluBWsub, labels = TRUE)

## set "neighbourhood" to order of adjacency + 1
neighbourhood(fluBWsub) <- nbOrder(neighbourhood(fluBWsub)) + 1L

## a crazy model base
fluModel <- list(
    end = list(f = addSeason2formula(~0 + ri(type="iid"))),
    ne = list(f = ~0 + fe(1, unitSpecific = TRUE),
        weights = W_powerlaw(maxlag = 3)),
    start = list(random = rep.int(0, ncol(fluBWsub)))
)


if (FALSE) { # check derivatives
    fluDeriv <- hhh4(stsObj = fluBWsub,
                     control = c(fluModel, list(family = "NegBinM")),
                     check.analyticals = TRUE)
    ana <- fluDeriv$pen$fisher$analytic
    num <- fluDeriv$pen$fisher$numeric
    equal <- mapply(function (...) isTRUE(all.equal.numeric(...)),
                    ana, num, tolerance = 1e-4)
    dim(equal) <- dim(ana)
    Matrix::image(Matrix::Matrix(equal))
}


## fit a model with unit-specific overdispersion parameters using "NegBinM",
## equal to family = factor(colnames(fluBWsub), levels=colnames(fluBWsub))
fluFitM <- hhh4(stsObj = fluBWsub, control = c(fluModel, list(
    family = "NegBinM")))

test_that("\"NegBinM\" fit is invariant to the ordering of the overdispersion parameters", {
    fluFitM_reordered <- hhh4(stsObj = fluBWsub, control = c(fluModel, list(
        family = factor(colnames(fluBWsub), levels=rev(colnames(fluBWsub))))))
    expect_equal(fluFitM_reordered$loglikelihood,
                 fluFitM$loglikelihood)
    expect_equal(fluFitM_reordered$margll,
                 fluFitM$margll)
    expect_equal(fluFitM_reordered$coefficients[names(fluFitM$coefficients)],
                 fluFitM$coefficients)
})

test_that("random intercepts can be extracted", {
    ris <- ranef(fluFitM, intercept = TRUE)
    expect_equal(dimnames(ris), list(colnames(fluBWsub), "end.ri(iid)"))
    ## compute endemic predictor at t = 0 (i.e., subset = 1)
    end.exppred.t0 <- meanHHH(theta = fluFitM$coefficients,
                              model = terms(fluFitM), subset = 1)$end.exppred
    expect_equal(exp(ris + fluFitM$coefficients["end.cos(2 * pi * t/52)"]),
                 t(end.exppred.t0), check.attributes = FALSE)
})


## fit a model with shared overdispersion parameters
fluFitShared <- hhh4(stsObj = fluBWsub, control = c(fluModel, list(
    family = factor(substr(colnames(fluBWsub), 3, 3) == "1",
                    levels = c(TRUE, FALSE), labels = c("region1", "elsewhere")))))

test_that("estimates with shared overdispersion are reproducible", {
    ## dput(coef(fluFitShared, se = TRUE))
    orig <- structure(
        c(0.0172448275799737, -2.29936227176632, -0.311391919170833,
          0.0173369590386396, 0.242634649538434, -0.73402605050834, -0.0411427686831543,
          -0.917845995715638, -0.324146451650439, -0.252506337389155, 0.153202205413176,
          -0.857813219848051, -1.00758863915022, 2.01735387997105, 2.38047570484809,
          -4.38317074697181, 2.46949727973784, 0.549903756338196, 1.12432744953686,
          0.647372578569298, 0.21388842588635, -0.437822769909503, 0.255185408180267,
          0.92949604237045, -1.09633602928844, 0.298117843865811, -0.68452091605681,
          0.23456335139387, 0.162259631408099, 0.209619606465627, -0.10216429396362,
          -0.629658878921399, 0.114133112372732, 0.823887580788133, 0.12141926111051,
          0.113879127629599, 0.109816278251024, 0.221038616887962, 0.115707006557826,
          0.187260599970159, 0.121830940397345, 0.172070355414403, 0.157444513096506,
          0.254811666726125, 0.268571254537371, 0.215202234247305, 0.212970632033808,
          0.262762514629277, 0.205440489731246, 0.0567461846032841, 0.154168532075271,
          0.320248263514015, 0.309517737483193, 0.366585194306804, 0.370748971125027,
          0.304859567470968, 0.397763842736319, 0.357894067104384, 0.380956131344983,
          0.344676554711052, 0.37300484854814, 0.378382126329053, 0.342270280546076,
          0.359489843015429),
        dim = c(32L, 2L),
        dimnames = list(
            c("ne.1.8115", "ne.1.8135", "ne.1.8117", "ne.1.8116", "ne.1.8111", "ne.1.8121",
              "ne.1.8118", "ne.1.8136", "ne.1.8119", "ne.1.8125", "ne.1.8127",
              "ne.1.8126", "ne.1.8128", "end.sin(2 * pi * t/52)", "end.cos(2 * pi * t/52)",
              "end.ri(iid)", "neweights.d", "overdisp.region1", "overdisp.elsewhere",
              "end.ri(iid).8115", "end.ri(iid).8135", "end.ri(iid).8117", "end.ri(iid).8116",
              "end.ri(iid).8111", "end.ri(iid).8121", "end.ri(iid).8118", "end.ri(iid).8136",
              "end.ri(iid).8119", "end.ri(iid).8125", "end.ri(iid).8127", "end.ri(iid).8126",
              "end.ri(iid).8128"),
            c("Estimate", "Std. Error"))
        )

    expect_equal(coef(fluFitShared, se = TRUE), orig)
})

test_that("calibrationTest.oneStepAhead() works and \"final\" is equivalent to fit", {
    mysubset <- tail(fluFitShared$control$subset, 16)
    osa_final <- oneStepAhead(fluFitShared, tp = mysubset[1L]-1L,
                              type = "final", verbose = FALSE)
    idx <- 3:5  # ignore "method" and "data.name" in calibrationTest() output
    expect_equal(calibrationTest(osa_final, which = "dss")[idx],
                 calibrationTest(fluFitShared, which = "dss", subset = mysubset)[idx])
})

test_that("simulation correctly uses shared overdispersion parameters", {
    fluSimShared <- simulate(fluFitShared, seed = 1)
    ## simulate from the NegBinM model using the estimates from the shared fit
    psiShared <- coeflist(fluFitShared)$fixed$overdisp
    psiByUnit <- psiShared[fluFitShared$control$family]
    names(psiByUnit) <- paste0("overdisp.", names(fluFitShared$control$family))
    coefsM <- c(coef(fluFitShared), psiByUnit)[names(coef(fluFitM))]
    fluSimSharedM <- simulate(fluFitM, seed = 1, coefs = coefsM)
    expect_identical(observed(fluSimShared), observed(fluSimSharedM))
    ## fails for surveillance 1.12.2
})
