#' Transform either mean or median model output into a point forecast object:
#'
#' @param model_out_tbl Model output tibble with predictions
#' @param oracle_output Predictions that would have been generated by an oracle
#' model that knew the observed target data values in advance
#' @param output_type Forecast output type: "mean" or "median"
#'
#' @return forecast_point
#'
#' @details This function transforms a model output tibble in the Hubverse
#' format (with either "mean" or "median" output type) to a scoringutils "point"
#' forecast object
#'
#' @examplesIf requireNamespace("hubExamples", quietly = TRUE)
#' # Median point forecast
#' median_forecast <- hubExamples::forecast_outputs |>
#'   dplyr::filter(.data[["output_type"]] == "median") |>
#'   transform_point_model_out(
#'     oracle_output = hubExamples::forecast_oracle_output,
#'     output_type = "median"
#'   )
#' median_forecast
#'
#' # Mean point forecast
#' mean_forecast <- hubExamples::forecast_outputs |>
#'   dplyr::filter(.data[["output_type"]] == "mean") |>
#'   transform_point_model_out(
#'     oracle_output = hubExamples::forecast_oracle_output,
#'     output_type = "mean"
#'   )
#' mean_forecast
#'
#' @importFrom rlang .data
#' @export
transform_point_model_out <- function(
  model_out_tbl,
  oracle_output,
  output_type
) {
  if (
    (!inherits(output_type, "character")) ||
      (!output_type %in% c("mean", "median"))
  ) {
    cli::cli_abort(
      "invalid 'output_type': {.val {output_type}} Must be 'mean' or 'median'"
    )
  }

  model_out_tbl <- validate_model_oracle_out(model_out_tbl, oracle_output)

  task_id_cols <- get_task_id_cols(model_out_tbl)
  type <- output_type

  model_out_tbl <- model_out_tbl |>
    dplyr::filter(output_type == type) |>
    dplyr::rename(model = "model_id")

  # Filter oracle_output to `type` rows only when it still carries the
  # `output_type` column (i.e. mixed output types).
  if ("output_type" %in% colnames(oracle_output)) {
    oracle_output <- dplyr::filter(oracle_output, output_type == type)
  }
  # Always drop output_type/output_type_id if present: output_type_id can
  # remain after a user-side filter that removed output_type, and neither is
  # used for the point join.
  oracle_output <- dplyr::select(
    oracle_output,
    -dplyr::any_of(c("output_type", "output_type_id"))
  )

  data <- dplyr::left_join(
    model_out_tbl,
    oracle_output,
    by = task_id_cols[task_id_cols %in% colnames(oracle_output)],
    relationship = "many-to-one"
  )

  forecast_point <- scoringutils::as_forecast_point(
    data,
    forecast_unit = c("model", task_id_cols),
    observed = "oracle_value",
    predicted = "value"
  )

  forecast_point
}
