test_that("janusplot_shape_sensitivity_shapes returns 14 canonical names", {
  shapes <- janusplot_shape_sensitivity_shapes()
  expect_type(shapes, "character")
  expect_length(shapes, 14L)
  expect_true(all(shapes %in% janusplot_shape_hierarchy()$category))
})

test_that("janusplot_shape_sensitivity runs and returns a well-formed data frame", {
  res <- janusplot_shape_sensitivity(
    shapes     = c("linear_up", "u_shape", "wave"),
    n_grid     = c(100L, 200L),
    sigma_grid = c(0.05, 0.20),
    n_rep      = 3L,
    verbose    = FALSE
  )
  expect_s3_class(res, "data.frame")
  expect_equal(nrow(res), 3L * 2L * 2L * 3L)
  expect_true(all(c("truth", "n", "sigma", "seed", "predicted",
                    "correct", "archetype_truth", "archetype_pred",
                    "archetype_correct",
                    "monotonicity_index", "convexity_index",
                    "n_turn", "n_inflect", "error") %in% names(res)))
  # Every truth in, every truth accounted for
  expect_setequal(unique(res$truth), c("linear_up", "u_shape", "wave"))
})

test_that("rejects unknown shapes / malformed grids", {
  expect_error(
    janusplot_shape_sensitivity(shapes = "not_a_shape", n_rep = 2L,
                                verbose = FALSE),
    regexp = "Unknown"
  )
  expect_error(
    janusplot_shape_sensitivity(n_grid = c(3L), n_rep = 2L,
                                verbose = FALSE),
    regexp = "n_grid"
  )
  expect_error(
    janusplot_shape_sensitivity(sigma_grid = c(-0.1), n_rep = 2L,
                                verbose = FALSE),
    regexp = "sigma_grid"
  )
  expect_error(
    janusplot_shape_sensitivity(n_rep = 0L, verbose = FALSE),
    regexp = "n_rep"
  )
})

test_that("summary returns the expected accuracy table", {
  res <- janusplot_shape_sensitivity(
    shapes     = c("linear_up", "u_shape"),
    n_grid     = c(100L),
    sigma_grid = c(0.05, 0.20),
    n_rep      = 3L,
    verbose    = FALSE
  )
  fine <- janusplot_shape_sensitivity_summary(res, "fine")
  arch <- janusplot_shape_sensitivity_summary(res, "archetype")
  expect_true(all(c("truth", "n", "sigma", "accuracy") %in% names(fine)))
  expect_true(all(c("truth", "n", "sigma", "accuracy") %in% names(arch)))
  expect_equal(nrow(fine), 2L * 1L * 2L)
  expect_true(all(fine$accuracy    >= 0 & fine$accuracy    <= 1))
  expect_true(all(arch$accuracy    >= 0 & arch$accuracy    <= 1))
})

test_that("all four plot types render to ggplot objects", {
  data("shape_sensitivity_demo", package = "janusplot",
       envir = environment())
  for (tp in c("confusion_fine", "confusion_archetype",
               "accuracy_grid", "recovery_curves")) {
    p <- janusplot_shape_sensitivity_plot(shape_sensitivity_demo, tp)
    expect_s3_class(p, "ggplot")
  }
})

test_that("plot rejects unknown types", {
  data("shape_sensitivity_demo", package = "janusplot",
       envir = environment())
  expect_error(
    janusplot_shape_sensitivity_plot(shape_sensitivity_demo, "bogus"),
    class = "rlang_error"
  )
})

test_that("linear_up is recognised as monotone at moderate n, low noise", {
  # Sanity bound on H1-style behaviour for the easiest shape. The
  # REML spline may pick up tiny curvature from noisy linear data
  # (landing on convex_up / concave_up — still monotone increasing),
  # so we test the coarser `shape_monotonic` tier: the classifier
  # must at least recognise the truth as monotone.
  res <- janusplot_shape_sensitivity(
    shapes     = "linear_up",
    n_grid     = 500L,
    sigma_grid = 0.05,
    n_rep      = 20L,
    verbose    = FALSE
  )
  monot_pred <- janusplot:::.shape_lookup(res$predicted, "monotonic")
  expect_gte(mean(monot_pred == "monotone", na.rm = TRUE), 0.95)
})
