# --- 10-fold CV to choose best h from grid --------------------------------
# X: n x d (rows = obs, cols = covariates)
# y: length n
# h_grid: numeric vector of candidate bandwidths
# nfolds: default 10
# seed: optional for reproducible folds
ARCHmX_nw_cv_bandwidth <- function(X, y, h_grid, nfolds = 10, seed = NULL, shuffle = TRUE) {
  stopifnot(is.matrix(X), length(y) == nrow(X), length(h_grid) >= 1)
  
  n <- nrow(X)
  idx <- seq_len(n)
  if (shuffle) {
    if (!is.null(seed)) set.seed(seed)
    idx <- sample.int(n)
  }
  # make folds as contiguous chunks of idx
  folds <- split(idx, rep_len(seq_len(nfolds), n))
  
  # CV error for each h (MSE, weighted by fold sizes)
  cv_mse <- numeric(length(h_grid))
  
  for (k in seq_along(h_grid)) {
    h <- h_grid[k]
    se_sum <- 0
    n_sum  <- 0
    
    for (f in seq_along(folds)) {
      val_idx <- folds[[f]]
      train_idx <- setdiff(seq_len(n), val_idx)
      
      Xtr <- X[train_idx, , drop = FALSE]
      ytr <- y[train_idx]
      Xva <- X[val_idx, , drop = FALSE]
      yva <- y[val_idx]
      
      yhat <- ARCHmX_predict_nw_iso(Xtr, ytr, Xva, h)
      se_sum <- se_sum + sum((yva - yhat)^2)
      n_sum  <- n_sum  + length(val_idx)
    }
    
    cv_mse[k] <- se_sum / n_sum
  }
  
  best_idx <- which.min(cv_mse)
  list(
    best_h = h_grid[best_idx],
    cv_mse = cv_mse,
    h_grid = h_grid,
    predict = function(Xnew, Xtrain = X, ytrain = y) {
      # convenience predictor using the chosen h
      ARCHmX_predict_nw_iso(Xtrain, ytrain, Xnew, h_grid[best_idx])
    }
  )
}