test_that("compute_log_returns returns correct values on valid input", {
  px <- c(100, 101, 103, 102)

  ret <- compute_log_returns(px)

  expect_type(ret, "double")
  expect_equal(length(ret), 3L)
  expect_equal(
    ret,
    c(log(101 / 100), log(103 / 101), log(102 / 103)),
    tolerance = 1e-12
  )
})

test_that("compute_log_returns rejects non-numeric input", {
  expect_error(
    compute_log_returns(c("100", "101", "102")),
    regexp = "numeric",
    ignore.case = TRUE
  )
})

test_that("compute_log_returns rejects too-short input", {
  expect_error(
    compute_log_returns(100),
    regexp = "at least|length|observations",
    ignore.case = TRUE
  )
})

test_that("compute_log_returns rejects non-finite input", {
  expect_error(
    compute_log_returns(c(100, NA, 101)),
    regexp = "finite|missing|NA",
    ignore.case = TRUE
  )

  expect_error(
    compute_log_returns(c(100, Inf, 101)),
    regexp = "finite|Inf",
    ignore.case = TRUE
  )
})

test_that("compute_log_returns rejects non-positive prices", {
  expect_error(
    compute_log_returns(c(100, 0, 101)),
    regexp = "positive|non-positive|price",
    ignore.case = TRUE
  )

  expect_error(
    compute_log_returns(c(100, -1, 101)),
    regexp = "positive|non-positive|price",
    ignore.case = TRUE
  )
})

test_that("fit_hmm_em accepts valid gaussian interface inputs", {
  skip_on_cran()

  set.seed(100)
  x <- rnorm(80, mean = 0, sd = 0.01)

  fit <- fit_hmm_em(
    x = x,
    K = 2L,
    model = "gaussian",
    n_starts = 1L,
    max_iter = 8L,
    tol = 1e-4,
    verbose = FALSE
  )

  expect_type(fit, "list")
  expect_true(is.numeric(fit$logLik))
  expect_true(length(fit$logLik) == 1L)
  expect_true(is.finite(fit$logLik))
  expect_equal(dim(fit$A), c(2L, 2L))
  expect_true(is.list(fit$params))
})

test_that("fit_hmm_em accepts valid student interface inputs", {
  skip_on_cran()

  set.seed(101)
  x <- rnorm(80, mean = 0, sd = 0.01)

  fit <- fit_hmm_em(
    x = x,
    K = 2L,
    model = "student",
    n_starts = 1L,
    max_iter = 8L,
    tol = 1e-4,
    nu_grid = c(3, 5, 10),
    verbose = FALSE
  )

  expect_type(fit, "list")
  expect_true(is.numeric(fit$logLik))
  expect_true(length(fit$logLik) == 1L)
  expect_true(is.finite(fit$logLik))
  expect_equal(dim(fit$A), c(2L, 2L))
  expect_true(is.list(fit$params))
  expect_true("nu" %in% names(fit$params))
})

test_that("fit_hmm_em rejects non-numeric x", {
  expect_error(
    fit_hmm_em(
      x = c("a", "b", "c"),
      K = 2L,
      model = "gaussian",
      n_starts = 1L,
      max_iter = 5L,
      tol = 1e-4,
      verbose = FALSE
    ),
    regexp = "numeric",
    ignore.case = TRUE
  )
})

test_that("fit_hmm_em rejects too-short x", {
  expect_error(
    fit_hmm_em(
      x = c(0.01, -0.01),
      K = 2L,
      model = "gaussian",
      n_starts = 1L,
      max_iter = 5L,
      tol = 1e-4,
      verbose = FALSE
    ),
    regexp = "at least|length|observations",
    ignore.case = TRUE
  )
})

test_that("fit_hmm_em rejects non-finite x", {
  expect_error(
    fit_hmm_em(
      x = c(0.01, NA, 0.02, 0.03),
      K = 2L,
      model = "gaussian",
      n_starts = 1L,
      max_iter = 5L,
      tol = 1e-4,
      verbose = FALSE
    ),
    regexp = "finite|missing|NA",
    ignore.case = TRUE
  )
})

test_that("fit_hmm_em rejects invalid model", {
  set.seed(102)
  x <- rnorm(50, mean = 0, sd = 0.01)

  expect_error(
    fit_hmm_em(
      x = x,
      K = 2L,
      model = "invalid_model",
      n_starts = 1L,
      max_iter = 5L,
      tol = 1e-4,
      verbose = FALSE
    ),
    regexp = "model|gaussian|student",
    ignore.case = TRUE
  )
})

test_that("fit_hmm_em rejects invalid K", {
  set.seed(103)
  x <- rnorm(50, mean = 0, sd = 0.01)

  expect_error(
    fit_hmm_em(
      x = x,
      K = 0L,
      model = "gaussian",
      n_starts = 1L,
      max_iter = 5L,
      tol = 1e-4,
      verbose = FALSE
    ),
    regexp = "K|positive|integer",
    ignore.case = TRUE
  )

  expect_error(
    fit_hmm_em(
      x = x,
      K = -1L,
      model = "gaussian",
      n_starts = 1L,
      max_iter = 5L,
      tol = 1e-4,
      verbose = FALSE
    ),
    regexp = "K|positive|integer",
    ignore.case = TRUE
  )
})

test_that("fit_hmm_em rejects invalid n_starts and max_iter", {
  set.seed(104)
  x <- rnorm(50, mean = 0, sd = 0.01)

  expect_error(
    fit_hmm_em(
      x = x,
      K = 2L,
      model = "gaussian",
      n_starts = 0L,
      max_iter = 5L,
      tol = 1e-4,
      verbose = FALSE
    ),
    regexp = "n_starts|positive|integer",
    ignore.case = TRUE
  )

  expect_error(
    fit_hmm_em(
      x = x,
      K = 2L,
      model = "gaussian",
      n_starts = 1L,
      max_iter = 0L,
      tol = 1e-4,
      verbose = FALSE
    ),
    regexp = "max_iter|positive|integer",
    ignore.case = TRUE
  )
})

test_that("fit_hmm_em rejects invalid tol", {
  set.seed(105)
  x <- rnorm(50, mean = 0, sd = 0.01)

  expect_error(
    fit_hmm_em(
      x = x,
      K = 2L,
      model = "gaussian",
      n_starts = 1L,
      max_iter = 5L,
      tol = 0,
      verbose = FALSE
    ),
    regexp = "tol|positive",
    ignore.case = TRUE
  )

  expect_error(
    fit_hmm_em(
      x = x,
      K = 2L,
      model = "gaussian",
      n_starts = 1L,
      max_iter = 5L,
      tol = -1e-4,
      verbose = FALSE
    ),
    regexp = "tol|positive",
    ignore.case = TRUE
  )
})

test_that("fit_hmm_em rejects invalid nu_grid for student model", {
  set.seed(106)
  x <- rnorm(50, mean = 0, sd = 0.01)

  expect_error(
    fit_hmm_em(
      x = x,
      K = 2L,
      model = "student",
      n_starts = 1L,
      max_iter = 5L,
      tol = 1e-4,
      nu_grid = c(NA, 5, 10),
      verbose = FALSE
    ),
    regexp = "nu_grid|finite|numeric",
    ignore.case = TRUE
  )

  expect_error(
    fit_hmm_em(
      x = x,
      K = 2L,
      model = "student",
      n_starts = 1L,
      max_iter = 5L,
      tol = 1e-4,
      nu_grid = numeric(0),
      verbose = FALSE
    ),
    regexp = "nu_grid|length|empty",
    ignore.case = TRUE
  )
})