class ServiceCheckValidityCheck extends AbstractCheckDescriptor {

  @Override
  public void perform(PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request)
      throws AmbariException {

    ServiceConfigDAO serviceConfigDAO = serviceConfigDAOProvider.get();
    HostRoleCommandDAO hostRoleCommandDAO = hostRoleCommandDAOProvider.get();

    final String clusterName = request.getClusterName();
    final Cluster cluster = clustersProvider.get().getCluster(clusterName);
    long clusterId = cluster.getClusterId();

    // build a mapping of the last config changes by service
    Map<String, Long> lastServiceConfigUpdates = new HashMap<>();
    for (Service service : cluster.getServices().values()) {
      if (service.getMaintenanceState() != MaintenanceState.OFF || !hasAtLeastOneComponentVersionAdvertised(service)) {
        continue;
      }
      StackId stackId = service.getDesiredStackId();
      boolean isServiceWitNoConfigs = ambariMetaInfo.get().isServiceWithNoConfigs(stackId.getStackName(), stackId.getStackVersion(), service.getName());
      if (isServiceWitNoConfigs){
        LOG.info(String.format("%s in %s version %s does not have customizable configurations. Skip checking service configuration history.", service.getName(), stackId.getStackName(), stackId.getStackVersion()));
      } else {
        LOG.info(String.format("%s in %s version %s has customizable configurations. Check service configuration history.", service.getName(), stackId.getStackName(), stackId.getStackVersion()));
        ServiceConfigEntity lastServiceConfig = serviceConfigDAO.getLastServiceConfig(clusterId, service.getName());
        lastServiceConfigUpdates.put(service.getName(), lastServiceConfig.getCreateTimestamp());
      }
    }

    // get the latest service checks, grouped by role
    List<LastServiceCheckDTO> lastServiceChecks = hostRoleCommandDAO.getLatestServiceChecksByRole(clusterId);
    Map<String, Long> lastServiceChecksByRole = new HashMap<>();
    for( LastServiceCheckDTO lastServiceCheck : lastServiceChecks ) {
      lastServiceChecksByRole.put(lastServiceCheck.role, lastServiceCheck.endTime);
    }

    LinkedHashSet<ServiceCheckConfigDetail> failures = new LinkedHashSet<>();

    // for every service, see if there was a service check executed and then
    for( Entry<String, Long> entry : lastServiceConfigUpdates.entrySet() ) {
      String serviceName = entry.getKey();
      long configCreationTime = entry.getValue();
      String role = actionMetadataProvider.get().getServiceCheckAction(serviceName);

      if(!lastServiceChecksByRole.containsKey(role) ) {
        LOG.info("There was no service check found for service {} matching role {}", serviceName, role);
        failures.add(new ServiceCheckConfigDetail(serviceName, null, null));
        continue;
      }

      long lastServiceCheckTime = lastServiceChecksByRole.get(role);
      if (lastServiceCheckTime < configCreationTime) {
        failures.add(
            new ServiceCheckConfigDetail(serviceName, lastServiceCheckTime, configCreationTime));

        LOG.info(
            "The {} service (role {}) had its configurations updated on {}, but the last service check was {}",
            serviceName, role, DATE_FORMAT.format(new Date(configCreationTime)),
            DATE_FORMAT.format(new Date(lastServiceCheckTime)));
      }
    }

    if (!failures.isEmpty()) {
      prerequisiteCheck.getFailedDetail().addAll(failures);

      LinkedHashSet<String> failedServiceNames = failures.stream().map(
          failure -> failure.serviceName).collect(Collectors.toCollection(LinkedHashSet::new));

      prerequisiteCheck.setFailedOn(failedServiceNames);
      prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
      String failReason = getFailReason(prerequisiteCheck, request);
      prerequisiteCheck.setFailReason(String.format(failReason, StringUtils.join(failedServiceNames, ", ")));
    }
  }

  public  ServiceCheckValidityCheck();

}