test_that("prior constructors validate and carry target", {
  p <- prior_normal(mean = 1, sd = 2, target = "field")
  expect_s3_class(p, "ngme_prior_spec")
  expect_equal(p$dist, "normal")
  expect_equal(unname(p$hyper), c(1, 2))
  expect_equal(p$target, "field")

  expect_error(prior_normal(sd = -1))
  expect_error(prior_pc_sd(u = 0, alpha = 0.1))
  expect_error(prior_pc_sd(u = 1, alpha = 1.2))
  expect_error(prior_half_cauchy(scale = 0))
  expect_error(prior_inv_exponential(lambda = 0))
  expect_error(prior_inv_exponential(lower = -1))
})

test_that("prior_inv_exp is an alias of prior_inv_exponential", {
  p1 <- prior_inv_exponential(lambda = 1.2, lower = 0.3, target = "coef")
  p2 <- prior_inv_exp(lambda = 1.2, lower = 0.3, target = "coef")
  expect_equal(p2, p1)
})

test_that("ngme_noise compiles named priors to internal format", {
  p <- priors(
    mu = prior_normal(0, 2, target = "coef"),
    sigma = prior_pc_sd(1, 0.1, target = "field"),
    nu = prior_inv_exponential(2, lower = 0.5, target = "coef")
  )

  noise <- ngme_noise(
    noise_type = "nig",
    theta_mu = 0,
    theta_sigma = 0,
    theta_nu = 0,
    B_mu = matrix(1, 1, 1),
    B_sigma = matrix(1, 1, 1),
    B_nu = matrix(1, 1, 1),
    prior = p
  )

  expect_equal(noise$prior_mu$type, "normal")
  expect_equal(noise$prior_mu$target, "coef")
  expect_equal(noise$prior_mu$param[2], 1 / 4)

  expect_equal(noise$prior_sigma$type, "pc.sd")
  expect_equal(noise$prior_sigma$target, "field")
  expect_equal(noise$prior_sigma$param[1], -log(0.1) / 1)

  expect_equal(noise$prior_nu$type, "inv.exponential")
  expect_equal(noise$prior_nu$target, "coef")
  expect_equal(noise$prior_nu$param[1], 2)
  expect_equal(noise$prior_nu$param[2], 0.5)
})

test_that("f compiles operator priors from new API", {
  fit <- f(
    map = 1:10,
    model = ar1(),
    noise = noise_normal(),
    prior = priors(
      theta = prior_normal(0, 2),
      rho = prior_half_cauchy(1)
    )
  )

  expect_true(inherits(fit, "ngme_model"))
  expect_equal(length(fit$prior_theta_K), length(fit$operator$theta_K))
  expect_equal(fit$prior_theta_K[[1]]$type, "half.cauchy")
  expect_equal(fit$prior_theta_K[[1]]$target, "coef")

  expect_error(
    f(
      map = 1:10,
      model = ar1(),
      noise = noise_normal(),
      prior = priors(bad_name = prior_normal(0, 1))
    ),
    "Unknown operator prior names"
  )
})

test_that("f sets default nu prior to inv.exponential using median(h)", {
  fit <- f(
    map = 1:20,
    model = ar1(),
    noise = noise_nig()
  )

  expect_equal(fit$noise$prior_nu$type, "inv.exponential")
  expect_equal(fit$noise$prior_nu$target, "coef")
  expect_equal(fit$noise$prior_nu$param[1], log(2), tolerance = 1e-12)
  expect_equal(fit$noise$prior_nu$param[2], 0)
})

test_that("f keeps explicit nu prior and does not override it", {
  fit <- f(
    map = 1:20,
    model = ar1(),
    noise = noise_nig(
      prior = priors(
        nu = prior_normal(0, 2)
      )
    )
  )

  expect_equal(fit$noise$prior_nu$type, "normal")
  expect_equal(fit$noise$prior_nu$param[2], 1 / 4)
})

test_that("f keeps normal default prior for non-stationary nu", {
  B_nu <- cbind(1, seq(0, 1, length.out = 20))
  fit <- f(
    map = 1:20,
    model = ar1(),
    noise = noise_nig(
      B_nu = B_nu,
      theta_nu = c(0, 0)
    )
  )

  expect_equal(fit$noise$prior_nu$type, "normal")
  expect_equal(fit$noise$prior_nu$param[1], 0)
  expect_equal(fit$noise$prior_nu$param[2], 0.01)
})

test_that("beta prior compilation supports global and named forms", {
  b <- compile_beta_priors(
    priors(
      beta = prior_normal(0, 2),
      x = prior_half_cauchy(1),
      feff_3 = prior_normal(1, 0.5)
    ),
    c("(Intercept)", "x", "z")
  )

  expect_equal(length(b), 3)
  expect_equal(b[[1]]$type, "normal")
  expect_equal(b[[1]]$param[2], 1 / 4)
  expect_equal(b[[2]]$type, "half.cauchy")
  expect_equal(b[[3]]$type, "normal")
  expect_equal(b[[3]]$param[1], 1)

  expect_error(
    compile_beta_priors(priors(unknown = prior_normal(0, 1)), c("a", "b")),
    "Unknown beta prior names"
  )
  expect_error(
    compile_beta_priors(priors(beta = prior_normal(0, 1, target = "field")), c("a")),
    "target='coef' only"
  )
})

test_that("default beta prior leaves intercept unconstrained", {
  b <- compile_beta_priors(NULL, c("(Intercept)", "x", "(Intercept)_u_wind", "z"))

  expect_equal(length(b), 4)
  expect_equal(b[[1]]$type, "none")
  expect_equal(b[[1]]$target, "coef")
  expect_equal(length(b[[1]]$param), 0)
  expect_equal(b[[2]]$type, "normal")
  expect_equal(b[[3]]$type, "none")
  expect_equal(b[[4]]$type, "normal")
})

test_that("ngme accepts prior_beta and stores compiled priors per replicate", {
  dat <- data.frame(y = rnorm(20), x = rnorm(20), id = 1:20)

  fit <- ngme(
    y ~ x + f(id, model = ar1(), noise = noise_normal()),
    data = dat,
    family = noise_normal(),
    prior_beta = priors(
      beta = prior_normal(0, 2),
      x = prior_half_cauchy(1)
    ),
    control_opt = control_opt(estimation = FALSE, standardize_fixed = FALSE)
  )

  pb <- fit$replicates[[1]]$prior_beta
  expect_equal(length(pb), ncol(fit$replicates[[1]]$X))
  expect_equal(pb[[2]]$type, "half.cauchy")
  expect_equal(pb[[1]]$type, "normal")
})
