test_that("prepare_dcvar_data works with valid input", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  result <- prepare_dcvar_data(df, vars = c("y1", "y2"))

  expect_equal(result$n_time, 50)
  expect_equal(result$D, 2)
  expect_equal(nrow(result$Y), 50)
  expect_equal(ncol(result$Y), 2)
  expect_true(attr(result, "standardized"))
  expect_equal(attr(result, "time_values"), 1:50)
})

test_that("prepare_dcvar_data rejects non-bivariate input", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50), y3 = rnorm(50))
  expect_error(prepare_dcvar_data(df, vars = "y1"), "Exactly 2")
  expect_error(prepare_dcvar_data(df, vars = c("y1", "y2", "y3")), "Exactly 2")
})

test_that("prepare_dcvar_data rejects duplicate vars", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  expect_error(prepare_dcvar_data(df, vars = c("y1", "y1")), "distinct")
})

test_that("prepare_dcvar_data rejects missing columns", {
  df <- data.frame(time = 1:50, y1 = rnorm(50))
  expect_error(prepare_dcvar_data(df, vars = c("y1", "y2")), "not found")
})

test_that("prepare_dcvar_data errors on interior missing values by default", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  df$y1[25] <- NA
  expect_error(prepare_dcvar_data(df, vars = c("y1", "y2")), "interior missing")
})

test_that("prepare_dcvar_data reports the interior missing count", {
  df <- data.frame(time = 1:10, y1 = rnorm(10), y2 = rnorm(10))
  df$y1[c(1, 2, 6)] <- NA
  expect_error(
    prepare_dcvar_data(df, vars = c("y1", "y2")),
    "Found 1 interior missing value"
  )
})

test_that("prepare_dcvar_data can drop interior missing values when allow_gaps = TRUE", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  df$y1[25] <- NA

  expect_warning(
    out <- prepare_dcvar_data(df, vars = c("y1", "y2"), allow_gaps = TRUE),
    "Removing 1 row"
  )
  expect_equal(out$n_time, 49)
  expect_equal(attr(out, "time_values"), c(1:24, 26:50))
})

test_that("prepare_dcvar_data warns on edge missing values", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  df$y1[1] <- NA

  expect_warning(
    out <- prepare_dcvar_data(df, vars = c("y1", "y2")),
    "edges only"
  )
  expect_equal(out$n_time, 49)
  expect_equal(attr(out, "time_values"), 2:50)
})

test_that("prepare_dcvar_data preserves standardization metadata", {
  df <- data.frame(time = 1:50, y1 = rnorm(50, 5, 2), y2 = rnorm(50, -3, 0.5))
  result <- prepare_dcvar_data(df, vars = c("y1", "y2"), standardize = TRUE)

  expect_true(!is.null(attr(result, "Y_means")))
  expect_true(!is.null(attr(result, "Y_sds")))
  expect_equal(length(attr(result, "Y_means")), 2)
})

test_that("prepare_dcvar_data skips standardization when FALSE", {
  df <- data.frame(time = 1:50, y1 = rnorm(50, 5, 2), y2 = rnorm(50))
  result <- prepare_dcvar_data(df, vars = c("y1", "y2"), standardize = FALSE)

  expect_false(attr(result, "standardized"))
  expect_null(attr(result, "Y_means"))
})

test_that("prepare_dcvar_data sets correct prior hyperparameters", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  result <- prepare_dcvar_data(df, vars = c("y1", "y2"),
                               prior_mu_sd = 3, prior_sigma_omega_rate = 0.2)

  expect_equal(result$sigma_mu_prior, 3)
  expect_equal(result$sigma_omega_prior, 0.2)
})

test_that("prepare_dcvar_data rejects invalid prior scalars", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  expect_error(
    prepare_dcvar_data(df, vars = c("y1", "y2"), prior_mu_sd = 0),
    "prior_mu_sd"
  )
  expect_error(
    prepare_dcvar_data(df, vars = c("y1", "y2"), prior_sigma_eps_rate = -1),
    "prior_sigma_eps_rate"
  )
})

test_that("prepare_hmm_data adds HMM-specific fields", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  result <- prepare_hmm_data(df, vars = c("y1", "y2"), K = 3)

  expect_equal(result$K, 3L)
  expect_equal(result$kappa, 10)
  expect_equal(result$alpha_off, 1)
  expect_equal(result$z_rho_prior_sd, 1.0)
})

test_that("prepare_hmm_data validates K consistently with dcvar_hmm", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  expect_error(prepare_hmm_data(df, vars = c("y1", "y2"), K = 2.7), "integer >= 2")
})

test_that("prepare_hmm_data rejects invalid prior scalars", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  expect_error(
    prepare_hmm_data(df, vars = c("y1", "y2"), prior_kappa = 0),
    "prior_kappa"
  )
})

test_that("prepare_constant_data adds copula prior", {
 df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  result <- prepare_constant_data(df, vars = c("y1", "y2"))

  expect_equal(result$z_rho_prior_sd, 1.0)
  expect_null(result$sigma_omega_prior)
})

test_that("prepare_constant_data rejects duplicate vars", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  expect_error(prepare_constant_data(df, vars = c("y1", "y1")), "distinct")
})

test_that("prepare_constant_data rejects invalid prior scalars", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  expect_error(
    prepare_constant_data(df, vars = c("y1", "y2"), prior_mu_sd = 0),
    "prior_mu_sd"
  )
  expect_error(
    prepare_constant_data(df, vars = c("y1", "y2"), prior_sigma_eps_rate = -1),
    "prior_sigma_eps_rate"
  )
  expect_error(
    prepare_constant_data(df, vars = c("y1", "y2"), prior_z_rho_sd = Inf),
    "prior_z_rho_sd"
  )
})

test_that("data is sorted by time_var", {
  df <- data.frame(time = c(3, 1, 2), y1 = c(30, 10, 20), y2 = c(33, 11, 22))
  result <- prepare_dcvar_data(df, vars = c("y1", "y2"), standardize = FALSE)

  expect_equal(result$Y[1, 1], 10)
  expect_equal(result$Y[2, 1], 20)
  expect_equal(result$Y[3, 1], 30)
  expect_equal(attr(result, "time_values"), c(1, 2, 3))
})

test_that("prepare_dcvar_data rejects duplicate time values", {
  df_duplicate <- data.frame(time = c(1, 2, 2), y1 = rnorm(3), y2 = rnorm(3))
  expect_error(
    prepare_dcvar_data(df_duplicate, vars = c("y1", "y2")),
    "Duplicate"
  )
})

test_that("prepare_dcvar_data handles irregular time values consistently", {
  df_irregular <- data.frame(time = c(1, 3, 4), y1 = rnorm(3), y2 = rnorm(3))
  expect_error(
    prepare_dcvar_data(df_irregular, vars = c("y1", "y2")),
    "not evenly spaced"
  )

  expect_warning(
    out <- prepare_dcvar_data(
      df_irregular,
      vars = c("y1", "y2"),
      allow_gaps = TRUE
    ),
    "not evenly spaced"
  )
  expect_equal(attr(out, "time_values"), c(1, 3, 4))
})

test_that("prepare_dcvar_data rejects character and unordered-factor time columns", {
  df <- data.frame(time = paste0("T", 1:12), y1 = rnorm(12), y2 = rnorm(12))
  expect_error(prepare_dcvar_data(df, vars = c("y1", "y2")), "lexicographically")

  df$time <- factor(df$time)
  expect_error(prepare_dcvar_data(df, vars = c("y1", "y2")), "lexicographically")
})

test_that("prepare_dcvar_data accepts ordered-factor time columns", {
  lv <- paste0("T", 1:12)
  df <- data.frame(time = factor(lv, levels = lv, ordered = TRUE),
                   y1 = rnorm(12), y2 = rnorm(12))
  out <- prepare_dcvar_data(df, vars = c("y1", "y2"))
  expect_equal(out$n_time, 12)
})

test_that("prepare_dcvar_data treats edge runs of missing rows as edges, not gaps", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  df$y1[1:2] <- NA
  df$y2[49:50] <- NA

  expect_warning(
    out <- prepare_dcvar_data(df, vars = c("y1", "y2")),
    "edges only"
  )
  expect_equal(out$n_time, 46)
  expect_equal(attr(out, "time_values"), 3:48)
})

test_that("prepare_dcvar_data still errors on genuinely interior missing runs", {
  df <- data.frame(time = 1:50, y1 = rnorm(50), y2 = rnorm(50))
  df$y1[20:21] <- NA
  expect_error(prepare_dcvar_data(df, vars = c("y1", "y2")), "interior missing")
})

test_that("prepare_dcvar_data aborts cleanly when too few complete rows remain", {
  df <- data.frame(time = 1:5, y1 = c(NA, NA, 1, NA, NA), y2 = rnorm(5))
  expect_warning(
    expect_error(prepare_dcvar_data(df, vars = c("y1", "y2")), "At least 3 observations"),
    "edges only"
  )

  df_all_na <- data.frame(time = 1:5, y1 = NA_real_, y2 = rnorm(5))
  expect_error(prepare_dcvar_data(df_all_na, vars = c("y1", "y2")), "no complete rows")
})

test_that("prepare_dcvar_data accepts tibble input identically to data.frame", {
  skip_if_not_installed("tibble")
  df <- data.frame(time = 1:30, y1 = rnorm(30), y2 = rnorm(30))
  out_df <- prepare_dcvar_data(df, vars = c("y1", "y2"))
  out_tbl <- prepare_dcvar_data(tibble::as_tibble(df), vars = c("y1", "y2"))
  expect_equal(out_tbl$Y, out_df$Y)
  expect_equal(attr(out_tbl, "time_values"), attr(out_df, "time_values"))
})
