#' @title Anomaly detector based on ML regression
#' @description
#' Trains a regression model to forecast the next value from a sliding window
#' and flags large prediction errors as anomalies. Uses DALToolbox regressors.
#'
#' A set of preconfigured regression methods are described at
#' <https://cefet-rj-dal.github.io/daltoolbox/> (e.g., `ts_elm`, `ts_conv1d`,
#' `ts_lstm`, `ts_mlp`, `ts_rf`, `ts_svm`).
#'
#' @param model A DALToolbox regression model.
#' @param sw_size Integer. Sliding window size.
#' @return `hanr_ml` object.
#'
#' @examples
#' library(daltoolbox)
#' library(tspredit)
#'
#' # Load anomaly example data
#' data(examples_anomalies)
#'
#' # Use a simple example
#' dataset <- examples_anomalies$simple
#' head(dataset)
#'
#' # Configure a time series regression model
#' model <- hanr_ml(tspredit::ts_elm(tspredit::ts_norm_gminmax(),
#'                    input_size=4, nhid=3, actfun="purelin"))
#'
#' # Fit the model
#' model <- daltoolbox::fit(model, dataset$serie)
#'
#' # Run detection
#' detection <- detect(model, dataset$serie)
#'
#' # Show detected anomalies
#' print(detection[(detection$event),])
#'
#' @references
#' - Hyndman RJ, Athanasopoulos G (2021). Forecasting: Principles and Practice. OTexts.
#' - Goodfellow I, Bengio Y, Courville A (2016). Deep Learning. MIT Press.
#'
#'@export
hanr_ml <- function(model, sw_size = 15) {
  obj <- harbinger()
  obj$model <- model
  obj$sw_size <- sw_size

  class(obj) <- append("hanr_ml", class(obj))
  return(obj)
}

#'@importFrom tspredit ts_data
#'@importFrom tspredit ts_projection
#'@exportS3Method fit hanr_ml
fit.hanr_ml <- function(obj, serie, ...) {
  # Build sliding windows and input/output projection
  ts <- tspredit::ts_data(serie, obj$sw_size)
  io <- tspredit::ts_projection(ts)

  # Fit the underlying regressor on supervised (X, y)
  obj$model <- fit(obj$model, x=io$input, y=io$output)

  return(obj)
}

#'@importFrom tspredit ts_data
#'@importFrom tspredit ts_projection
#'@importFrom stats na.omit
#'@importFrom stats predict
#'@exportS3Method detect hanr_ml
detect.hanr_ml <- function(obj, serie, ...) {
  # Normalize indexing and omit NAs
  obj <- obj$har_store_refs(obj, serie)

  # Build sliding windows and inputs for prediction
  ts <- tspredit::ts_data(obj$serie, obj$sw_size)
  io <- tspredit::ts_projection(ts)

  # One-step-ahead prediction and residuals
  adjust <- stats::predict(obj$model, io$input)
  values <- io$output

  res <- io$output-adjust

  # Distance and outlier detection on residuals
  res <- obj$har_distance(res)
  anomalies <- obj$har_outliers(res)
  anomalies <- obj$har_outliers_check(anomalies, res, values)
  threshold <- attr(anomalies, "threshold")

  # Align back to original positions (pad warm-up window)
  res <- c(rep(0, obj$sw_size - 1), res)
  anomalies <- c(rep(FALSE, obj$sw_size - 1), anomalies)
  attr(anomalies, "threshold") <- threshold

  # Restore detections to original indexing
  detection <- obj$har_restore_refs(obj, anomalies = anomalies, res = res)

  return(detection)
}
