test_that("dcvar_stan_path returns valid file paths", {
  for (m in c("dcvar", "dcvar_dynamic", "hmm", "constant", "multilevel", "sem")) {
    path <- dcvar_stan_path(m)
    expect_true(file.exists(path), info = paste("model:", m))
    expect_true(grepl("\\.stan$", path), info = paste("model:", m))
  }
  expect_match(basename(dcvar_stan_path("dcvar_dynamic")), "^dcvar_dynamic\\.stan$")

  sem_exp <- dcvar_stan_path("sem", margins = "exponential")
  expect_true(file.exists(sem_exp))
  expect_match(basename(sem_exp), "^sem_EG\\.stan$")

  clayton_constant <- dcvar_stan_path("constant", copula = "clayton")
  expect_true(file.exists(clayton_constant))
  expect_match(basename(clayton_constant), "^constant_NCl\\.stan$")

  ml_exp <- dcvar_stan_path("multilevel", margins = "exponential")
  expect_true(file.exists(ml_exp))
  expect_match(basename(ml_exp), "^multilevel_EG\\.stan$")

  sem_naive <- dcvar_stan_path("sem_naive")
  expect_true(file.exists(sem_naive))
  expect_match(basename(sem_naive), "^sem_naive_NG\\.stan$")
})

test_that("dcvar_stan_path rejects invalid model names", {
  expect_error(dcvar_stan_path("invalid"))
})

test_that(".resolve_backend() resolves auto to rstan", {
  expect_equal(.resolve_backend("auto"), "rstan")
})

test_that(".validate_sampling_args requires scalar finite integer counts", {
  expect_invisible(.validate_sampling_args(1, 1, 1, 0.9, 10))

  expect_error(.validate_sampling_args(c(1, 2), 1, 1, 0.9, 10), "chains")
  expect_error(.validate_sampling_args(1.5, 1, 1, 0.9, 10), "chains")
  expect_error(.validate_sampling_args(1, NA_real_, 1, 0.9, 10), "iter_warmup")
  expect_error(.validate_sampling_args(1, 1, Inf, 0.9, 10), "iter_sampling")
  expect_error(.validate_sampling_args(1, 1, 1, c(0.8, 0.9), 10), "adapt_delta")
  expect_error(.validate_sampling_args(1, 1, 1, NaN, 10), "adapt_delta")
  expect_error(.validate_sampling_args(1, 1, 1, 0.9, 10.5), "max_treedepth")
})

test_that(".compiled_exe_path uses the correct platform suffix", {
  path <- .compiled_exe_path(tempdir(), "dcvar-model")

  if (.Platform$OS.type == "windows") {
    expect_match(path, "\\.exe$")
  } else {
    expect_false(grepl("\\.exe$", path, ignore.case = TRUE))
  }
})

test_that(".compile_model_backend() rejects missing Stan source files", {
  missing_file <- file.path(tempdir(), "does-not-exist.stan")
  expect_error(
    .compile_model_backend(missing_file, backend = "rstan"),
    "does not exist"
  )
})

test_that(".stan_include_paths() keeps custom and bundled include roots", {
  custom_dir <- tempfile("custom-stan-dir")
  dir.create(custom_dir)
  custom_file <- file.path(custom_dir, "model.stan")
  package_dir <- system.file("stan", package = "dcvar")
  custom_dir_norm <- normalizePath(custom_dir, winslash = "/", mustWork = TRUE)
  package_dir_norm <- normalizePath(package_dir, winslash = "/", mustWork = TRUE)

  include_paths <- .stan_include_paths(custom_file, package_dir)

  expect_equal(include_paths[[1]], custom_dir_norm)
  expect_true(package_dir_norm %in% include_paths)
  expect_equal(length(include_paths), length(unique(include_paths)))
})

test_that(".stan_cache_fingerprint changes when recursively included files change", {
  root <- tempfile("stan-cache-root")
  dir.create(file.path(root, "functions"), recursive = TRUE)

  model_file <- file.path(root, "model.stan")
  shared_file <- file.path(root, "functions", "shared.stan")
  nested_file <- file.path(root, "functions", "nested.stan")

  writeLines("#include functions/shared.stan", model_file)
  writeLines("#include functions/nested.stan", shared_file)
  writeLines("functions { real nested() { return 1; } }", nested_file)

  on.exit(unlink(root, recursive = TRUE), add = TRUE)

  fingerprint_1 <- .stan_cache_fingerprint(model_file, c(root, system.file("stan", package = "dcvar")))
  writeLines("functions { real nested() { return 2; } }", nested_file)
  fingerprint_2 <- .stan_cache_fingerprint(model_file, c(root, system.file("stan", package = "dcvar")))

  expect_false(identical(fingerprint_1, fingerprint_2))
})

test_that(".stan_cache_fingerprint respects include search path order", {
  model_root <- tempfile("stan-cache-model")
  extra_root <- tempfile("stan-cache-extra")
  package_root <- tempfile("stan-cache-package")

  dir.create(model_root)
  dir.create(file.path(extra_root, "functions"), recursive = TRUE)
  dir.create(file.path(package_root, "functions"), recursive = TRUE)

  model_file <- file.path(model_root, "model.stan")
  writeLines("#include functions/shared.stan", model_file)
  writeLines("functions { real shared() { return 1; } }", file.path(extra_root, "functions", "shared.stan"))
  writeLines("functions { real shared() { return 2; } }", file.path(package_root, "functions", "shared.stan"))

  on.exit(unlink(c(model_root, extra_root, package_root), recursive = TRUE), add = TRUE)

  fingerprint_1 <- .stan_cache_fingerprint(model_file, c(extra_root, package_root))
  fingerprint_2 <- .stan_cache_fingerprint(model_file, c(package_root, extra_root))

  expect_false(identical(fingerprint_1, fingerprint_2))
})

test_that(".fit_summary() assigns stable names to unnamed summary functions", {
  draws <- posterior::as_draws_array(
    array(
      rnorm(40),
      dim = c(10, 1, 4),
      dimnames = list(NULL, NULL, c("phi_unit[1,1]", "phi_unit[1,2]", "phi_unit[1,3]", "phi_unit[1,4]"))
    )
  )

  summ <- .fit_summary(
    draws,
    variables = NULL,
    backend = "rstan",
    required = paste0("phi_unit[1,", 1:4, "]"),
    required_type = "exact",
    context = "test",
    output_type = "parameter",
    mean, sd,
    ~posterior::quantile2(.x, probs = c(0.025, 0.975))
  )

  expect_true(all(c("variable", "mean", "sd", "q2.5", "q97.5") %in% names(summ)))
})

test_that("all bundled top-level Stan files translate successfully", {
  skip_if_no_cmdstanr_toolchain()

  stanc <- file.path(cmdstanr::cmdstan_path(), "bin", "stanc")
  stan_dir <- system.file("stan", package = "dcvar")
  stan_files <- list.files(stan_dir, pattern = "\\.stan$", full.names = TRUE, recursive = TRUE)
  stan_files <- stan_files[!grepl("/functions/", stan_files, fixed = TRUE)]

  for (stan_file in stan_files) {
    output_file <- tempfile(
      pattern = paste0(tools::file_path_sans_ext(basename(stan_file)), "_"),
      fileext = ".hpp"
    )
    on.exit(unlink(output_file), add = TRUE)

    result <- system2(
      stanc,
      c("--include-paths", stan_dir, "--output", output_file, normalizePath(stan_file)),
      stdout = TRUE,
      stderr = TRUE
    )
    status <- attr(result, "status")
    if (is.null(status)) status <- 0L
    expect_equal(status, 0L, info = paste(stan_file, paste(result, collapse = "\n")))
    expect_true(file.exists(output_file), info = paste("missing output for", stan_file))
  }
})
