test_that("simulate_dcvar_sem returns correct structure", {
  sim <- simulate_dcvar_sem(n_time = 50, rho = 0.5, seed = 1)

  expect_type(sim, "list")
  expect_named(sim, c("data", "true_params", "latent_states", "innovations"))
  expect_s3_class(sim$data, "data.frame")
})

test_that("simulate_dcvar_sem data has correct dimensions", {
  J <- 3
  n_time_val <- 50
  sim <- simulate_dcvar_sem(n_time = n_time_val, J = J, rho = 0.5, seed = 2)

  # Data: T rows, 1 time column + 2*J indicator columns
  expect_equal(nrow(sim$data), n_time_val)
  expect_equal(ncol(sim$data), 1 + 2 * J)
  expect_true("time" %in% names(sim$data))
})

test_that("simulate_dcvar_sem indicator column names are correct", {
  J <- 3
  sim <- simulate_dcvar_sem(n_time = 40, J = J, rho = 0.3, seed = 3)

  expected_cols <- c("time",
                     paste0("y1_", seq_len(J)),
                     paste0("y2_", seq_len(J)))
  expect_equal(names(sim$data), expected_cols)
})

test_that("simulate_dcvar_sem latent_states has correct dimensions", {
  n_time_val <- 60
  sim <- simulate_dcvar_sem(n_time = n_time_val, rho = 0.5, seed = 4)

  expect_equal(nrow(sim$latent_states), n_time_val)
  expect_equal(ncol(sim$latent_states), 2)
})

test_that("simulate_dcvar_sem innovations has correct dimensions", {
  n_time_val <- 60
  sim <- simulate_dcvar_sem(n_time = n_time_val, rho = 0.5, seed = 5)

  expect_equal(nrow(sim$innovations), n_time_val)
  expect_equal(ncol(sim$innovations), 2)
})

test_that("simulate_dcvar_sem true_params contains expected elements", {
  J <- 3
  sim <- simulate_dcvar_sem(n_time = 50, J = J, rho = 0.4, seed = 6)

  expect_named(sim$true_params,
               c("Phi", "mu", "margins", "rho", "lambda", "sigma_e", "J", "sigma"))
  expect_equal(sim$true_params$rho, 0.4)
  expect_equal(sim$true_params$J, J)
  expect_equal(length(sim$true_params$lambda), J)
  expect_equal(dim(sim$true_params$Phi), c(2, 2))
  expect_equal(sim$true_params$margins, "normal")
})

test_that("simulate_dcvar_sem errors when lambda length mismatches J", {
  expect_error(
    simulate_dcvar_sem(n_time = 50, J = 3, lambda = c(0.8, 0.8), rho = 0.5),
    "lambda"
  )
  expect_error(
    simulate_dcvar_sem(n_time = 50, J = 2, lambda = c(0.8, 0.8, 0.8), rho = 0.5),
    "lambda"
  )
})

test_that("simulate_dcvar_sem validates lambda finiteness and sigma_e", {
  expect_error(
    simulate_dcvar_sem(n_time = 50, J = 2, lambda = c(0.8, NA_real_), rho = 0.5),
    "lambda"
  )
  expect_error(
    simulate_dcvar_sem(n_time = 50, J = 2, lambda = c(0.8, 0.8), sigma_e = 0, rho = 0.5),
    "sigma_e"
  )
})

test_that("simulate_dcvar_sem validates Phi and sigma shapes", {
  expect_error(
    simulate_dcvar_sem(n_time = 20, Phi = matrix(1:3, nrow = 1), rho = 0.5),
    "Phi"
  )
  expect_error(
    simulate_dcvar_sem(n_time = 20, sigma = c(1, 1, 1), rho = 0.5),
    "sigma"
  )
})

test_that("simulate_dcvar_sem supports exponential latent margins", {
  sim <- simulate_dcvar_sem(
    n_time = 40,
    J = 2,
    lambda = rep(sqrt(0.8), 2),
    margins = "exponential",
    sigma_exp = c(0.7, 1.1),
    skew_direction = c(1, -1),
    rho = 0.25,
    seed = 12
  )

  expect_equal(sim$true_params$margins, "exponential")
  expect_equal(sim$true_params$sigma_exp, c(0.7, 1.1))
  expect_equal(sim$true_params$skew_direction, c(1, -1))
  expect_true(all(is.finite(sim$innovations)))
})

test_that("simulate_dcvar_sem records the scale used for skew-normal and gamma innovations", {
  skip_if_not_installed("sn")

  sim_sn <- simulate_dcvar_sem(
    n_time = 8000,
    J = 1,
    lambda = 1,
    sigma_e = 1e-12,
    Phi = matrix(0, 2, 2),
    margins = "skew_normal",
    sigma = c(2, 2.5),
    skew_params = list(alpha = c(0, 0)),
    rho = 0,
    seed = 21
  )
  expect_equal(sim_sn$true_params$sigma, c(2, 2.5))
  expect_equal(unname(apply(sim_sn$innovations, 2, stats::sd)), sim_sn$true_params$sigma, tolerance = 0.06)

  sim_gam <- simulate_dcvar_sem(
    n_time = 8000,
    J = 1,
    lambda = 1,
    sigma_e = 1e-12,
    Phi = matrix(0, 2, 2),
    margins = "gamma",
    sigma = c(2, 2.5),
    skew_direction = c(1, 1),
    skew_params = list(shape = 2),
    rho = 0,
    seed = 22
  )
  expect_equal(sim_gam$true_params$sigma, c(2, 2.5))
  expect_equal(unname(apply(sim_gam$innovations, 2, stats::sd)), sim_gam$true_params$sigma, tolerance = 0.08)
})

test_that("simulate_dcvar_sem exponential margins require skew_direction", {
  expect_error(
    simulate_dcvar_sem(n_time = 20, margins = "exponential", sigma_exp = c(1, 1)),
    "skew_direction"
  )
})

test_that("simulate_dcvar_sem works with different J values", {
  for (J in c(2, 4, 5)) {
    sim <- simulate_dcvar_sem(n_time = 40, J = J,
                              lambda = rep(0.8, J),
                              rho = 0.5, seed = 100 + J)
    expect_equal(ncol(sim$data), 1 + 2 * J, info = paste("J =", J))
    expect_equal(nrow(sim$data), 40, info = paste("J =", J))
    expect_equal(length(sim$true_params$lambda), J, info = paste("J =", J))
  }
})

test_that("simulate_dcvar_sem is reproducible with seed", {
  s1 <- simulate_dcvar_sem(n_time = 30, rho = 0.5, seed = 42)
  s2 <- simulate_dcvar_sem(n_time = 30, rho = 0.5, seed = 42)
  expect_identical(s1$data, s2$data)
  expect_identical(s1$latent_states, s2$latent_states)
})

test_that("simulate_dcvar_sem warns that burnin is ignored", {
  expect_warning(
    simulate_dcvar_sem(n_time = 20, burnin = 10, seed = 11),
    "ignored"
  )
})
