check_recipe <- function(recipe) {
  # recipe: yaml recipe already read it
  ## TODO: set up logger-less case
  info(recipe$Run$logger, paste("Checking recipe:", recipe$recipe_path))
  
  # ---------------------------------------------------------------------
  # ANALYSIS CHECKS
  # ---------------------------------------------------------------------
  
  TIME_SETTINGS_SEASONAL <- c("sdate", "ftime_min", "ftime_max", "hcst_start",
                              "hcst_end")
  TIME_SETTINGS_DECADAL <- c("ftime_min", "ftime_max", "hcst_start", "hcst_end")

  TIME_SETTINGS_SUBSEASONAL <- c("sdate", "ftime_min", "ftime_max", "hcst_start",
                                 "hcst_end", "week_day", "sweek_window",
                                 "sday_window")
  PARAMS <- c("Horizon", "Time", "Variables", "Region", "Regrid", "Workflow",
              "Datasets")
  HORIZONS <- c("subseasonal", "seasonal", "decadal")
  
  # Define error status variable
  error_status <- F
  
  # Check basic elements in recipe:Analysis:
  if (!("Analysis" %in% names(recipe))) {
    error(recipe$Run$logger,
          "The recipe must contain an element called 'Analysis'.")
    error_status <- TRUE
  }
  
  if (!all(PARAMS %in% names(recipe$Analysis))) {
    error(recipe$Run$logger, 
          paste0("The element 'Analysis' in the recipe must contain all of ",
                 "the following: ", paste(PARAMS, collapse = ", "), "."))
    error_status <- TRUE
  }
  
  if (!any(HORIZONS %in% recipe$Analysis$Horizon)) {
    error(recipe$Run$logger,
          paste0("The element 'Horizon' in the recipe must be one of the ",
                 "following: ", paste(HORIZONS, collapse = ", "), "."))
    error_status <- T
  }
  # Check time settings
  archive <- get_archive(recipe)
  if (recipe$Analysis$Horizon == "seasonal") {
    if (!all(TIME_SETTINGS_SEASONAL %in% names(recipe$Analysis$Time))) {
      error(recipe$Run$logger,
            paste0("The element 'Time' in the recipe must contain all of the ",
                   "following: ", paste(TIME_SETTINGS_SEASONAL,
                                        collapse = ", "), "."))
      error_status <- TRUE
    }
  } else if (recipe$Analysis$Horizon == "decadal") {
    if (!all(TIME_SETTINGS_DECADAL %in% names(recipe$Analysis$Time))) {
      error(recipe$Run$logger,
            paste0("The element 'Time' in the recipe must contain all of the ",
                   "following: ", paste(TIME_SETTINGS_DECADAL,
                                        collapse = ", "), "."))
      error_status <- TRUE
    }
  } else if (recipe$Analysis$Horizon == "subseasonal") {
    if (!all(TIME_SETTINGS_SUBSEASONAL %in% names(recipe$Analysis$Time))) {
      error(recipe$Run$logger,
            paste0("The element 'Time' in the recipe must contain all of the ",
                   "following: ", paste(TIME_SETTINGS_SUBSEASONAL,
                                        collapse = ", "), "."))
      error_status <- TRUE
    } 
  } else {
    archive <- NULL
  }

  # Check variable parameters
  if ("name" %in% names(recipe$Analysis$Variables)) {
    recipe_variables <- recipe$Analysis$Variables$name
  } else {
    recipe_variables <- sapply(recipe$Analysis$Variables, get, x = "name")
  }
  recipe_variables <- unlist(strsplit(recipe_variables, ", | |,"))
  # Sea-ice threshold check
  if (("tas-tos" %in% recipe_variables) &&
      (!is.null(recipe$Analysis$Variables$sic_threshold))) {
    if (!is.numeric(recipe$Analysis$Variables$sic_threshold) ||
        !dplyr::between(recipe$Analysis$Variables$sic_threshold, 0, 1)) {
      error(recipe$Run$logger,
            paste("The element Analysis:Variables:sic_threshold must be a",
                  "numeric value between 0 and 1."))
      error_status <- TRUE
    }
  }
  # Check system names
  if (!is.null(archive)) {
    if (!all(recipe$Analysis$Datasets$System$name %in% 
             c(names(archive$System), 'Multimodel'))) {
      error(recipe$Run$logger,
            "The specified System name was not found in the archive.")
      error_status <- TRUE
    }
    # Check reference names
    if (!all(recipe$Analysis$Datasets$Reference$name %in% 
             names(archive$Reference))) {
      error(recipe$Run$logger,
            "The specified Reference name was not found in the archive.")
      error_status <- TRUE
    }
  }
  # Check multimodel
  if (is.null(recipe$Analysis$Datasets$Multimodel) || 
      isFALSE(recipe$Analysis$Datasets$Multimodel)) {
    recipe$Analysis$Datasets$Multimodel <- list(execute = FALSE)
  }
  if (!isTRUE(recipe$Analysis$Datasets$Multimodel$execute)) {
    multimodel <- FALSE
  } else {
    multimodel <- TRUE
  }
  MULTIMODEL_METHODS <- c("pooled") ## to be added: mean, median...
  MULTIMODEL_CREATEFROM <- c("calibration", "anomalies", "indicators")
  if (multimodel) {
    if (!is.null(recipe$Analysis$Datasets$Multimodel)) {
      if (!tolower(recipe$Analysis$Datasets$Multimodel$execute) %in% 
          c('true', 'false', 'both')) {
        error(recipe$Run$logger,
              paste("The specified execution for the multimodel is not valid.",
                    "Please specify yes/true, no/false or 'both'."))
        error_status <- TRUE
      }
      if (recipe$Analysis$Datasets$Multimodel$approach %in%
          MULTIMODEL_METHODS) {
        error(recipe$Run$logger,
              paste("The specified approach for the multimodel is not valid.",
                    "Please specify pooled.")) #, mean or median."))
        error_status <- TRUE
      }
      if (recipe$Analysis$Datasets$Multimodel$createFrom %in% 
          MULTIMODEL_CREATEFROM) {
        error(recipe$Run$logger,
              paste("The specified 'createFrom' for the multimodel is not valid.",
                    "Please specify Calibration, Anomalies, Indicators."))
        error_status <- TRUE
      }
    }
  } else {
    recipe$Analysis$Datasets$Multimodel$execute <- FALSE
  }
  # Check ftime_min and ftime_max
  if ((!(recipe$Analysis$Time$ftime_min > 0)) ||
      (!is.integer(recipe$Analysis$Time$ftime_min))) {
    error(recipe$Run$logger,
          "The element 'ftime_min' must be an integer larger than 0.")
    error_status <- TRUE
  }
  if ((!(recipe$Analysis$Time$ftime_max > 0)) ||
      (!is.integer(recipe$Analysis$Time$ftime_max))) {
    error(recipe$Run$logger,
          "The element 'ftime_max' must be an integer larger than 0.")
    error_status <- TRUE
  }
  if (recipe$Analysis$Time$ftime_max < recipe$Analysis$Time$ftime_min) {
    error(recipe$Run$logger,
          "'ftime_max' cannot be smaller than 'ftime_min'.")
    error_status <- TRUE
  }
  # Check consistency of hindcast years
  if (!(as.numeric(recipe$Analysis$Time$hcst_start) %% 1 == 0) ||
      (!(recipe$Analysis$Time$hcst_start > 0))) {
    error(recipe$Run$logger,
          "The element 'hcst_start' must be a valid year.")
    error_status <- TRUE
  }
  if (!(as.numeric(recipe$Analysis$Time$hcst_end) %% 1 == 0) ||
      (!(recipe$Analysis$Time$hcst_end > 0))) {
    error(recipe$Run$logger,
          "The element 'hcst_end' must be a valid year.")
    error_status <- TRUE
  }
  if (recipe$Analysis$Time$hcst_end < recipe$Analysis$Time$hcst_start) {
    error(recipe$Run$logger,
          "'hcst_end' cannot be smaller than 'hcst_start'.")
    error_status <- TRUE
  }
  # Subseasonal-specific checks
  if (recipe$Analysis$Horizon == "subseasonal") {
    if (is.null(recipe$Analysis$Time$week_day)) {
      recipe$Analysis$Time$week_day <- "thursday"
      info(recipe$Run$logger,
           "Initialization day set to Thursdays")
    }
    sww <- recipe$Analysis$Time$sweek_window
    if (is.null(sww) || !is.numeric(sww) || (sww %% 2) != 1) {
      recipe$Analysis$Time$sweek_window <- 9
      info(recipe$Run$logger,
           "Number of initialisation 'weeks' for skill assessment set to 9.") 
    }
    sdw <- recipe$Analysis$Time$sday_window
    if (is.null(sdw) || !is.numeric(sdw) || (sdw %% 2) != 1) {
      # Calibration
      # If 'method' is FALSE/no/'none' or NULL, set to 'raw'
      ## TODO: Review this check
      if ((is.logical(recipe$Analysis$Workflow$Calibration$method) &&
           recipe$Analysis$Workflow$Calibration$method == FALSE) ||
           recipe$Analysis$Workflow$Calibration$method == 'none' ||
           is.null(recipe$Analysis$Workflow$Calibration$method)) {
        recipe$Analysis$Time$sday_window <- 1
        info(recipe$Run$logger,
            "Number of initialisation 'days' for calibration set to 1.")
      } else {
        recipe$Analysis$Time$sday_window <- 3
        info(recipe$Run$logger,
            "Number of initialisation 'days' for calibration set to 3.")
      }
    }
  }
  
  ## TODO: To be implemented in the future
  # if (length(recipe$Analysis$Time$sdate$fcst_day) > 1 &&
  #     tolower(recipe$Analysis$Horizon) != "subseasonal") {
  #   warn(recipe$Run$logger,
  #        paste("Only subseasonal verification allows multiple forecast days."),
  #              "Element fcst_day in recipe set as 1.")
  #   recipe$Analysis$Time$sdate$fcst_day <- '01'
  # }
  
  if (is.null(recipe$Analysis$Time$fcst_year)) {
    warn(recipe$Run$logger,
         paste("The element 'fcst_year' is not defined in the recipe.",
               "No forecast year will be used."))
  }
  
  # Regrid checks:
  if (length(recipe$Analysis$Regrid) != 2) {
    error(recipe$Run$logger,
          "The 'Regrid' element must specify the 'method' and 'type'.")
    error_status <- TRUE
  }

  if (recipe$Analysis$Regrid$type == 'to_system' && multimodel) {
      error(recipe$Run$logger,
            paste0("The 'Regrid$type' cannot be 'to_system' if ",
                   "'Multimodel$execute' is yes/true or both."))
      error_status <- TRUE
  }
  # TODO: Add Workflow checks?
  # ...
  # calculate number of workflows to create for each variable and
  if (length(recipe$Analysis$Horizon) > 1) {
    error(recipe$Run$logger,
          "Only one single Horizon can be specified in the recipe")
    error_status <- TRUE
  }
  
  ## TODO: Refine this
  # nvar <- length(recipe$Analysis$Variables)
  # if (nvar > 2) {
  #   error(recipe$Run$logger,
  #         "Only two type of Variables can be listed: ECVs and Indicators.")
  #   stop("EXECUTION FAILED")
  # }
  
  # remove NULL or None Indicators or ECVs from the recipe:
  if (!is.null(recipe$Analysis$Variables$Indicators) &&
      !is.list(recipe$Analysis$Variables$Indicators)) {
    recipe$Analysis$Variables <- recipe$Analysis$Variables[
      -which(names(recipe$Analysis$Variables) == 'Indicators')]
  }
  if (!is.null(recipe$Analysis$Variables$ECVs) &&
      !is.list(recipe$Analysis$Variables$ECVs)) {
    recipe$Analysis$Variables <- recipe$Analysis$Variables[
      -which(names(recipe$Analysis$Variables) == 'ECVs')]
  }
  
  # Region checks:
  LIMITS <- c('latmin', 'latmax', 'lonmin', 'lonmax')
  # Ordinary recipe
  if (is.null(names(recipe$Analysis$Region))) {
    for (region in recipe$Analysis$Region) {
      if (!all(LIMITS %in% names(region))) {
        error(recipe$Run$logger,
              paste0("There must be 4 elements in 'Region': ",
                     paste(LIMITS, collapse = ", "), "."))
        error_status <- T
      }
    }
    if (length(recipe$Analysis$Region) > 1) {
      for (region in recipe$Analysis$Region) {
        if (!("name" %in% names(region)) || (is.null(region$name))) {
          error(recipe$Run$logger,
                paste("If more than one region has been defined, every region",
                      "must have a unique name."))
        }
      }
    }
    # Atomic recipe
  } else if (!all(LIMITS %in% names(recipe$Analysis$Region))) {
    error(recipe$Run$logger,
          paste0("There must be 4 elements in 'Region': ",
                 paste(LIMITS, collapse = ", "), "."))
    error_status <- TRUE
  }
  ## TODO: Implement multiple regions
  # nregions <- length(recipe$Analysis$Region)
  # for (i in 1:length(recipe$Analysis$Region)) {
  #   if (!all(limits %in% names(recipe$Analysis$Region[[i]]))) {
  #     limits <- paste(limits, collapse = " ")
  #     error(recipe$Run$logger,
  #           paste0("Each region defined in element 'Region' ",
  #                  "should have 4 elements: ",
  #                  paste(limits, collapse = ", "), "."))
  #     error_status <- T
  #   }
  #   if (length(recipe$Analysis$Region) > 1) {
  #     if (!("name" %in% names(recipe$Analysis$Region[[i]]))) {
  #       error(recipe$Run$logger,
  #             paste("If multiple regions are requested, each region must",
  #                   "have a 'name'".)
  #   # are numeric? class list mode list 
  # }
  # ---------------------------------------------------------------------
  # WORKFLOW CHECKS
  # ---------------------------------------------------------------------
  # Time_Aggregation
  if ("Time_aggregation" %in% names(recipe$Analysis$Workflow)) {
    TIME_AGG_METHODS <- c("average", "accumulation")
    if (!("execute" %in% names(recipe$Analysis$Workflow$Time_aggregation))) {
      recipe$Analysis$Workflow$Time_aggregation$execute <- FALSE
    } else {
      if (recipe$Analysis$Workflow$Time_aggregation$execute) {
        if (!("method" %in% names(recipe$Analysis$Workflow$Time_aggregation))) {
          error(recipe$Run$logger,
                "'Workflow:Time_aggregation:method' is not defined in the recipe.")
          error_status <- TRUE
        } else {
          if (!(recipe$Analysis$Workflow$Time_aggregation$method %in%
              TIME_AGG_METHODS)) {
            error(recipe$Run$logger,
                  paste0("Time_aggregation method should be one of: ",
                         paste(TIME_AGG_METHODS, collapse = ", "), "."))
            error_status <- TRUE
          }
        }
        if ((any(!c('ini', 'end') %in% names(recipe$Analysis$Workflow$Time_aggregation)) &&
            !'user_def' %in% names(recipe$Analysis$Workflow$Time_aggregation))) {
          error(recipe$Run$logger,
                paste("Missing parameters in Time_aggregation section; please",
                      "add 'ini' and 'end' or 'user_def'."))
          error_status <- TRUE
        }
        if (all(c('ini', 'end') %in% names(recipe$Analysis$Workflow$Time_aggregation))) {
          if (!is.numeric(recipe$Analysis$Workflow$Time_aggregation$ini) || 
              !is.numeric(recipe$Analysis$Workflow$Time_aggregation$end)) { 
            error(recipe$Run$logger,
                  "ini and end in Time aggregation need to be numeric")
            error_status <- TRUE
          } else {
            if (length(recipe$Analysis$Workflow$Time_aggregation$ini) != 
                length(recipe$Analysis$Workflow$Time_aggregation$end)) {
              error(recipe$Run$logger,
                    paste("The numeber of ini and end parameters need to be",
                          "the same for Time aggregation"))
              error_status <- TRUE
            }
            max_ind <- max(c(recipe$Analysis$Workflow$Time_aggregation$ini, 
                             recipe$Analysis$Workflow$Time_aggregation$end))
            if (recipe$Analysis$Time$ftime_max < max_ind) {
              error(recipe$Run$logger,
                    paste("The requested max forecast time is smaller than indices",
                          "requested for time aggregation"))
              error_status <- TRUE
            }
          }
        }
        if ('user_def' %in% names(recipe$Analysis$Workflow$Time_aggregation)) {
          if (all(c('ini', 'end') %in% 
              names(recipe$Analysis$Workflow$Time_aggregation))) {
            recipe$Analysis$Workflow$Time_aggregation$user_def <- NULL 
            warn(recipe$Run$logger,
                 paste("Both 'ini' and 'end' and 'user_def' are defined under",
                       "'Time_aggregation'. Only 'ini' and 'end' indices will",
                       "be used."))
          }
          if (is.list(recipe$Analysis$Workflow$Time_aggregation$user_def)) {
            if (is.character(names(recipe$Analysis$Workflow$Time_aggregation$user_def))) {
              if (!all(sapply(recipe$Analysis$Workflow$Time_aggregation$user_def,
                   is.numeric))) {
                error(recipe$Run$logger,
                      paste0("'Time_aggregation:user_def' elements are ",
                             "expected to be numeric."))
                error_status <- TRUE
              }
            } else {
            warn(recipe$Run$logger,
                 paste("No names provided to the list of 'Time_aggregation:user_def'",
                       "indices. Review plots and file names to make sure they",
                       "are correct."))
            }
          } else if (!is.null(recipe$Analysis$Workflow$Time_aggregation$user_def)) {
            error(recipe$Run$logger,
                  "'Time_aggregation:user_def' should be a named list.")
            error_status <- TRUE
          }
        }
      }  
    }
  }
  # Calibration
  # If 'method' is FALSE/no/'none' or NULL, set to 'raw'
  ## TODO: Review this check
  if ((is.logical(recipe$Analysis$Workflow$Calibration$method) &&
       recipe$Analysis$Workflow$Calibration$method == FALSE) ||
      recipe$Analysis$Workflow$Calibration$method == 'none' ||
      is.null(recipe$Analysis$Workflow$Calibration$method)) {
    warn(recipe$Run$logger,
         "No Calibration method was specified, raw data verification.")
    recipe$Analysis$Workflow$Calibration$method <- 'raw'
  } else {
    if (is.null(recipe$Analysis$Workflow$Calibration$method)) {
      error(recipe$Run$logger,
            "The 'Calibration' element 'method' must be specified.")
      error_status <- TRUE
    }
    SAVING_OPTIONS_CALIB <- c("all", "none", "exp_only", "fcst_only")
    if ((is.null(recipe$Analysis$Workflow$Calibration$save)) ||
        (!(recipe$Analysis$Workflow$Calibration$save %in% SAVING_OPTIONS_CALIB))) {
      error(recipe$Run$logger,
            paste0("Please specify which Calibration module outputs you want ",
                   "to save with the 'save' parameter. The options are: ",
                   paste(SAVING_OPTIONS_CALIB, collapse = ", "), "."))
      error_status <- TRUE
    }
  }
  # Anomalies
  if ("Anomalies" %in% names(recipe$Analysis$Workflow)) {
    # Computation and cross-validation checks
    if (is.null(recipe$Analysis$Workflow$Anomalies$compute)) {
      error(recipe$Run$logger,
            "Parameter 'compute' must be defined under 'Anomalies'.")
      error_status <- TRUE
    } else if (!(is.logical(recipe$Analysis$Workflow$Anomalies$compute))) {
      error(recipe$Run$logger,
            paste("Parameter 'Anomalies:compute' must be a logical value",
                  "(True/False or yes/no)."))
      error_status <- TRUE
    } else if ((recipe$Analysis$Workflow$Anomalies$compute)) {
      # Cross-validation check
      if (!is.logical(recipe$Analysis$Workflow$Anomalies$cross_validation)) {
        error(recipe$Run$logger,
              paste("If anomaly computation is requested, parameter",
                    "'cross_validation' must be defined under 'Anomalies',
                    and it must be a logical value (True/False or yes/no)."))
        error_status <- TRUE
      }
      # Saving checks
      SAVING_OPTIONS_ANOM <- c("all", "none", "exp_only", "fcst_only")
      if ((is.null(recipe$Analysis$Workflow$Anomalies$save)) ||
          (!(recipe$Analysis$Workflow$Anomalies$save %in% SAVING_OPTIONS_ANOM))) {
        error(recipe$Run$logger,
              paste0("Please specify which Anomalies module outputs you want ",
                     "to save with the 'save' parameter. The options are: ",
                     paste(SAVING_OPTIONS_ANOM, collapse = ", "), "."))
        error_status <- TRUE
      }
    }
  }
  
  # Downscaling
  if ("Downscaling" %in% names(recipe$Analysis$Workflow)) {
    downscal_params <- recipe$Analysis$Workflow$Downscaling
    # Define accepted entries
    DOWNSCAL_TYPES <- c("none", "int", "intbc", "intlr", "analogs", "logreg")
    BC_METHODS <- c("quantile_mapping", "bias", "evmos", "mse_min", "crps_min",
                    "rpc-based", "qm")
    LR_METHODS <- c("basic", "large-scale", "9nn")
    LOGREG_METHODS <- c("ens_mean", "ens_mean_sd", "sorted_members")
    # Check downscaling type
    if ("type" %in% names(downscal_params)) {
      if (length(downscal_params$type) == 0) {
        downscal_params$type <- "none"
        warn(recipe$Run$logger,
             paste("Downscaling 'type' is empty in the recipe, setting it to",
                   "'none'."))
      }
      if (!(downscal_params$type %in% DOWNSCAL_TYPES)) {
        error(recipe$Run$logger,
              paste0("The type of Downscaling request in the recipe is not ",
                     "available. It must be one of the following: ",
                     paste(DOWNSCAL_TYPES, collapse = ", "), "."))
        error_status <- T
      }
      if ((downscal_params$type %in% c("int", "intbc", "intlr", "logreg")) &&
          (is.null(downscal_params$target_grid))) {
        error(recipe$Run$logger,
              paste("A target grid is required for the downscaling method",
                    "requested in the recipe."))
        error_status <- T
      }
      if (downscal_params$type == "int") {
        if (is.null(downscal_params$int_method)) {
          error(recipe$Run$logger,
                paste("Downscaling type 'int' was requested, but no",
                      "interpolation method is provided in the recipe."))
          error_status <- T
        }
      } else if (downscal_params$type %in% 
                 c("int", "intbc", "intlr", "logreg")) {
        if (is.null(downscal_params$int_method)) {
          error(recipe$Run$logger,
                paste("Downscaling type", downscal_params$type, 
                      "was requested in the recipe, but no",
                      "interpolation method is provided."))
          error_status <- T
        }
      } else if (downscal_params$type == "intbc") {
        if (is.null(downscal_params$bc_method)) {
          error(recipe$Run$logger,
                paste("Downscaling type 'intbc' was requested in the recipe, but",
                      "no bias correction method is provided."))
          error_status <- T
        } else if (!(downscal_params$bc_method %in% BC_METHODS)) {
          error(recipe$Run$logger,
                paste0("The accepted Bias Correction methods for the downscaling",
                       " module are: ", paste(BC_METHODS, collapse = ", "), "."))
          error_status <- T
        }
      } else if (downscal_params$type == "intlr") {
        if (length(downscal_params$lr_method) == 0) {
          error(recipe$Run$logger,
                paste("Downscaling type 'intlr' was requested in the recipe, but",
                      "no linear regression method was provided."))
          error_status <- T
        } else if (!(downscal_params$lr_method %in% LR_METHODS)) {
          error(recipe$Run$logger,
                paste0("The accepted linear regression methods for the",
                       " downscaling module are: ",
                       paste(LR_METHODS, collapse = ", "), "."))
          error_status <- T
        }
      } else if (downscal_params$type == "analogs") {
        if (is.null(downscal_params$nanalogs)) {
          warn(recipe$Run$logger,
               paste("Downscaling type is 'analogs, but the number of analogs",
                     "has not been provided in the recipe. The default is 3."))
        }
      } else if (downscal_params$type == "logreg") {
        if (is.null(downscal_params$int_method)) {
          error(recipe$Run$logger,
                paste("Downscaling type 'logreg' was requested in the recipe, but",
                      "no interpolation method was provided."))
          error_status <- T
        }
        if (is.null(downscal_params$log_reg_method)) {
          error(recipe$Run$logger,
                paste("Downscaling type 'logreg' was requested in the recipe,",
                      "but no logistic regression method is provided."))
          error_status <- T
        } else if (!(downscal_params$log_reg_method %in% LOGREG_METHODS)) {
          error(recipe$Run$logger,
                paste0("The accepted logistic regression methods for the ",
                       "downscaling module are: ",
                       paste(LOGREG_METHODS, collapse = ", "), "."))
          error_status <- T
        }
      }
    }
  }
  
  # Indices
  if ("Indices" %in% names(recipe$Analysis$Workflow)) {
    nino_indices <- paste0("nino", c("1+2", "3", "3.4", "4"))
    indices <- c("nao", nino_indices)
    if (!("Anomalies" %in% names(recipe$Analysis$Workflow))) {
      error(recipe$Run$logger,
            paste0("Indices uses Anomalies as input, but Anomalies are missing",
                   "in the recipe."))
      error_status <- TRUE
    } else if (!(recipe$Analysis$Workflow$Anomalies$compute)) {
      error(recipe$Run$logger,
            paste0("Indices uses Anomalies as input, but the parameter",
                   "'Anomalies:compute' is set as no/False."))
      error_status <- T
    }
    recipe_indices <- tolower(names(recipe$Analysis$Workflow$Indices))
    if (!all(recipe_indices %in% indices)) {
      error(recipe$Run$logger,
            paste0("Some of the indices under 'Indices' are not available.",
                   "The available Indices are: 'NAO', 'Nino1+2', 'Nino3', ",
                   "'Nino3.4' and 'Nino4'."))
      error_status <- TRUE
    }
    # Check that variables correspond with indices requested
    if (("nao" %in% recipe_indices) &&
        (!all(recipe_variables %in% c("psl", "z500")))) {
      error(recipe$Run$logger,
            paste0("It is not possible to compute the NAO with some of the ",
                   "variables requested. To compute the NAO, please make sure",
                   "your recipe requests only psl and/or z500."))
      error_status <- TRUE
    }
    if ((any(nino_indices %in% recipe_indices)) &&
        (!all(recipe_variables %in% c("tos", "sst")))) {
      error(recipe$Run$logger,
            paste0("It is not possible to compute El Nino indices with some ",
                   "of the variables requested. To compute El Nino, please ",
                   "make sure your recipe requests only tos."))
      error_status <- TRUE
    }
  }
  
  # Skill
  AVAILABLE_METRICS <- c("enscorr", "corr_individual_members", "rps", "rps_syear",
                         "rpss", "frps", "frpss", "crps", "crps_syear", 
                         "crpss", "bss10", "bss90",
                         "mean_bias", "mean_bias_ss", "enssprerr", "rps_clim",
                         "rps_clim_syear", "crps_clim", "crps_clim_syear",
                         "enscorr_specs", "frps_specs", "rpss_specs", 
                         "frpss_specs", "bss10_specs", "bss90_specs", "rms")
  if ("Skill" %in% names(recipe$Analysis$Workflow)) {
    if (is.null(recipe$Analysis$Workflow$Skill$metric)) {
      error(recipe$Run$logger,
            "Parameter 'metric' must be defined under 'Skill'.")
      error_status <- T
    } else {
      requested_metrics <- strsplit(recipe$Analysis$Workflow$Skill$metric, 
                                    ", | |,")[[1]]
      if (!all(requested_metrics %in% AVAILABLE_METRICS)) {
        error(recipe$Run$logger,
              paste0("Some of the metrics requested under 'Skill' are not ",
                     "available in SUNSET. Check the documentation to see the ",
                     "full list of accepted skill metrics."))
        error_status <- TRUE
      }
      if (recipe$Analysis$Output_format != 'scorecards') {
        if (any(grepl('_syear', requested_metrics)))  {
          recipe$Analysis$Output_format <- 'scorecards'
          warn(recipe$Run$logger,
               paste0("'_syear' metrics can only be saved as 'scorecards' ",
                      "output format. The output format is now 'scorecards'."))
        }
      }
    }
    # Saving checks
    SAVING_OPTIONS_SKILL <- c("all", "none")
    if ((is.null(recipe$Analysis$Workflow$Skill$save)) ||
        (!(recipe$Analysis$Workflow$Skill$save %in% SAVING_OPTIONS_SKILL))) {
      error(recipe$Run$logger,
            paste0("Please specify whether you want to save the Skill metrics ",
                   "with the 'save' parameter. The options are: ",
                   paste(SAVING_OPTIONS_SKILL, collapse = ", "), "."))
      error_status <- TRUE
    }
  }
  
  # Probabilities
  if ("Probabilities" %in% names(recipe$Analysis$Workflow)) {
    if (is.null(recipe$Analysis$Workflow$Probabilities$percentiles)) {
      error(recipe$Run$logger,
            "Parameter 'percentiles' must be defined under 'Probabilities'.")
      error_status <- TRUE
    } else if (!is.list(recipe$Analysis$Workflow$Probabilities$percentiles)) {
      error(recipe$Run$logger,
            paste("Parameter 'Probabilities:percentiles' expects a list.",
                  "See documentation in the wiki for examples."))
      error_status <- TRUE
    }
    # Saving checks
    SAVING_OPTIONS_PROBS <- c("all", "none", "bins_only", "percentiles_only")
    if ((is.null(recipe$Analysis$Workflow$Probabilities$save)) ||
        (!(recipe$Analysis$Workflow$Probabilities$save %in% SAVING_OPTIONS_PROBS))) {
      error(recipe$Run$logger,
            paste0("Please specify whether you want to save the percentiles ",
                   "and probability bins with the 'save' parameter. The ",
                   "options are: ",
                   paste(SAVING_OPTIONS_PROBS, collapse = ", "), "."))
      error_status <- TRUE
    }
  }
  
  # Visualization
  if ("Visualization" %in% names(recipe$Analysis$Workflow)) {
    PLOT_OPTIONS <- c("skill_metrics", "forecast_ensemble_mean",
                      "most_likely_terciles", "statistics")
    # Separate plots parameter and check if all elements are in PLOT_OPTIONS
    if (is.null(recipe$Analysis$Workflow$Visualization$plots)) {
      error(recipe$Run$logger,
            "The 'plots' element must be defined under 'Visualization'.")
      error_status <- TRUE
    } else {
      plots <- strsplit(recipe$Analysis$Workflow$Visualization$plots,
                        ", | |,")[[1]]
      if (!all(plots %in% PLOT_OPTIONS)) {
        error(recipe$Run$logger,
              paste0("The options available for the plots are: ",
                     paste(PLOT_OPTIONS, collapse = ", "), "."))
        error_status <- TRUE
      }
    }
    # Check multi_panel option
    ## TODO: Multi-panel
    if (is.null(recipe$Analysis$Workflow$Visualization$multi_panel)) {
      warn(recipe$Run$logger,
           paste0("Visualization:multi_panel not specified for the plots, the",
                  " default is 'no/False'."))
    } else if (!is.logical(recipe$Analysis$Workflow$Visualization$multi_panel)) {
      error(recipe$Run$logger,
            paste0("Parameter 'Visualization:multi_panel' must be a logical ",
                   "value: either 'yes/True' or 'no/False'"))
      error_status <- TRUE
    }
    # Check projection
    if (is.null(recipe$Analysis$Workflow$Visualization$projection)) {
      warn(recipe$Run$logger,
           paste0("Visualization:projection not specified for the plots, the ",
                  "default projection is cylindrical equidistant."))
    }
    ## TODO: Add significance?
    if ("most_likely_terciles" %in% plots) {
      if (is.null(recipe$Analysis$Workflow$Visualization$mask_terciles)) {
        warn(recipe$Run$logger,
             paste0("Visualization:mask_terciles not set for tercile plots,",
                    " the default setting is: 'no/False'."))
      } else if (!(recipe$Analysis$Workflow$Visualization$mask_terciles %in%
                   c(TRUE, FALSE, "both"))) {
        error(recipe$Run$logger,
              paste0("Parameter Visualization:mask_terciles must be one of: ",
                     "yes/True, no/False, 'both'"))
        error_status <- TRUE
      }
      if (is.null(recipe$Analysis$Workflow$Visualization$dots)) {
        warn(recipe$Run$logger,
             paste0("Visualization:dots not set for tercile plots, the default",
                    " setting is: 'no/False'."))
      } else if (!(recipe$Analysis$Workflow$Visualization$dots %in%
                   c(TRUE, FALSE, "both"))) {
        error(recipe$Run$logger,
              paste0("Parameter Visualization:plots must be one of: ",
                     "yes/True, no/False, 'both'"))
      }
    }
    # Check user-defined file format for plots
    PLOT_FILE_FORMATS <- c("pdf", "png", "jpg", "jpeg", "eps")
    if (!is.null(recipe$Analysis$Workflow$Visualization$file_format) &&
        !(recipe$Analysis$Workflow$Visualization$file_format) %in%
        PLOT_FILE_FORMATS) {
      error(recipe$Run$logger,
            paste0("Visualization:file_format must be one of the following: ",
                   paste(PLOT_FILE_FORMATS, collapse = ", "), "."))
      error_status <- TRUE
    }
  }
  # Scorecards
  if (!is.null(recipe$Analysis$Workflow$Statistics)) {
    statistics <- strsplit(recipe$Analysis$Workflow$Statistics$metric,
                           ", | |,")[[1]]
  } else {
    statistics <- NULL
  }
  if ("Scorecards" %in% names(recipe$Analysis$Workflow)) {
    if (recipe$Analysis$Workflow$Scorecards$execute == TRUE) {
      if (is.null(recipe$Analysis$Workflow$Scorecards$metric)) {
        error(recipe$Run$logger,
              "Parameter 'metric' must be defined under 'Scorecards'.")
        error_status <- T
      } else {
        sc_metrics <- strsplit(recipe$Analysis$Workflow$Scorecards$metric, 
                               ", | |,")[[1]]
        if (recipe$Analysis$Workflow$Scorecards$metric_aggregation == 'score') {
          if ('rpss' %in% sc_metrics) {
            if (!('rps_clim_syear' %in% requested_metrics)) {
              requested_metrics <- c(requested_metrics, 'rps_clim_syear')
            }
            if (!('rps_syear' %in% requested_metrics)) {
              requested_metrics <- c(requested_metrics, 'rps_syear')
            }
          }
          if ('crpss' %in% sc_metrics) {
            if (!('crps_clim_syear' %in% requested_metrics)) {
              requested_metrics <- c(requested_metrics, 'crps_clim_syear')
            }
            if (!('crps_syear' %in% requested_metrics)) {
              requested_metrics <- c(requested_metrics, 'crps_syear')
            }
          }
          if (('enscorr' %in% sc_metrics) &&
              !all(c('std', 'cov', 'n_eff') %in% statistics)) {
            error(recipe$Run$logger,
                  paste("For 'enscorr' to be plotted in the Scorecards with",
                        "the 'score' aggregation, the Statistics module must",
                        "be called and the statistics 'std', 'cov' and",
                        "'n_eff' are required."))
            error_status <- TRUE
          }
          recipe$Analysis$Workflow$Skill$metric <- paste0(requested_metrics,
                                                          collapse = " ")
        }
        if (recipe$Analysis$Output_format != 'scorecards') {
          warn(recipe$Run$logger,
               "Scorecards requested: setting output format as 'Scorecards'")
          recipe$Analysis$Output_format  <- 'scorecards' 
        }
        if (!all(sc_metrics %in% requested_metrics)) {
          error(recipe$Run$logger,
                paste0("All of the metrics requested under 'Scorecards' must ",
                       "be requested in the 'Skill' section."))
          error_status <- T
        }
      }
    }    
  }
  # ---------------------------------------------------------------------
  # RUN CHECKS
  # ---------------------------------------------------------------------
  
  ## TODO: These checks should probably go first
  RUN_FIELDS = c("Loglevel", "Terminal", "output_dir", "code_dir")
  LOG_LEVELS = c("INFO", "DEBUG", "WARN", "ERROR", "FATAL")
  
  if (!("Run" %in% names(recipe))) {
    stop("The recipe must contain an element named 'Run'.")
  }
  if (!all(RUN_FIELDS %in% names(recipe$Run))) {
    error(recipe$Run$logger, paste("Recipe element 'Run' must contain",
                                   "all of the following fields:",
                                   paste(RUN_FIELDS, collapse=", "), "."))
    error_status <- TRUE
  }
  if (!is.character(recipe$Run$output_dir)) {
    error(recipe$Run$logger,
          paste("The Run element 'output_dir' in", recipe$name, "file",
                "should be a character string indicating the path where",
                "the outputs should be saved."))
    error_status <- TRUE
  }
  if (!is.character(recipe$Run$code_dir)) {
    error(recipe$Run$logger,
          paste("The Run element 'code_dir' in", recipe$name, "file ",
                "should be a character string indicating the path",
                "where the code is."))
    error_status <- TRUE
  }
  if (!is.logical(recipe$Run$Terminal)) {
    error(recipe$Run$logger,
          paste("The Run element 'Terminal' in", recipe$name, "file ",
                "should be a boolean value indicating whether or not to",
                "print the logs in the terminal."))
    error_status <- TRUE
  }
  ## TODO: Review this case, since default value is allowed
  if (!is.character(recipe$Run$Loglevel) || 
      !any(recipe$Run$Loglevel %in% LOG_LEVELS)) {
    error(logger,
          paste("The Run element 'Loglevel' in", recipe$name, "file",
                "should be a character string specifying one of the levels available:",
                paste0(LOG_LEVELS, collapse='/')))
    error_status <- TRUE
  }
  
  # ---------------------------------------------------------------------
  # AUTOSUBMIT CHECKS
  # ---------------------------------------------------------------------
  
  AUTO_PARAMS <- c("script", "expid", "hpc_user", "wallclock",
                   "processors_per_job", "platform", "email_notifications",
                   "email_address", "notify_completed", "notify_failed")
  # Autosubmit false by default
  if (is.null(recipe$Run$autosubmit)) {
    recipe$Run$autosubmit <- FALSE
  }
  # Autosubmit configuration checks
  if (recipe$Run$autosubmit) {
    # Read autosubmit info for the specific filesystem (e.g. esarchive)
    auto_specs <- read_yaml("conf/autosubmit.yml")[[recipe$Run$filesystem]]
    # Check that the autosubmit configuration parameters are present
    if (!("auto_conf" %in% names(recipe$Run))) {
      error(recipe$Run$logger,
            "The 'auto_conf' is missing from the 'Run' section of the recipe.")
      error_status <- TRUE
    } else if (!all(AUTO_PARAMS %in% names(recipe$Run$auto_conf))) {
      error(recipe$Run$logger,
            paste0("The element 'Run:auto_conf' must contain all of the ",
                   "following: ", paste(AUTO_PARAMS, collapse = ", "), "."))
      error_status <- TRUE
    }
    # Check that the script is not NULL and exists
    if (is.null(recipe$Run$auto_conf$script)) {
      error(recipe$Run$logger,
            "A script must be provided to run the recipe with autosubmit.")
      error_status <- TRUE
    } else if (!file.exists(recipe$Run$auto_conf$script)) {
      error(recipe$Run$logger,
            "Could not find the file for the script in 'auto_conf'.")
      error_status <- TRUE
    }
    # Check that the experiment ID exists
    if (is.null(recipe$Run$auto_conf$expid)) {
      error(recipe$Run$logger,
            paste("The Autosubmit EXPID is missing. You can create one by",
                  "running the following commands on the autosubmit machine:"))
      error(recipe$Run$logger,
            paste("module load", auto_specs$module_version))
      error(recipe$Run$logger,
            paste("autosubmit expid -H", auto_specs$platform,
                  "-d <Description>"))
      error_status <- TRUE
    } else if (!dir.exists(paste0(auto_specs$experiment_dir,
                                  recipe$Run$auto_conf$expid))) {
      error(recipe$Run$logger,
            paste0("No folder in ", auto_specs$experiment_dir,
                   " for the EXPID", recipe$Run$auto_conf$expid,
                   ". Please make sure it is correct."))
      error_status <- TRUE
    }
    if ((recipe$Run$auto_conf$email_notifications) &&
        (is.null(recipe$Run$auto_conf$email_address))) {
      error(recipe$Run$logger,
            "Autosubmit notifications are enabled but email address is empty!")
      error_status <- TRUE
    }
    if (is.null(recipe$Run$auto_conf$hpc_user)) {
      error(recipe$Run$logger,
            "The 'Run:auto_conf:hpc_user' field can not be empty.")
      error_status <- TRUE
    } else if ((recipe$Run$filesystem == "esarchive") &&
               (!substr(recipe$Run$auto_conf$hpc_user, 1, 6) == "bsc032")) {
      error(recipe$Run$logger,
            "Please check your hpc_user ID. It should look like: 'bsc032xxx'")
      error_status <- TRUE
    }
    # Multimodel-specific parameters
    if (multimodel) {
      if (is.null(recipe$Run$auto_conf$wallclock_multimodel)) {
        warn(recipe$Run$logger,
             paste("No parameter 'wallclock_multimodel' specified, 'wallclock'",
                   "will be used."))
        recipe$Run$auto_conf$wallclock_multimodel <- recipe$Run$auto_conf$wallclock
      }
      if (is.null(recipe$Run$auto_conf$custom_directives_multimodel) &&
          !is.null(recipe$Run$auto_conf$custom_directives_multimodel)) {
        warn(recipe$Run$logger,
             paste("No 'custom_directives_multimodel' specified, the",
                   "single-model verification custom directives will be used."))
        recipe$Run$auto_conf$custom_directives_multimodel <- 
          recipe$Run$auto_conf$custom_directives
      }
      if (is.null(recipe$Run$auto_conf$processors_multimodel) &&
          !is.null(recipe$Run$auto_conf$processors_per_job)) {
        warn(recipe$Run$logger,
             paste("No 'processors_multimodel' specified, the",
                   "'processors_per_job' parameters will be used."))
        recipe$Run$auto_conf$custom_directives_multimodel <- 
          recipe$Run$auto_conf$custom_directives
      }
    }
  }
  
  # ---------------------------------------------------------------------
  # WORKFLOW CHECKS
  # ---------------------------------------------------------------------
  # Check workflow: need to define restrictions? 
  # e.g. only one calibration method
  ## TODO: Implement number of dependent verifications
  #nverifications <- check_number_of_dependent_verifications(recipe)
  # info(recipe$Run$logger, paste("Start Dates:",
  #                               paste(fcst.sdate, collapse = " ")))
  
  # Return error if any check has failed
  if (error_status) {
    error(recipe$Run$logger, "RECIPE CHECK FAILED.")
    stop("The recipe contains some errors. Find the full list in the",
         " main.log file.")
  } else {
    info(recipe$Run$logger, "##### RECIPE CHECK SUCCESSFULL #####")
  }
  return(recipe)
}
