# tests/testthat/test-makeScales.R

test_that("makeScales: basic generation works and respects shapes, names, bounds", {
  # Ensure testthat is loaded
  if (!requireNamespace("LikertMakeR", quietly = TRUE)) {
    skip("LikertMakeR not installed; skipping this test.")
  }

  set.seed(123)

  n <- 128L
  k <- 4L
  means <- c(2.5, 3.0, 3.0, 3.5)
  sds <- c(1.0, 1.0, 1.5, 0.75)
  lb <- rep(1L, k)
  ub <- rep(5L, k)


  corMat <- matrix(c(
    1.00, 0.30, 0.40, 0.60,
    0.30, 1.00, 0.50, 0.70,
    0.40, 0.50, 1.00, 0.80,
    0.60, 0.70, 0.80, 1.00
  ), nrow = k, byrow = TRUE)
  dimnames(corMat) <- list(paste0("Q", 1:k), paste0("Q", 1:k))


  df <- makeScales(
    n = n, means = means, sds = sds,
    lowerbound = lb, upperbound = ub, cormatrix = corMat
  )

  # structure
  testthat::expect_true(is.data.frame(df))
  testthat::expect_identical(nrow(df), n)
  testthat::expect_identical(ncol(df), k)
  testthat::expect_identical(colnames(df), colnames(corMat))

  # bounds respected
  for (j in seq_len(k)) {
    testthat::expect_true(all(df[[j]] >= lb[j] & df[[j]] <= ub[j]))
  }

  # moments are in the ballpark (tolerances reflect discreteness and finite n)
  m_hat <- vapply(df, mean, numeric(1))
  s_hat <- vapply(df, stats::sd, numeric(1))

  testthat::expect_true(all(abs(m_hat - means) <= 0.25))
  testthat::expect_true(all(abs(s_hat - sds) <= 0.35))

  # correlations roughly match target (off-diagonal)
  Rhat <- stats::cor(df)
  offdiag <- function(M) M[upper.tri(M)]
  testthat::expect_true(all(abs(offdiag(Rhat) - offdiag(corMat)) <= 0.15))
})

testthat::test_that("makeScales: scalar lower/upper/items recycle correctly", {
  set.seed(42)
  n <- 150L
  k <- 5L

  means <- rep(3, k)
  sds <- rep(1, k)
  lb <- 1L # scalar, should recycle
  ub <- 7L # scalar, should recycle
  items <- 3L # scalar, should recycle

  corMat <- diag(k)
  dimnames(corMat) <- list(LETTERS[1:k], LETTERS[1:k])

  df <- makeScales(
    n = n, means = means, sds = sds,
    lowerbound = lb, upperbound = ub, items = items,
    cormatrix = corMat
  )

  testthat::expect_true(is.data.frame(df))
  testthat::expect_identical(nrow(df), n)
  testthat::expect_identical(ncol(df), k)
  testthat::expect_identical(colnames(df), colnames(corMat))
  for (j in seq_len(k)) {
    testthat::expect_true(all(df[[j]] >= 1L & df[[j]] <= 7L))
  }
})


testthat::test_that("makeScales: rejects non-PSD correlation matrices early with a clear error", {
  set.seed(99)
  n <- 50L
  means <- c(3, 3, 3)
  sds <- c(1, 1, 1)
  lb <- c(1, 1, 1)
  ub <- c(5, 5, 5)

  badCor <- matrix(c(
    1, -0.9, -0.9,
    -0.9, 1, -0.9,
    -0.9, -0.9, 1
  ), nrow = 3, byrow = TRUE)

  testthat::expect_error(
    makeScales(n, means, sds, lb, ub, cormatrix = badCor),
    regexp = "not Positive Semi-Definite|Requested correlations are not possible"
  )
})


testthat::test_that("makeScales: mismatched lengths and dimensions fail fast with a descriptive error", {
  set.seed(1)
  n <- 40L
  means <- c(3, 3, 3) # length 3
  sds <- c(1, 1) # length 2 -> mismatch
  lb <- 1
  ub <- 5
  items <- 5L
  corMat <- diag(3)

  testthat::expect_error(
    makeScales(n, means, sds, lb, ub, items, cormatrix = corMat),
    regexp = "sds' must be a numeric vector of positive.*finite values of length 3"
  )
})


testthat::test_that("makeScales: NA/Inf inputs are rejected", {
  # testthat::skip("Enable after adding explicit NA/Inf/type checks that stop().")

  n <- 20L
  means <- c(3, NA)
  sds <- c(1, 1)
  lb <- c(1, 1)
  ub <- c(5, 5)
  corMat <- diag(2)

  testthat::expect_error(
    makeScales(n, means, sds, lb, ub, cormatrix = corMat),
    regexp = "missing|NA|finite|numeric"
  )

  means2 <- c(3, Inf)
  testthat::expect_error(
    makeScales(n, means2, sds, lb, ub, cormatrix = corMat),
    regexp = "finite"
  )
})

testthat::test_that("makeScales: lowerbound < upperbound and within-range means are enforced", {
  # testthat::skip("Enable after adding bound order and mean-range checks that stop().")

  n <- 30L
  means <- c(6, 2) # first mean outside [lb, ub] if lb=1,ub=5
  sds <- c(1, 1)
  lb <- c(1, 3)
  ub <- c(5, 2) # invalid: lb >= ub in 2nd position
  corMat <- diag(2)

  testthat::expect_error(
    makeScales(n, means, sds, lb, ub, cormatrix = corMat),
    regexp = "All 'lowerbound' values must be strictly less than the corresponding 'upperbound' values."
  )


  lb2 <- c(1, 1)
  ub2 <- c(5, 5)
  testthat::expect_error(
    makeScales(n, means, sds, lb2, ub2, cormatrix = corMat),
    regexp = "means.*within.*bounds|outside"
  )
})
