/**
   * Returns a map where the key is a user and the value is a naturally-sorted list of results they
   * should receive.
   *
   * @param results all the validation run results, in a sorted set
   * @return map of users to results
   */
  private Map<User, SortedSet<ValidationResult>> getUserResults(
      SortedSet<ValidationResult> results) {
    Map<User, SortedSet<ValidationResult>> userResults = new HashMap<>();

    for (ValidationResult result : results) {
      for (ValidationRuleGroup ruleGroup : result.getValidationRule().getGroups()) {
        if (ruleGroup.hasUserGroupsToAlert()) {
          for (UserGroup userGroup : ruleGroup.getUserGroupsToAlert()) {
            for (User user : userGroup.getMembers()) {
              if (!ruleGroup.isAlertByOrgUnits()
                  || canUserAccessSource(user, result.getOrgUnit())) {
                SortedSet<ValidationResult> resultSet = userResults.get(user);

                if (resultSet == null) {
                  resultSet = new TreeSet<>();

                  userResults.put(user, resultSet);
                }

                resultSet.add(result);
              }
            }
          }
        }
      }
    }
    return userResults;
  }
  /**
   * Generate and send an alert message containing a list of validation results to a set of users.
   *
   * @param results results to put in this message
   * @param users users to receive these results
   * @param scheduledRunStart date/time when the scheduled run started
   */
  private void sendAlertmessage(
      SortedSet<ValidationResult> results, Set<User> users, Date scheduledRunStart) {
    StringBuilder builder = new StringBuilder();

    SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");

    Map<String, Integer> importanceCountMap = countResultsByImportanceType(results);

    String subject =
        "Alerts as of "
            + dateTimeFormatter.format(scheduledRunStart)
            + ": High "
            + (importanceCountMap.get("high") == null ? 0 : importanceCountMap.get("high"))
            + ", Medium "
            + (importanceCountMap.get("medium") == null ? 0 : importanceCountMap.get("medium"))
            + ", Low "
            + (importanceCountMap.get("low") == null ? 0 : importanceCountMap.get("low"));

    // TODO use velocity template for message

    for (ValidationResult result : results) {
      ValidationRule rule = result.getValidationRule();

      builder
          .append(result.getOrgUnit().getName())
          .append(" ")
          .append(result.getPeriod().getName())
          .append(
              result.getAttributeOptionCombo().isDefault()
                  ? ""
                  : " " + result.getAttributeOptionCombo().getName())
          .append(LN)
          .append(rule.getName())
          .append(" (")
          .append(rule.getImportance())
          .append(") ")
          .append(LN)
          .append(rule.getLeftSide().getDescription())
          .append(": ")
          .append(result.getLeftsideValue())
          .append(LN)
          .append(rule.getRightSide().getDescription())
          .append(": ")
          .append(result.getRightsideValue())
          .append(LN)
          .append(LN);
    }

    log.info("Alerting users: " + users.size() + ", subject: " + subject);

    messageService.sendMessage(subject, builder.toString(), null, users);
  }