library(dplyr)
library(tidyr)


set.seed(123)
mu <- c(1,2,3)
sigma <- rbind(c(3,0.5,0.3),c(0.5,4,0.5),c(0.3,0.5,5))
baseline_mean <- mu[1]
n <- 20
n_visits <- length(mu)
outcome <- c(replicate(n, sample_mvnorm(mu,sigma)))
ids <- as.factor(rep(paste0("id_", seq.int(n)), each = n_visits))
visits <- as.factor(rep(seq.int(n_visits)-1, n))

test_data_basics <- function(res, n_visits, n) {

    expect_true(is.data.frame(res))
    expect_true(all(sapply(res[,c("id", "group", "visit")], is.factor)))
    expect_true(all(sapply(res[, ! colnames(res) %in% c("id", "group", "visit")], is.numeric)))
    expect_equal(nrow(res), n_visits*n)
    expect_equal(nlevels(res$visit), n_visits)
    expect_length(unique(res$id), n)

    expect_true(
        all(
            sapply(
                res[, c("ind_ice1", "dropout_ice1", "ind_ice2")],
                function(x) all(x %in% c(0, 1))
            )
        )
    )

    expect_true(all(!is.na(res$outcome_noICE)))

    outcome_baseline <- unique(res$outcome_bl)
    expect_equal(res$outcome_noICE[seq(from = 1, to = n_visits*n, by = n_visits)], outcome_baseline)
    expect_equal(res$outcome[seq(from = 1, to = n_visits*n, by = n_visits)], outcome_baseline)
}


test_that("set_simul_pars", {

    pars <- set_simul_pars(
        mu = mu,
        sigma = sigma,
        n = n)

    expect_equal(list(pars$mu, pars$sigma, pars$n) , list(mu, sigma, n))

    expect_true(
        all(c(pars$prob_ice1, pars$prob_post_ice1_dropout, pars$prob_ice2, pars$prob_miss) == 0)
    )
    expect_true(
        pars$or_outcome_ice1 == 1
    )

    expect_true(validate(pars))

    expect_equal(class(pars), "simul_pars")

    pars$prob_ice1 <- rep(0.5, n_visits - 1)
    expect_true(validate(pars))

    pars$prob_ice2 <- NULL
    expect_error(validate(pars))

    pars$prob_ice2 <- 0.5
    pars$prob_dropout <- 2
    expect_error(validate(pars))

    pars$prob_dropout <- NULL
    pars$prob_ice2 <- 2
    expect_error(
        validate(pars),
        regexp = "`prob_ice2`"
    )
    
    pars$prob_ice2 <- 0.5
    pars$sigma[1,1] <- NA
    expect_error(validate(pars))

    expect_error(set_simul_pars(mu = mu))

})


test_that("simulate_dropout", {

    prob_dropout <- 0
    res <- simulate_dropout(prob_dropout, ids)
    expect_equal(res, rep(0, length(ids)))

    prob_dropout <- 1
    res <- simulate_dropout(prob_dropout, ids)
    expect_equal(res, rep(c(0,1,1), n))

    prob_dropout <- 0.5
    res <- simulate_dropout(prob_dropout, ids)
    expect_true(all(res %in% c(0,1)))
    expect_length(res, length(ids))

    prob_dropout <- 1
    subset <- rep(0, length(ids))
    res <- simulate_dropout(prob_dropout, ids, subset)
    expect_equal(res, rep(0, length(ids)))

    subset <- rep(c(0,0,1), n)
    res <- simulate_dropout(prob_dropout, ids, subset)
    expect_length(res, length(ids))
    expect_equal(res[subset == 0], subset[subset == 0])
    expect_true(all(res[subset == 1] == 1))

    subset <- rep(c(1,1,1), n)
    res <- simulate_dropout(prob_dropout, ids, subset)
    expect_equal(res, rep(c(0,1,1), n))

    prob_dropout <- 0
    subset <- rep(0, length(ids))
    res <- simulate_dropout(prob_dropout, ids, subset)
    expect_equal(res, rep(0, length(ids)))

    subset <- rep(c(0,0,1), n)
    res <- simulate_dropout(prob_dropout, ids, subset)
    expect_equal(res, rep(0, length(ids)))

    subset <- rep(c(1,1,1), n)
    res <- simulate_dropout(prob_dropout, ids, subset)
    expect_equal(res, rep(0, length(ids)))

})


test_that("simulate_ice", {

    pars <- set_simul_pars(mu, sigma, n)
    res <- simulate_ice(outcome, visits, ids, pars$prob_ice1, pars$or_outcome_ice1, baseline_mean)
    expect_equal(res, rep(0, length(ids)))

    pars$prob_ice1 <- 1
    res <- simulate_ice(outcome, visits, ids, pars$prob_ice1, pars$or_outcome_ice1, baseline_mean)
    expect_equal(res, rep(c(0,1,1), n))

    pars$prob_ice1 <- rep(1, n_visits - 1)
    res <- simulate_ice(outcome, visits, ids, pars$prob_ice1, pars$or_outcome_ice1, baseline_mean)
    expect_equal(res, rep(c(0,1,1), n))

    pars$prob_ice1 <- 0.1
    pars$or_outcome_ice1 <- 3
    res <- simulate_ice(outcome, visits, ids, pars$prob_ice1, pars$or_outcome_ice1, baseline_mean)
    expect_length(res, length(ids))
    expect_true(all(res %in% c(0,1)))
    expect_true(all(res[seq(from = 1, to = length(ids), by = n_visits)] == 0))

})


test_that("adjust_trajectories_single", {

    outcome <- outcome[1:3]
    distr_pars_group <- list(mu = mu, sigma = sigma)
    strategy_fun <- getStrategies()$CIR

    # no missing values
    res <- adjust_trajectories_single(distr_pars_group, outcome, strategy_fun, distr_pars_ref = NULL)
    expect_equal(res, outcome)

    distr_pars_ref <- distr_pars_group
    distr_pars_ref$sigma[1, 1] <- 1
    distr_pars_ref$mu <- c(3, 6, 9)
    res <- adjust_trajectories_single(distr_pars_group, outcome, strategy_fun, distr_pars_ref)
    expect_equal(res, outcome)

    # missing values
    outcome[2:3] <- NA
    res <- adjust_trajectories_single(distr_pars_group, outcome, strategy_fun, distr_pars_ref)
    expect_true(all(!is.na(res)))
    expect_equal(res[1], outcome[1])

})


test_that("adjust_trajectories", {

    ind_ice <- unlist(
        tapply(
            rep(0, length(ids)),
            ids,
            function(x) {
                p <- runif(1)
                if (p <= 0.33) {
                    x[2:3] <- 1
                } else if (p <= 0.66) {
                    x[3] <- 1
                }
                return(x)
            }
        ),
        use.names = FALSE
    )

    ids <- factor(ids, levels = unique(ids))

    distr_pars_group <- list(mu = mu, sigma = sigma)
    strategy_fun <- getStrategies()$JR
    res <- adjust_trajectories(
        distr_pars_group,
        outcome,
        ids,
        ind_ice = rep(0, length(ids)),
        strategy_fun, distr_pars_ref = NULL
    )
    expect_equal(res, outcome)

    distr_pars_ref <- distr_pars_group
    distr_pars_ref$sigma[1,1] <- 1
    distr_pars_ref$mu <- c(3,6,9)
    res <- adjust_trajectories(
        distr_pars_group,
        outcome,
        ids,
        ind_ice,
        strategy_fun,
        distr_pars_ref
    )
    expect_length(res, length(ids))
    expect_true(all(!is.na(res)))
    expect_false(identical(res, outcome))

    outcome[2] <- NA
    expect_error(
        adjust_trajectories(
            distr_pars_group,
            outcome,
            ids,
            ind_ice,
            strategy_fun,
            distr_pars_ref
        )
    )
})


test_that("generate_data_single", {

    pars_group <- set_simul_pars(
        mu = mu,
        sigma = sigma,
        n = n
    )
    strategy_fun <- getStrategies()$CR

    # no ICEs
    res <- generate_data_single(pars_group, strategy_fun, distr_pars_ref = NULL)
    test_data_basics(res, n_visits, n)
    expect_true(all(!is.na(res)))
    expect_true(identical(res$outcome_noICE, res$outcome))
    expect_true(all(c(res$ind_ice1, res$dropout_ice1, res$ind_ice2) == 0))

    # only ICE1
    pars_group$prob_ice1 <- 1
    res <- generate_data_single(pars_group, strategy_fun, distr_pars_ref = NULL)
    test_data_basics(res, n_visits, n)
    expect_equal(res$ind_ice1, rep(c(0,1,1), n))
    expect_false(identical(res$outcome_noICE, res$outcome))

    # add dropout
    pars_group$prob_post_ice1_dropout <- 1
    res <- generate_data_single(pars_group, strategy_fun, distr_pars_ref = NULL)
    test_data_basics(res, n_visits, n)
    expect_equal(res$dropout_ice1, rep(c(0,1,1), n))
    expect_true(all(is.na(res$outcome[-seq(from = 1, to = n_visits*n, by = n_visits)])))

    # only ice2
    pars_group$prob_ice1 <- 0
    pars_group$prob_post_ice1_dropout <- 0
    pars_group$prob_ice2 <- 1
    res <- generate_data_single(pars_group, strategy_fun, distr_pars_ref = NULL)
    test_data_basics(res, n_visits, n)
    expect_equal(res$ind_ice2, rep(c(0,1,1), n))
    expect_true(all(is.na(res$outcome[-seq(from = 1, to = n_visits*n, by = n_visits)])))

    # both ice1 and ice2 have prob 1 -> expect only ice1
    pars_group$prob_ice1 <- 1
    pars_group$prob_ice2 <- 1
    res <- generate_data_single(pars_group, strategy_fun, distr_pars_ref = NULL)
    test_data_basics(res, n_visits, n)
    expect_equal(res$ind_ice2, rep(c(0,0,0), n))
    expect_equal(res$ind_ice1, rep(c(0,1,1), n))
    expect_true(all(!is.na(res$outcome)))

    pars_group$prob_post_ice1_dropout <- 1
    res <- generate_data_single(pars_group, strategy_fun, distr_pars_ref = NULL)
    test_data_basics(res, n_visits, n)
    expect_equal(res$ind_ice2, rep(c(0,0,0), n))
    expect_equal(res$ind_ice1, rep(c(0,1,1), n))
    expect_true(all(is.na(res$outcome[-seq(from = 1, to = n_visits*n, by = n_visits)])))

    # prob of both ICEs different than 0 or 1
    pars_group$prob_ice1 <- 0.7
    pars_group$prob_ice2 <- 0.7
    res <- generate_data_single(pars_group, strategy_fun, distr_pars_ref = NULL)
    test_data_basics(res, n_visits, n)
    expect_true(all(res$ind_ice2*res$ind_ice1 == 0)) # they cannot both happen
    expect_true(all(is.na(res$outcome[(pars_group$prob_ice1 == 1 &
                                           pars_group$prob_post_ice1_dropout == 1) |
                                          pars_group$prob_ice2 == 1])))
})


test_that("simulate_data", {

    pars_t <- set_simul_pars(
        mu = mu,
        sigma = sigma,
        n = n
    )
    pars_c <- pars_t
    pars_c$mu <- c(3,6,9)
    pars_c$sigma[3,3] <- 10
    post_ice1_traj <- "JR"

    # no ICEs
    res <- simulate_data(pars_c, pars_t, post_ice1_traj)
    test_data_basics(res, n_visits, 2*n)

    # ICE1
    pars_c$prob_ice1 <- pars_t$prob_ice1 <- c(0.4, 0.5)
    pars_c$or_outcome_ice1 <- pars_t$or_outcome_ice1 <- 3
    pars_c$prob_post_ice1_dropout <- pars_t$prob_post_ice1_dropout <- 0.5

    post_ice1_traj <- "CIR"
    set.seed(123)
    res <- simulate_data(pars_c, pars_t, post_ice1_traj)
    test_data_basics(res, n_visits, 2*n)

    # custom function
    myfun <- function(pars_group, pars_ref, index_mar) return(strategy_CIR(pars_group, pars_ref, index_mar))
    post_ice1_traj <- "myfun"
    set.seed(123)
    res_cir <- simulate_data(pars_c, pars_t, post_ice1_traj, strategies = getStrategies(myfun = myfun))
    test_data_basics(res, n_visits, 2*n)
    expect_equal(res, res_cir) # custom function is just CIR

    pars_c <- pars_t <- set_simul_pars(
        mu = mu,
        sigma = sigma,
        n = n,
        prob_miss = 1
    )
    post_ice1_traj <- "JR"
    res <- simulate_data(pars_c, pars_t, post_ice1_traj)
    expect_true(all(is.na(res$outcome[-seq(from = 1, to = n_visits*2*n, by = n_visits)])))

    pars_c <- pars_t <- set_simul_pars(
        mu = mu,
        sigma = sigma,
        n = n,
        prob_ice2 = 1
    )
    res <- simulate_data(pars_c, pars_t, post_ice1_traj)
    expect_true(all(is.na(res$outcome[-seq(from = 1, to = n_visits*2*n, by = n_visits)])))

    class(pars_c) <- NULL
    expect_error(simulate_data(pars_c, pars_t, post_ice1_traj))

})





test_that("generic tests to ensure simulate_data is working as expected", {

    expect_around <- function(x, e, margin = 0.05) {
        expect_true(
            all(c(
                x <= e * (1 + margin),
                x >= e * (1 - margin)
            ))
        )
    }

    set.seed(1241)

    simpar_c_sigma <- list(
        sd = c(5, 4, 2),
        cor = c(0.1, 0.2, 0.3)
    )

    simpar_c <- set_simul_pars(
        mu = c(10, 10, 10),
        sigma = as_vcov(simpar_c_sigma$sd, simpar_c_sigma$cor),
        n = 9000,
        prob_ice1 = c(0.25),
        or_outcome_ice1 = 1,
        prob_post_ice1_dropout = 0,
        prob_ice2 = 0,
        prob_miss = 0.2
    )

    simpar_t_sigma <- list(
        sd = c(1, 2, 3),
        cor = c(0.3, 0.4, 0.5)
    )

    simpar_t <- set_simul_pars(
        mu = c(10, 30, 60),
        sigma = as_vcov(simpar_t_sigma$sd, simpar_t_sigma$cor),
        n = 7000,
        prob_ice1 = c(0.75, 0),
        or_outcome_ice1 = 1,
        prob_post_ice1_dropout = 0,
        prob_ice2 = 0,
        prob_miss = 0
    )

    dat <- simulate_data(
        pars_c = simpar_c,
        pars_t = simpar_t,
        post_ice1_traj = "JR"
    ) %>%
        as_tibble()


    count <- dat %>%
        group_by(group) %>%
        tally() %>%
        pull(n)

    expect_equal(
        count,
        c(simpar_c$n, simpar_t$n) * 3
    )


    control_nrow <- dat %>%
        filter(group == "Control") %>%
        filter(!is.na(outcome)) %>%
        filter(outcome_noICE != outcome) %>%
        nrow()

    expect_equal(
        control_nrow,
        0
    )



    stat_c <- dat %>%
        filter(group == "Control") %>%
        group_by(visit) %>%
        summarise(
            m = mean(outcome, na.rm = TRUE),
            v = var(outcome, na.rm = TRUE),
            nd = mean(ind_ice1),
            nm = mean(is.na(outcome))
        )


    stat_t <- dat %>%
        filter(group == "Intervention") %>%
        group_by(visit) %>%
        summarise(
            m = mean(outcome),
            v = var(outcome_noICE),
            nd = mean(ind_ice1)
        )


    pt <- c(0, simpar_t$prob_ice1)
    cum_pt <- 1 - cumprod(1 - pt)
    e_mean <- (simpar_t$mu * (1 - cum_pt)) + (simpar_c$mu * cum_pt)


    expect_around(stat_t$m, e_mean)
    expect_around(stat_t$v, diag(simpar_t$sigma))

    expect_around(stat_c$m, simpar_c$mu)
    expect_around(stat_c$v, diag(simpar_c$sigma))

    expect_around(
        stat_c$nm,
        c(0, rep(simpar_c$prob_miss, length(simpar_c$mu) - 1))
    )

    pc <- c(0, rep(simpar_c$prob_ice1, length(simpar_c$mu) - 1))
    cum_pc <- 1 - cumprod(1 - pc)
    expect_around(stat_c$nd, cum_pc)

})
