#' @export
print.combss <- function(x, ...) {
  cat("COMBSS fit\n")
  cat("  family:    ", x$family, "\n", sep = "")
  cat("  n, p:      ", x$n, ", ", x$p, "\n", sep = "")
  cat("  q:         ", x$q, "\n", sep = "")
  cat("  lam_ridge: ", x$lam_ridge, "\n", sep = "")
  if (!is.null(x$subset)) {
    cat("  best k:    ", length(x$subset), "\n", sep = "")
    if (!is.null(x$mse))
      cat("  mse:       ", signif(x$mse, 5), "\n", sep = "")
    if (!is.null(x$accuracy))
      cat("  accuracy:  ", signif(x$accuracy, 5), "\n", sep = "")
    cat("  subset:    ", paste(x$subset, collapse = ", "), "\n", sep = "")
  } else {
    cat("  (no validation data; subset_list only)\n")
  }
  invisible(x)
}

#' @export
summary.combss <- function(object, ...) {
  print(object)
  cat("\nSubset path:\n")
  for (k in seq_along(object$subset_list)) {
    feats <- object$subset_list[[k]]
    line <- sprintf("  k=%2d  features: %s", k, paste(feats, collapse = ","))
    if (!is.null(object$mse_path))
      line <- paste0(line, sprintf("  mse=%.4f", object$mse_path[k]))
    if (!is.null(object$accuracy_path))
      line <- paste0(line, sprintf("  acc=%.4f", object$accuracy_path[k]))
    cat(line, "\n")
  }
  invisible(object)
}

#' Extract the subset of selected feature indices.
#'
#' @param object A `combss` object.
#' @param k Subset size to return. Defaults to the best `k` if validation
#'   data was used, else the largest `k` evaluated.
#' @param ... Unused.
#' @return Integer vector of selected column indices (1-indexed).
#' @examples
#' set.seed(1)
#' x <- matrix(rnorm(100 * 20), 100, 20)
#' y <- as.numeric(x[, 1] + x[, 2] + rnorm(100) * 0.1)
#' fit <- combss(x, y, family = "gaussian", q = 5)
#' coef(fit, k = 2)
#' @export
coef.combss <- function(object, k = NULL, ...) {
  if (is.null(k)) {
    k <- if (!is.null(object$best_k)) object$best_k else object$q
  }
  object$subset_list[[k]]
}

#' Predict from a `combss` fit.
#'
#' Refits the chosen subset on the original training data via OLS or ridge
#' (Gaussian / binomial / multinomial as appropriate) and predicts on `newx`.
#' Note: the training data is not stored on the fit object, so `newx` must be
#' supplied along with `x_train` and `y_train`.
#'
#' @param object A `combss` object.
#' @param newx Numeric matrix of new observations.
#' @param x_train,y_train Original training data used to refit on the chosen
#'   subset.
#' @param k Subset size to use. Defaults to the best `k` if validation data
#'   was used, else the largest `k` evaluated.
#' @param ... Unused.
#' @return Numeric vector of predictions for `newx`.
#' @examples
#' set.seed(1)
#' x <- matrix(rnorm(100 * 20), 100, 20)
#' y <- as.numeric(x[, 1] + x[, 2] + rnorm(100) * 0.1)
#' fit <- combss(x, y, family = "gaussian", q = 5)
#' newx <- matrix(rnorm(5 * 20), 5, 20)
#' predict(fit, newx = newx, x_train = x, y_train = y, k = 2)
#' @export
predict.combss <- function(object, newx, x_train, y_train, k = NULL, ...) {
  if (missing(x_train) || missing(y_train))
    stop("`x_train` and `y_train` are required to refit on the chosen subset.",
         call. = FALSE)
  if (is.null(k)) {
    k <- if (!is.null(object$best_k)) object$best_k else object$q
  }
  idx <- object$subset_list[[k]]
  refit_predict(as.matrix(x_train), y_train, as.matrix(newx),
                idx, object$family)
}
