private void getAutoGeneratedRules(
      Metadata metadata, Condition condition, Condition parentCondition, List<Rule> rules) {
    Set<String> tags = condition.getConditionType().getMetadata().getTags();
    if (tags.contains("eventCondition") && !tags.contains("profileCondition")) {
      try {
        Map<String, Object> m = new HashMap<>(3);
        m.put("scope", metadata.getScope());
        m.put("condition", condition);
        m.put("numberOfDays", parentCondition.getParameter("numberOfDays"));
        String key = CustomObjectMapper.getObjectMapper().writeValueAsString(m);
        key = "eventTriggered" + getMD5(key);
        parentCondition.setParameter("generatedPropertyKey", key);
        Rule rule = rulesService.getRule(key);
        if (rule == null) {
          rule =
              new Rule(
                  new Metadata(
                      metadata.getScope(),
                      key,
                      "Auto generated rule for " + metadata.getName(),
                      ""));
          rule.setCondition(condition);
          rule.getMetadata().setHidden(true);
          final Action action = new Action();
          action.setActionType(definitionsService.getActionType("setEventOccurenceCountAction"));
          action.setParameter("pastEventCondition", parentCondition);

          rule.setActions(Arrays.asList(action));
          rule.setLinkedItems(Arrays.asList(metadata.getId()));
          rules.add(rule);

          updateExistingProfilesForPastEventCondition(condition, parentCondition);
        } else {
          rule.getLinkedItems().add(metadata.getId());
          rules.add(rule);
        }
      } catch (JsonProcessingException e) {
        logger.error(e.getMessage(), e);
      }
    } else {
      Collection<Object> values = new ArrayList<>(condition.getParameterValues().values());
      for (Object parameterValue : values) {
        if (parameterValue instanceof Condition) {
          getAutoGeneratedRules(metadata, (Condition) parameterValue, condition, rules);
        } else if (parameterValue instanceof Collection) {
          for (Object subCondition : (Collection<?>) parameterValue) {
            if (subCondition instanceof Condition) {
              getAutoGeneratedRules(metadata, (Condition) subCondition, condition, rules);
            }
          }
        }
      }
    }
  }
  private void updateExistingProfilesForScoring(Scoring scoring) {
    long t = System.currentTimeMillis();
    Condition scoringCondition = new Condition();
    scoringCondition.setConditionType(
        definitionsService.getConditionType("profilePropertyCondition"));
    scoringCondition.setParameter("propertyName", "scores." + scoring.getItemId());
    scoringCondition.setParameter("comparisonOperator", "exists");
    List<Profile> previousProfiles =
        persistenceService.query(scoringCondition, null, Profile.class);

    HashMap<String, Object> scriptParams = new HashMap<>();
    scriptParams.put("scoringId", scoring.getItemId());

    for (Profile profileToRemove : previousProfiles) {
      persistenceService.updateWithScript(
          profileToRemove.getItemId(),
          null,
          Profile.class,
          "if (ctx._source.systemProperties.scoreModifiers == null) { ctx._source.systemProperties.scoreModifiers=[:] } ; if (ctx._source.systemProperties.scoreModifiers.containsKey(scoringId)) { ctx._source.scores[scoringId] = ctx._source.systemProperties.scoreModifiers[scoringId] } else { ctx._source.scores.remove(scoringId) }",
          scriptParams);
    }
    if (scoring.getMetadata().isEnabled()) {
      String script =
          "if (ctx._source.scores == null) { ctx._source.scores=[:] } ; if (ctx._source.scores.containsKey(scoringId)) { ctx._source.scores[scoringId] += scoringValue } else { ctx._source.scores[scoringId] = scoringValue }";
      Map<String, Event> updatedProfiles = new HashMap<>();
      for (ScoringElement element : scoring.getElements()) {
        scriptParams.put("scoringValue", element.getValue());
        for (Profile p : persistenceService.query(element.getCondition(), null, Profile.class)) {
          persistenceService.updateWithScript(
              p.getItemId(), null, Profile.class, script, scriptParams);
          Event profileUpdated = new Event("profileUpdated", null, p, null, null, p, new Date());
          profileUpdated.setPersistent(false);
          updatedProfiles.put(p.getItemId(), profileUpdated);
        }
      }
      Iterator<Map.Entry<String, Event>> entries = updatedProfiles.entrySet().iterator();
      while (entries.hasNext()) {
        eventService.send(entries.next().getValue());
      }
    }
    logger.info("Profiles updated in {}", System.currentTimeMillis() - t);
  }
  private void updateExistingProfilesForPastEventCondition(
      Condition eventCondition, Condition parentCondition) {
    long t = System.currentTimeMillis();
    List<Condition> l = new ArrayList<Condition>();
    Condition andCondition = new Condition();
    andCondition.setConditionType(definitionsService.getConditionType("booleanCondition"));
    andCondition.setParameter("operator", "and");
    andCondition.setParameter("subConditions", l);

    l.add(eventCondition);

    Integer numberOfDays = (Integer) parentCondition.getParameter("numberOfDays");
    if (numberOfDays != null) {
      Condition numberOfDaysCondition = new Condition();
      numberOfDaysCondition.setConditionType(
          definitionsService.getConditionType("sessionPropertyCondition"));
      numberOfDaysCondition.setParameter("propertyName", "timeStamp");
      numberOfDaysCondition.setParameter("comparisonOperator", "greaterThan");
      numberOfDaysCondition.setParameter("propertyValue", "now-" + numberOfDays + "d");
      l.add(numberOfDaysCondition);
    }
    String propertyKey = (String) parentCondition.getParameter("generatedPropertyKey");
    Map<String, Long> res =
        persistenceService.aggregateQuery(
            andCondition, new TermsAggregate("profileId"), Event.ITEM_TYPE);
    for (Map.Entry<String, Long> entry : res.entrySet()) {
      if (!entry.getKey().startsWith("_")) {
        Map<String, Object> p = new HashMap<>();
        p.put(propertyKey, entry.getValue());
        Map<String, Object> p2 = new HashMap<>();
        p2.put("pastEvents", p);
        try {
          persistenceService.update(entry.getKey(), null, Profile.class, "systemProperties", p2);
        } catch (Exception e) {
          logger.error(e.getMessage(), e);
        }
      }
    }

    logger.info("Profiles past condition updated in {}", System.currentTimeMillis() - t);
  }
  public SegmentsAndScores getSegmentsAndScoresForProfile(Profile profile) {
    Set<String> segments = new HashSet<String>();
    Map<String, Integer> scores = new HashMap<String, Integer>();

    List<Segment> allSegments = this.allSegments;
    for (Segment segment : allSegments) {
      if (persistenceService.testMatch(segment.getCondition(), profile)) {
        segments.add(segment.getMetadata().getId());
      }
    }

    List<Scoring> allScoring = this.allScoring;
    Map<String, Integer> scoreModifiers =
        (Map<String, Integer>) profile.getSystemProperties().get("scoreModifiers");
    for (Scoring scoring : allScoring) {
      if (scoring.getMetadata().isEnabled()) {
        int score = 0;
        for (ScoringElement scoringElement : scoring.getElements()) {
          if (persistenceService.testMatch(scoringElement.getCondition(), profile)) {
            score += scoringElement.getValue();
          }
        }
        String scoringId = scoring.getMetadata().getId();
        if (scoreModifiers != null
            && scoreModifiers.containsKey(scoringId)
            && scoreModifiers.get(scoringId) != null) {
          score += scoreModifiers.get(scoringId);
        }
        if (score > 0) {
          scores.put(scoringId, score);
        }
      }
    }

    return new SegmentsAndScores(segments, scores);
  }