#' Predict method for xreg2 objects
#'
#' @title Predict from an xreg2 Model
#' @description Generates predictions (linear predictors and intermediate
#'   computed variables) from a fitted \code{xreg2} model applied to new data.
#'
#' @param object A fitted \code{xreg2} object.
#' @param newdata A named list of data frames (one per model component), or a
#'   single data frame (automatically wrapped and named after the first
#'   component).
#' @param return_vector Logical; if \code{TRUE} and there is a single
#'   component, return a numeric vector of \code{Xb} values instead of a
#'   list. Default \code{FALSE}.
#' @param ... Currently unused.
#'
#' @return A named list of data frames (one per component), each augmented
#'   with computed variables including \code{Xb}.
#'
#' @examples
#' \donttest{
#' set.seed(1)
#' df   <- data.frame(y = rnorm(200, 2, 0.5), x = rnorm(200))
#' ctrl <- xreg2_control(
#'   formulas     = list(y ~ INTERCEPT + x * SLOPE),
#'   start_values = c(INTERCEPT = 0, SLOPE = 0),
#'   name         = "main"
#' )
#' fit  <- xreg2_fit(controlList = c(ctrl), dataList = list(main = df))
#' newdf <- data.frame(x = 1:5)
#' preds <- predict(fit, newdata = list(main = newdf))
#' }
#'
#' @export
predict.xreg2 <- function(object, newdata = NULL, return_vector = FALSE, ...) {
  if (is.data.frame(newdata)) {
    newdata <- list(newdata)
    names(newdata) <- names(object$formulas)[1]
  }
  if (!all(names(newdata) %in% names(object$formulas)))
    stop("Data type listed in newdata not in xreg2 object.")

  args <- as.list(object$parf)
  dtns <- names(newdata)
  names(dtns) <- dtns

  preds <- lapply(dtns, function(dtn) {
    formulas <- object$formulas[[dtn]]
    d_df     <- newdata[[dtn]]
    for (formulanum in seq_along(formulas)) {
      formula <- formulas[[formulanum]]
      new_var <- as.character(formula[[2]])
      if (new_var %in% c(colnames(d_df), object$valuevars[[dtn]])) new_var <- "Xb"
      d_df[, new_var] <- with(args, with(d_df, eval(parse(text = formula))))
    }
    d_df
  })

  if (length(newdata) == 1 && return_vector) return(preds[[1]]$Xb)
  return(preds)
}

#' Print method for xreg2 objects
#'
#' @title Print an xreg2 Model
#' @description Prints a concise summary of a fitted \code{xreg2} model,
#'   including coefficients, standard errors, and minimised negative
#'   log-likelihoods.
#'
#' @param x A fitted \code{xreg2} object.
#' @param ... Currently unused.
#'
#' @return \code{x} invisibly.
#'
#' @export
print.xreg2 <- function(x, ...) {
  cat("\nxreg2 fitted model\n")
  cat("Method:", x$method, "\n")
  cat("Optimisation time:", format(x$optim_time), "\n\n")

  if (NROW(x$full_coef)) {
    cat("Coefficients:\n")
    print(x$full_coef)
  }

  cat("\nNegative log-likelihoods:\n")
  print(x$minima)

  cat("\nObservation counts:\n")
  print(x$counts)

  invisible(x)
}

#' Summary method for xreg2 objects
#'
#' @title Summarise an xreg2 Model
#' @description Prints a detailed summary of a fitted \code{xreg2} model.
#'   Currently equivalent to \code{\link{print.xreg2}}.
#'
#' @param object A fitted \code{xreg2} object.
#' @param ... Currently unused.
#'
#' @return \code{object} invisibly.
#'
#' @export
summary.xreg2 <- function(object, ...) {
  print(object, ...)
}

#' AIC method for xreg2 objects
#'
#' @title AIC for an xreg2 Model
#' @importFrom stats AIC BIC
#' @description Computes the Akaike Information Criterion for a fitted
#'   \code{xreg2} model. Scale parameters (SIGMA, LN_SIGMA, OMEGA, LN_OMEGA)
#'   are excluded from the parameter count.
#'
#' @param object A fitted \code{xreg2} object.
#' @param ... Currently unused.
#' @param k Numeric; penalty per parameter. Default \code{2}.
#' @param just_total Logical; return only the total AIC across all components.
#'   Default \code{TRUE}.
#'
#' @return A numeric scalar (AIC value).
#'
#' @export
AIC.xreg2 <- function(object, ..., k = 2, just_total = TRUE) {
  coefs <- object$coef[!names(object$coef) %in%
                         c("SIGMA", "SIGMA_INTERCEPT", "LN_SIGMA", "OMEGA", "LN_OMEGA")]
  npar  <- length(coefs)
  mini  <- object$minima
  if (just_total) mini <- mini[["total"]]
  return(2 * mini + k * npar)
}

#' BIC method for xreg2 objects
#'
#' @title BIC for an xreg2 Model
#' @description Computes the Bayesian Information Criterion for a fitted
#'   \code{xreg2} model.
#'
#' @param object A fitted \code{xreg2} object.
#' @param ... Currently unused.
#' @param just_total Logical; return only the total BIC. Default \code{TRUE}.
#'
#' @return A numeric scalar (BIC value).
#'
#' @export
BIC.xreg2 <- function(object, ..., just_total = TRUE) {
  counts <- object$counts
  names(counts)[length(counts)] <- "total"
  if (just_total) counts <- counts[["total"]]
  AIC(object, ..., k = log(counts), just_total = just_total)
}
