library(testthat)
test_that("sobol_design basic functionality works", {
  # Basic 2D design
  design <- sobol_design(
    lower = c(a = 0, b = 0),
    upper = c(a = 1, b = 1),
    nseq = 100
  )

  expect_s3_class(design, "data.frame")
  expect_equal(nrow(design), 100)
  expect_equal(ncol(design), 2)
  expect_equal(names(design), c("a", "b"))
  expect_true(all(design$a >= 0 & design$a <= 1))
  expect_true(all(design$b >= 0 & design$b <= 1))
})

test_that("sobol_design respects parameter ranges", {
  design <- sobol_design(
    lower = c(x = -10, y = 100),
    upper = c(x = 10, y = 200),
    nseq = 50
  )

  expect_true(all(design$x >= -10 & design$x <= 10))
  expect_true(all(design$y >= 100 & design$y <= 200))
})

test_that("sobol_design handles name ordering", {
  # Different order in lower and upper
  design <- sobol_design(
    lower = c(a = 0, b = 100),
    upper = c(b = 200, a = 1),
    nseq = 10
  )

  expect_equal(names(design), c("a", "b"))
  expect_true(all(design$a >= 0 & design$a <= 1))
  expect_true(all(design$b >= 100 & design$b <= 200))
})

test_that("sobol_design validates inputs", {
  # Length mismatch
  expect_error(
    sobol_design(lower = c(a = 0), upper = c(a = 1, b = 2), nseq = 10),
    "length"
  )

  # Missing names
  expect_error(
    sobol_design(lower = c(0, 1), upper = c(1, 2), nseq = 10),
    "Assertion on 'lower' failed: Must have names."
  )

  # Name mismatch
  expect_error(
    sobol_design(lower = c(a = 0, b = 1), upper = c(a = 1, c = 2), nseq = 10),
    "names"
  )

  # Invalid nseq
  expect_error(
    sobol_design(lower = c(a = 0), upper = c(a = 1), nseq = -1),
    "nseq"
  )

  expect_error(
    sobol_design(lower = c(a = 0), upper = c(a = 1), nseq = 1.5),
    "nseq"
  )

  # Too many points
  expect_error(
    sobol_design(lower = c(a = 0), upper = c(a = 1), nseq = 2^30 + 1),
    "Assertion on 'nseq' failed"
  )

  # Empty vectors
  expect_error(
    sobol_design(lower = numeric(0), upper = numeric(0), nseq = 10),
    "Must have length >= 1, "
  )

  # Lower >= upper
  expect_error(
    sobol_design(lower = c(a = 1), upper = c(a = 0), nseq = 10),
    "lower.*less than"
  )

  # Non-finite values
  expect_error(
    sobol_design(lower = c(a = 0), upper = c(a = Inf), nseq = 10),
    "finite"
  )
})

test_that("sobol_design works with high dimensions", {
  params <- paste0("p", 1:10)
  design <- sobol_design(
    lower = setNames(rep(0, 10), params),
    upper = setNames(rep(1, 10), params),
    nseq = 100
  )

  expect_equal(nrow(design), 100)
  expect_equal(ncol(design), 10)
  expect_equal(names(design), params)
  expect_true(all(design >= 0 & design <= 1))
})

test_that("sobol_design is reproducible", {
  design1 <- sobol_design(
    lower = c(a = 0, b = 0),
    upper = c(a = 1, b = 1),
    nseq = 100
  )

  design2 <- sobol_design(
    lower = c(a = 0, b = 0),
    upper = c(a = 1, b = 1),
    nseq = 100
  )

  expect_equal(design1, design2)
})

test_that("sobol_design produces valid Sobol sequence properties", {
  # Generate design
  design <- sobol_design(
    lower = c(x = 0, y = 0),
    upper = c(x = 1, y = 1),
    nseq = 1000
  )

  # All points should be in [0, 1] range
  expect_true(all(design$x >= 0 & design$x <= 1))
  expect_true(all(design$y >= 0 & design$y <= 1))

  # Coverage test: all quadrants should be represented
  q1 <- sum(design$x < 0.5 & design$y < 0.5)
  q2 <- sum(design$x >= 0.5 & design$y < 0.5)
  q3 <- sum(design$x < 0.5 & design$y >= 0.5)
  q4 <- sum(design$x >= 0.5 & design$y >= 0.5)

  # Each quadrant should have roughly 25% of points
  expect_true(q1 > 200 && q1 < 300)
  expect_true(q2 > 200 && q2 < 300)
  expect_true(q3 > 200 && q3 < 300)
  expect_true(q4 > 200 && q4 < 300)

  # Check low discrepancy: points should be well-distributed
  # Star discrepancy should be low for Sobol sequences
  # For 1000 points in 2D, we expect good coverage
  expect_equal(nrow(design), 1000)
  expect_equal(ncol(design), 2)
})

test_that("sobol_design API matches pomp-explore expectations", {
  # This test validates that the API is compatible with pomp-explore's sobol_design

  # Example from pomp-explore tests
  design <- sobol_design(
    lower = c(a = 0, b = 100),
    upper = c(b = 200, a = 1),
    nseq = 100
  )

  # Should return data frame
  expect_s3_class(design, "data.frame")

  # Should have correct dimensions
  expect_equal(nrow(design), 100)
  expect_equal(ncol(design), 2)

  # Should preserve parameter names from lower
  expect_equal(names(design), c("a", "b"))

  # Should scale correctly
  expect_true(all(design$a >= 0 & design$a <= 1))
  expect_true(all(design$b >= 100 & design$b <= 200))
})

test_that("sobol_design matches pomp-explore reference values", {
  # Test case 1: nseq = 4
  # Expected values from pomp-explore (sorted):
  # [0.250, 0.375, 0.750, 0.875]
  design_4 <- sobol_design(
    lower = c(p = 0),
    upper = c(p = 1),
    nseq = 4
  )

  sorted_4 <- sort(design_4$p)
  expected_4 <- c(0.250, 0.375, 0.750, 0.875)

  # Check values match (with tolerance for floating point)
  expect_equal(sorted_4, expected_4, tolerance = 1e-10)

  # Test case 2: nseq = 10
  # Expected values from pomp-explore (sorted):
  # [0.06250, 0.09375, 0.18750, 0.31250, 0.43750, 0.56250,
  #  0.59375, 0.68750, 0.81250, 0.93750]
  design_10 <- sobol_design(
    lower = c(p = 0),
    upper = c(p = 1),
    nseq = 10
  )

  sorted_10 <- sort(design_10$p)
  expected_10 <- c(
    0.06250, 0.09375, 0.18750, 0.31250, 0.43750,
    0.56250, 0.59375, 0.68750, 0.81250, 0.93750
  )

  # Check values match (with tolerance for floating point)
  expect_equal(sorted_10, expected_10, tolerance = 1e-10)
})

