package com.linkedin.thirdeye.dashboard.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import com.linkedin.thirdeye.dashboard.api.CollectionSchema;
import com.linkedin.thirdeye.dashboard.api.QueryResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.MultivaluedMap;
import java.util.*;
import java.util.regex.Pattern;

public class ViewUtils {
  private static final Joiner OR_JOINER = Joiner.on(" OR ");
  private static final TypeReference<List<String>> LIST_TYPE_REFERENCE = new TypeReference<List<String>>(){};
  private static final Logger LOGGER = LoggerFactory.getLogger(ViewUtils.class);

  public static Map<String, String> fillDimensionValues(CollectionSchema schema, Map<String, String> dimensionValues) {
    Map<String, String> filled = new TreeMap<>();
    for (String name : schema.getDimensions()) {
      String value = dimensionValues.get(name);
      if (value == null) {
        value = "*";
      }
      filled.put(name, value);
    }
    return filled;
  }

  public static Map<String, String> flattenDisjunctions(MultivaluedMap<String, String> dimensionValues) {
    Map<String, String> flattened = new HashMap<>();
    for (Map.Entry<String, List<String>> entry : dimensionValues.entrySet()) {
      flattened.put(entry.getKey(), OR_JOINER.join(entry.getValue()));
    }
    return flattened;
  }

  public static Map<List<String>, Map<String, Number[]>> processDimensionGroups(
      QueryResult queryResult,
      ObjectMapper objectMapper,
      Map<String, Map<String, String>> dimensionGroupMap,
      Map<String, Map<Pattern, String>> dimensionRegexMap,
      String dimensionName) throws Exception {
    // Aggregate w.r.t. dimension groups
    Map<List<String>, Map<String, Number[]>> processedResult = new HashMap<>(queryResult.getData().size());
    for (Map.Entry<String, Map<String, Number[]>> entry : queryResult.getData().entrySet()) {
      List<String> combination = objectMapper.readValue(entry.getKey(), LIST_TYPE_REFERENCE);
      Integer dimensionIdx = queryResult.getDimensions().indexOf(dimensionName);
      String value = combination.get(dimensionIdx);

      // Map to group
      boolean appliedExplicitMapping = false;
      if (dimensionGroupMap != null) {
        Map<String, String> mapping = dimensionGroupMap.get(dimensionName);
        if (mapping != null) {
          String groupValue = mapping.get(value);
          if (groupValue != null) {
            combination.set(dimensionIdx, groupValue);
            appliedExplicitMapping = true;
          }
        }
      }

      // Use regex (lower priority)
      if (!appliedExplicitMapping && dimensionRegexMap != null) {
        Map<Pattern, String> patterns = dimensionRegexMap.get(dimensionName);
        if (patterns != null) {
          int matches = 0;
          for (Map.Entry<Pattern, String> pattern : patterns.entrySet()) {
            if (pattern.getKey().matcher(value).find()) {
              matches++;
              combination.set(dimensionIdx, pattern.getValue());
            }
          }

          if (matches > 1) {
            LOGGER.warn("Multiple regexes match {}! {}", value, patterns);
          }
        }
      }

      Map<String, Number[]> existing = processedResult.get(combination);
      if (existing == null) {
        processedResult.put(combination, entry.getValue());
      } else {
        for (Map.Entry<String, Number[]> point : entry.getValue().entrySet()) {
          Number[] existingValues = existing.get(point.getKey());
          if (existingValues == null) {
            existingValues = new Number[point.getValue().length];
            Arrays.fill(existingValues, 0);
            existing.put(point.getKey(), existingValues);
          }

          Number[] incrementValues = point.getValue();
          for (int i = 0; i < existingValues.length; i++) {
            existingValues[i] = existingValues[i].doubleValue() + incrementValues[i].doubleValue();
          }
        }
      }
    }

    return processedResult;
  }
}

@@ -3404,77 +3404,97 @@
     
-processedResult.put(combination, entry.getValue());%0A      %7D else %7B%0A  
+existing = new HashMap%3C%3E();%0A        processedResult.put(combination, existing);%0A      %7D%0A%0A
     
@@ -3567,34 +3567,32 @@
 et()) %7B%0A        
-  
 Number%5B%5D existin
@@ -3584,25 +3584,17 @@
 umber%5B%5D 
-existingV
+v
 alues = 
@@ -3635,23 +3635,13 @@
     
-  
 if (
-existingV
+v
 alue
@@ -3659,35 +3659,25 @@
 %7B%0A          
-  existingV
+v
 alues = new 
@@ -3719,18 +3719,16 @@
         
-  
 Arrays.f
@@ -3731,25 +3731,17 @@
 ys.fill(
-existingV
+v
 alues, 0
@@ -3743,18 +3743,16 @@
 es, 0);%0A
-  
         
@@ -3782,25 +3782,17 @@
 tKey(), 
-existingV
+v
 alues);%0A
@@ -3791,39 +3791,35 @@
 alues);%0A        
-  
 %7D%0A%0A
-  
         Number%5B%5D
@@ -3828,22 +3828,16 @@
 ncrement
-Values
  = point
@@ -3853,26 +3853,24 @@
 ();%0A        
- 
- 
 for (int i =
@@ -3877,25 +3877,17 @@
  0; i %3C 
-existingV
+v
 alues.le
@@ -3913,19 +3913,9 @@
     
-  existingV
+v
 alue
@@ -3925,17 +3925,9 @@
 %5D = 
-existingV
+v
 alue
@@ -3956,22 +3956,16 @@
 ncrement
-Values
 %5Bi%5D.doub
@@ -3975,28 +3975,16 @@
 alue();%0A
-          %7D%0A
         

Fix for dimension grouping