class AutoSkipFailedSummaryAction extends AbstractUpgradeServerAction {

  @Override
  public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext)
      throws AmbariException, InterruptedException {

    HostRoleCommand hostRoleCommand = getHostRoleCommand();
    long requestId = hostRoleCommand.getRequestId();
    long stageId = hostRoleCommand.getStageId();

    String clusterName = hostRoleCommand.getExecutionCommandWrapper().getExecutionCommand().getClusterName();
    Cluster cluster = getClusters().getCluster(clusterName);

    // use the host role command to get to the parent upgrade group
    UpgradeItemEntity upgradeItem = m_upgradeDAO.findUpgradeItemByRequestAndStage(requestId,stageId);
    UpgradeGroupEntity upgradeGroup = upgradeItem.getGroupEntity();

    // find all of the stages in this group
    long upgradeGroupId = upgradeGroup.getId();
    UpgradeGroupEntity upgradeGroupEntity = m_upgradeDAO.findUpgradeGroup(upgradeGroupId);
    List<UpgradeItemEntity> groupUpgradeItems = upgradeGroupEntity.getItems();
    TreeSet<Long> stageIds = new TreeSet<>();
    for (UpgradeItemEntity groupUpgradeItem : groupUpgradeItems) {
      stageIds.add(groupUpgradeItem.getStageId());
    }

    // for every stage, find all tasks that have been SKIPPED_FAILED - we use a
    // bit of trickery here since within any given request, the stage ID are
    // always sequential. This allows us to make a simple query instead of some
    // overly complex IN or NESTED SELECT query
    long minStageId = stageIds.first();
    long maxStageId = stageIds.last();

    List<HostRoleCommandEntity> skippedTasks = m_hostRoleCommandDAO.findByStatusBetweenStages(
        hostRoleCommand.getRequestId(),
        HostRoleStatus.SKIPPED_FAILED, minStageId, maxStageId);

    if (skippedTasks.isEmpty()) {
      return createCommandReport(0, HostRoleStatus.COMPLETED, "{}",
          "There were no skipped failures", null);
    }

    StringBuilder buffer = new StringBuilder("The following steps failed but were automatically skipped:\n");
    Set<String> skippedCategories = new HashSet<>();
    Map<String, Object> skippedFailures = new HashMap<>();

    Set<String> skippedServiceChecks = new HashSet<>();
    Map<String, Object> hostComponents= new HashMap<>();

    // Internal representation for failed host components
    // To avoid duplicates
    // Format: <hostname, Set<Role>>
    Map<String, Set<Role>> publishedHostComponents= new HashMap<>();

    for (HostRoleCommandEntity skippedTask : skippedTasks) {
      try {
        String skippedCategory;
        if (skippedTask.getRoleCommand().equals(RoleCommand.SERVICE_CHECK)) {
          skippedCategory = SKIPPED_SERVICE_CHECK;

          String serviceCheckActionName = skippedTask.getRole().toString();
          String service = actionMetadata.getServiceNameByServiceCheckAction(serviceCheckActionName);
          skippedServiceChecks.add(service);

          skippedFailures.put(SKIPPED_SERVICE_CHECK, skippedServiceChecks);
          m_structuredFailures.put(FAILURES, skippedFailures);
        } else {
          skippedCategory = SKIPPED_HOST_COMPONENT;

          String hostName = skippedTask.getHostName();
          if (null != hostName) {
            List<Object> failures = (List<Object>) hostComponents.get(hostName);
            if (null == failures) {
              failures = new ArrayList<>();
              hostComponents.put(hostName, failures);

              publishedHostComponents.put(hostName, new HashSet<>());
            }

            Set<Role> publishedHostComponentsOnHost = publishedHostComponents.get(hostName);
            Role role = skippedTask.getRole();
            if (! publishedHostComponentsOnHost.contains(role)) {
              HashMap<String, String> details = new HashMap<>();

              String service = cluster.getServiceByComponentName(role.toString()).getName();

              details.put("service", service);
              details.put("component", role.toString());
              failures.add(details);
            }
          }

          skippedFailures.put(SKIPPED_HOST_COMPONENT, hostComponents);
          m_structuredFailures.put(FAILURES, skippedFailures);
        }

        skippedCategories.add(skippedCategory);

        ServiceComponentHostEventWrapper eventWrapper = new ServiceComponentHostEventWrapper(
          skippedTask.getEvent());

        ServiceComponentHostEvent event = eventWrapper.getEvent();

        buffer.append(event.getServiceComponentName());
        if (null != event.getHostName()) {
          buffer.append(" on ");
          buffer.append(event.getHostName());
        }

        buffer.append(": ");
        buffer.append(skippedTask.getCommandDetail());
        buffer.append("\n");
      } catch (Exception exception) {
        LOG.warn("Unable to extract failure information for {}", skippedTask);
        buffer.append(": ");
        buffer.append(skippedTask);
      }
    }

    m_structuredFailures.put(SKIPPED, skippedCategories);

    String structuredOutput = m_gson.toJson(m_structuredFailures);
    String standardOutput = MessageFormat.format(FAILURE_STD_OUT_TEMPLATE, skippedTasks.size());
    String standardError = buffer.toString();

    return createCommandReport(0, HostRoleStatus.HOLDING, structuredOutput, standardOutput,
        standardError);
  }

}