# Copyright 2015-2023 Province of British Columbia
# Copyright 2021 Environment and Climate Change Canada
# Copyright 2023-2024 Australian Government Department of Climate Change,
# Energy, the Environment and Water
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#       https://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

test_that("hp", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)

  withr::with_seed(102, {
    hp <- ssd_hp(fits, conc = 1, ci = TRUE, nboot = 10, average = FALSE, proportion = FALSE)
  })
  expect_snapshot_data(hp, "hp")
})

test_that("hp proportion = FALSE deprecated", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)

  withr::with_seed(102, {
    lifecycle::expect_deprecated(
      hp <- ssd_hp(fits, conc = 1),
      "ssd_hp\\(proportion = FALSE\\) was deprecated in ssdtools 2"
    )
  })
  expect_snapshot_data(hp, "hpdep")
})

test_that("hp fitdists works with zero length conc", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  hp <- ssd_hp(fits, numeric(0), proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_identical(colnames(hp), c("dist", "conc", "est", "se", "lcl", "ucl", "wt", "est_method", "ci_method", "boot_method", "nboot", "pboot", "samples"))
  expect_equal(hp$dist, character(0))
  expect_identical(hp$conc, numeric(0))
  expect_equal(hp$est, numeric(0))
  expect_equal(hp$se, numeric(0))
  expect_equal(hp$wt, numeric(0))
})

test_that("hp fitdist works with missing conc", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  hp <- ssd_hp(fits, NA_real_, proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp41")
})

test_that("hp fitdist works with 0 conc", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  hp <- ssd_hp(fits, 0, proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp49")
})

test_that("hp fitdist works with negative conc", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  hp <- ssd_hp(fits, -1, proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp57")
})

test_that("hp fitdist works with -Inf conc", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  hp <- ssd_hp(fits, -Inf, proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp65")
})

test_that("hp fitdist works with Inf conc", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  hp <- ssd_hp(fits, Inf, proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp73")
})

test_that("hp fitdists works reasonable conc", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  hp <- ssd_hp(fits, 1, proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp81")
})

test_that("hp fitdists works with multiple concs", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  hp <- ssd_hp(fits, c(2.5, 1), ci_method = "multi_fixed", proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp89")
})

test_that("hp fitdists works with cis", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")

  withr::with_seed(10, {
    hp <- ssd_hp(fits, 1, ci = TRUE, nboot = 10, ci_method = "MACL", samples = TRUE, proportion = FALSE)
  })
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp98")
})

test_that("hp fitdists works with multiple dists", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)

  hp <- ssd_hp(fits, 1, proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp106")
})

test_that("hp fitdists works not average multiple dists", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)

  hp <- ssd_hp(fits, 1, average = FALSE, proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp114")
})

test_that("hp fitdists gives different answer with model averaging as hp not same for either", {
  data <- ssddata::aims_molybdenum_marine

  fits_lgumbel <- ssd_fit_dists(data, dists = "lgumbel")
  expect_equal(ssd_hp(fits_lgumbel, ssd_hc(fits_lgumbel, proportion = 5 / 100)$est, proportion = TRUE)$est, 5 / 100)

  fits_lnorm_lnorm <- ssd_fit_dists(data, dists = "lnorm_lnorm")
  expect_equal(ssd_hp(fits_lnorm_lnorm, ssd_hc(fits_lnorm_lnorm, proportion = 5 / 100)$est, proportion = TRUE)$est, 5 / 100)
  fits_both <- ssd_fit_dists(data, dists = c("lgumbel", "lnorm_lnorm"), min_pmix = 0)
  expect_equal(ssd_hp(fits_both, ssd_hc(fits_both, proportion = 5 / 100, ci_method = "MACL", est_method = "arithmetic")$est, proportion = TRUE)$est, 4.59194131309822 / 100, tolerance = 1e-06)
})

test_that("ssd_hc fitdists works odds", {
  data <- ssddata::ccme_boron
  data$Conc <- plogis(data$Conc) * 0.9
  withr::local_seed(99)
  fits <- ssd_fit_dists(data, dists = "lnorm", rescale = "odds")
  withr::local_seed(99)
  hp1 <- ssd_hp(fits, average = FALSE, proportion = TRUE, est_method = "multi", ci = TRUE, nboot = 10L)
  expect_snapshot_data(hp1, "hpwet1")
  hp7 <- ssd_hp(fits, conc = 0.7979396, average = FALSE, proportion = TRUE, est_method = "multi", ci = TRUE, nboot = 10L)
  expect_snapshot_data(hp7, "hpwet7")
  hp10 <- ssd_hp(fits, conc = 10, average = FALSE, proportion = TRUE, est_method = "multi", ci = TRUE, nboot = 10L)
  expect_snapshot_data(hp10, "hpwet10")
})

test_that("ssd_hp fitdists works odds 0.8", {
  data <- ssddata::ccme_boron
  data$Conc <- plogis(data$Conc) * 0.9
  withr::local_seed(99)
  fits <- ssd_fit_dists(data, dists = "lnorm", rescale = "odds", odds_max = 0.8)
  withr::local_seed(99)
  hp1 <- ssd_hp(fits, average = FALSE, proportion = TRUE, est_method = "multi", ci = TRUE, nboot = 10L)
  expect_snapshot_data(hp1, "hpwet081")
  hp7 <- ssd_hp(fits, conc = 0.7591709, average = FALSE, proportion = TRUE, est_method = "multi", ci = TRUE, nboot = 10L)
  expect_snapshot_data(hp7, "hpwet087")
})

test_that("ssd_hp fitdists averages", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)
  hp <- ssd_hp(fits, ci_method = "MACL", est_method = "arithmetic", proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp145")
})

test_that("ssd_hp fitdists geomean", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)
  hp <- ssd_hp(fits, ci_method = "MACL", est_method = "geometric", proportion = FALSE)
  expect_s3_class(hp, "tbl_df")
  expect_snapshot_data(hp, "hp145g")
})

test_that("ssd_hp fitdists correct for rescaling", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)
  fits_rescale <- ssd_fit_dists(ssddata::ccme_boron, rescale = TRUE)
  hp <- ssd_hp(fits, 1, proportion = FALSE)
  hp_rescale <- ssd_hp(fits_rescale, 1, proportion = FALSE)
  expect_equal(hp_rescale, hp, tolerance = 1e-04)
})

test_that("hp fitdists with no fitdists", {
  x <- list()
  class(x) <- c("fitdists")
  hp <- ssd_hp(x, 1, proportion = FALSE)
  expect_s3_class(hp, c("tbl_df", "tbl", "data.frame"))
  expect_snapshot_data(hp, "hp130")
})

test_that("ssd_hp doesn't calculate cis with inconsistent censoring", {
  data <- ssddata::ccme_boron
  data$Conc2 <- data$Conc
  data$Conc[1] <- 0.5
  data$Conc2[1] <- 1.0
  fits <- ssd_fit_dists(data, dists = c("lnorm", "llogis"))
  withr::with_seed(10, {
    hp <- ssd_hp(fits, 1, ci = TRUE, nboot = 10, ci_method = "MACL", proportion = FALSE)
  })
  expect_snapshot_value(hp$se, style = "deparse")

  fits <- ssd_fit_dists(data, right = "Conc2", dists = c("lnorm", "llogis"))
  withr::with_seed(10, {
    expect_warning(
      hp <- ssd_hp(fits, 1, ci = TRUE, nboot = 10, proportion = FALSE),
      "^Parametric CIs cannot be calculated for censored data[.]$"
    )
  })
  expect_identical(hp$se, NA_real_)
})

test_that("ssd_hp same with equally weighted data", {
  data <- ssddata::ccme_boron
  data$Weight <- rep(1, nrow(data))
  fits <- ssd_fit_dists(data, weight = "Weight", dists = "lnorm")
  withr::with_seed(10, {
    hp <- ssd_hp(fits, 1, ci = TRUE, nboot = 10, proportion = FALSE)
  })

  data$Weight <- rep(2, nrow(data))
  fits2 <- ssd_fit_dists(data, weight = "Weight", dists = "lnorm")
  withr::with_seed(10, {
    hp2 <- ssd_hp(fits2, 1, ci = TRUE, nboot = 10, proportion = FALSE)
  })
  expect_equal(hp2, hp)
})

test_that("ssd_hp calculates cis with equally weighted data", {
  data <- ssddata::ccme_boron
  data$Weight <- rep(2, nrow(data))
  fits <- ssd_fit_dists(data, weight = "Weight", dists = "lnorm")
  withr::with_seed(10, {
    hp <- ssd_hp(fits, 1, ci = TRUE, nboot = 10, ci_method = "MACL", proportion = FALSE)
  })
  expect_snapshot_value(hp$se, style = "deparse")
})

test_that("ssd_hp calculates cis with two distributions", {
  data <- ssddata::ccme_boron
  fits <- ssd_fit_dists(data, dists = c("lnorm", "llogis"))
  withr::with_seed(10, {
    hp <- ssd_hp(fits, 1, ci = TRUE, nboot = 10, ci_method = "MACL", proportion = FALSE)
  })
  expect_snapshot_value(hp$se, style = "deparse")
})

test_that("ssd_hp doesn't calculate cis with unequally weighted data", {
  data <- ssddata::ccme_boron
  data$Weight <- rep(1, nrow(data))
  data$Weight[1] <- 2
  fits <- ssd_fit_dists(data, weight = "Weight", dists = "lnorm")
  expect_warning(
    hp <- ssd_hp(fits, 1, ci = TRUE, nboot = 10, proportion = FALSE),
    "^Parametric CIs cannot be calculated for unequally weighted data[.]$"
  )
  expect_identical(hp$se, NA_real_)
})

test_that("ssd_hp no effect with higher weight one distribution", {
  data <- ssddata::ccme_boron
  data$Weight <- rep(1, nrow(data))
  fits <- ssd_fit_dists(data, weight = "Weight", dists = "lnorm")
  data$Weight <- rep(10, nrow(data))
  fits_10 <- ssd_fit_dists(data, weight = "Weight", dists = "lnorm")
  withr::with_seed(10, {
    hp <- ssd_hp(fits, 3, ci = TRUE, nboot = 10, proportion = FALSE)
  })
  withr::with_seed(10, {
    hp_10 <- ssd_hp(fits_10, 3, ci = TRUE, nboot = 10, proportion = FALSE)
  })
  expect_equal(hp_10, hp)
})

test_that("ssd_hp effect with higher weight two distributions", {
  data <- ssddata::ccme_boron
  data$Weight <- rep(1, nrow(data))
  fits <- ssd_fit_dists(data, weight = "Weight", dists = c("lnorm", "llogis"))
  data$Weight <- rep(10, nrow(data))
  fits_10 <- ssd_fit_dists(data, weight = "Weight", dists = c("lnorm", "llogis"))
  withr::with_seed(10, {
    hp <- ssd_hp(fits, 3, ci = TRUE, nboot = 10, ci_method = "MACL", proportion = FALSE)
  })
  withr::with_seed(10, {
    hp_10 <- ssd_hp(fits_10, 3, ci = TRUE, nboot = 10, ci_method = "MACL", proportion = FALSE)
  })
  expect_snapshot_value(hp$est, style = "deparse")
  expect_snapshot_value(hp_10$est, style = "deparse")
  expect_snapshot_value(hp$se, style = "deparse")
  expect_snapshot_value(hp_10$se, style = "deparse")
})

test_that("ssd_hp cis with non-convergence", {
  withr::with_seed(99, {
    conc <- ssd_rlnorm_lnorm(100, meanlog1 = 0, meanlog2 = 1, sdlog1 = 1 / 10, sdlog2 = 1 / 10, pmix = 0.2)
    data <- data.frame(Conc = conc)
    fit <- ssd_fit_dists(data, dists = "lnorm_lnorm", min_pmix = 0.15)
    expect_identical(attr(fit, "min_pmix"), 0.15)
    hp15 <- ssd_hp(fit, conc = 1, ci = TRUE, nboot = 100, min_pboot = 0.9, proportion = FALSE)
    attr(fit, "min_pmix") <- 0.3
    expect_identical(attr(fit, "min_pmix"), 0.3)
    hp30 <- ssd_hp(fit, conc = 1, ci = TRUE, nboot = 100, min_pboot = 0.9, ci_method = "MACL", samples = TRUE, proportion = FALSE)
  })
  expect_s3_class(hp30, "tbl")
  expect_snapshot_data(hp30, "hp_30")
})

test_that("ssd_hp with 1 bootstrap", {
  fit <- ssd_fit_dists(ssddata::ccme_boron, dists = "lnorm")
  withr::with_seed(10, {
    hp <- ssd_hp(fit, 1, ci = TRUE, nboot = 1, ci_method = "MACL", samples = TRUE, proportion = FALSE)
  })
  expect_snapshot_data(hp, "hp_1")
})

test_that("ssd_hp fix_weight", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = c("lnorm", "lgumbel"))

  withr::with_seed(102, {
    hc_unfix <- ssd_hp(fits, nboot = 100, ci = TRUE, ci_method = "multi_free", samples = TRUE, proportion = FALSE)
  })
  expect_snapshot_data(hc_unfix, "hc_unfix")

  withr::with_seed(102, {
    hc_fix <- ssd_hp(fits, nboot = 100, ci = TRUE, ci_method = "multi_fixed", samples = TRUE, proportion = FALSE)
  })
  expect_snapshot_data(hc_fix, "hc_fix")
})

test_that("hp multis match", {
  fits <- ssd_fit_dists(ssddata::ccme_boron, dists = c("lnorm", "gamma"))
  withr::with_seed(102, {
    hp_tf <- ssd_hp(fits, ci = TRUE, nboot = 10, average = TRUE, ci_method = "weighted_samples", proportion = FALSE)
  })
  withr::with_seed(102, {
    hp_ft <- ssd_hp(fits, ci = TRUE, nboot = 10, average = TRUE, est_method = "arithmetic", ci_method = "multi_fixed", proportion = FALSE)
  })
  withr::with_seed(102, {
    hp_ff <- ssd_hp(fits, ci = TRUE, nboot = 10, average = TRUE, est_method = "arithmetic", ci_method = "weighted_samples", proportion = FALSE)
  })
  withr::with_seed(102, {
    hp_tt <- ssd_hp(fits, ci = TRUE, nboot = 10, average = TRUE, ci_method = "multi_fixed", proportion = FALSE)
  })

  expect_identical(hp_tf$est, hp_tt$est)
  expect_identical(hp_ft$est, hp_ff$est)
  expect_identical(hp_ft$se, hp_tt$se)
  expect_identical(hp_ff$se, hp_tf$se)
})

test_that("hp weighted bootie", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)
  withr::with_seed(102, {
    hp_weighted2 <- ssd_hp(fits,
      ci = TRUE, nboot = 10, average = TRUE, est_method = "arithmetic", ci_method = "weighted_samples",
      samples = TRUE, proportion = FALSE
    )
  })
  withr::with_seed(102, {
    hp_unweighted2 <- ssd_hp(fits, ci = TRUE, nboot = 10, average = TRUE, est_method = "arithmetic", ci_method = "MACL", samples = TRUE, proportion = FALSE)
  })

  expect_identical(hp_weighted2$est, hp_unweighted2$est)
  expect_identical(length(hp_weighted2$samples[[1]]), 10L)
  expect_identical(length(hp_unweighted2$samples[[1]]), 60L)

  expect_snapshot_boot_data(hp_weighted2, "hp_weighted2")
  expect_snapshot_boot_data(hp_unweighted2, "hp_unweighted2")
})

test_that("hp multi_est = TRUE deprecated", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)
  withr::with_seed(10, {
    multi <- ssd_hc(fits)
  })

  withr::with_seed(10, {
    lifecycle::expect_deprecated({
      true <- ssd_hc(fits, multi_est = TRUE)
    })
  })
  expect_identical(true, multi)
})

test_that("hp est_method = FALSE deprecated and overrides est_method", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)
  withr::with_seed(10, {
    arithmetic <- ssd_hp(fits, est_method = "arithmetic", proportion = FALSE)
  })

  withr::with_seed(10, {
    lifecycle::expect_deprecated({
      false <- ssd_hp(fits, multi_est = FALSE, est_method = "geometric", proportion = FALSE)
    })
  })

  expect_identical(false, arithmetic)
})

test_that("hp ci_method = 'weighted_arithmetic' deprecated for MACL", {
  fits <- ssd_fit_dists(ssddata::ccme_boron)
  withr::with_seed(10, {
    lifecycle::expect_deprecated({
      weighted_arithmetic <- ssd_hp(fits, ci_method = "weighted_arithmetic", proportion = FALSE)
    })
  })

  withr::with_seed(10, {
    macl <- ssd_hp(fits, ci_method = "MACL", proportion = FALSE)
  })

  expect_identical(macl, weighted_arithmetic)
})
