package com.linkedin.thirdeye.dashboard.resources.v2;

import com.linkedin.thirdeye.client.DAORegistry;
import com.linkedin.thirdeye.dashboard.resources.v2.pojo.RootCauseEntity;
import com.linkedin.thirdeye.dashboard.resources.v2.rootcause.DefaultEntityFormatter;
import com.linkedin.thirdeye.dashboard.resources.v2.rootcause.DimensionEntityFormatter;
import com.linkedin.thirdeye.dashboard.resources.v2.rootcause.EventEntityFormatter;
import com.linkedin.thirdeye.dashboard.resources.v2.rootcause.MetricEntityFormatter;
import com.linkedin.thirdeye.dashboard.resources.v2.rootcause.ServiceEntityFormatter;
import com.linkedin.thirdeye.rootcause.Entity;
import com.linkedin.thirdeye.rootcause.RCAFramework;
import com.linkedin.thirdeye.rootcause.RCAFrameworkExecutionResult;
import com.linkedin.thirdeye.rootcause.impl.EntityUtils;
import com.linkedin.thirdeye.rootcause.impl.TimeRangeEntity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


@Path(value = "/rootcause")
@Produces(MediaType.APPLICATION_JSON)
public class RootCauseResource {
  private static final Logger LOG = LoggerFactory.getLogger(RootCauseResource.class);

  private static final DateTimeFormatter ISO8601 = ISODateTimeFormat.basicDateTimeNoMillis();

  private final List<RootCauseEntityFormatter> formatters;

  private static final long HOUR_IN_MS = 60 * 60 * 1000;
  private static final long DAY_IN_MS = 24 * HOUR_IN_MS;

  private final RCAFramework rca;

  public RootCauseResource(RCAFramework rca, List<RootCauseEntityFormatter> formatters) {
    this.rca = rca;
    this.formatters = formatters;
  }

  @GET
  @Path("/query")
  public List<RootCauseEntity> queryRootCause(
      @QueryParam("current") Long current,
      @QueryParam("currentDate") String currentDate,
      @QueryParam("baseline") Long baseline,
      @QueryParam("baselineDate") String baselineDate,
      @QueryParam("windowSize") Long windowSize,
      @QueryParam("windowSizeInHours") Long windowSizeInHours,
      @QueryParam("windowSizeInDays") Long windowSizeInDays,
      @QueryParam("urn") List<String> urns,
      @QueryParam("pipeline") @DefaultValue("OUTPUT") String pipeline) throws Exception {

    // input validation
    if((current == null && currentDate == null) ||
        (current != null && currentDate != null))
      throw new IllegalArgumentException("Must provide either currentDate or current");

    if((baseline == null && baselineDate == null) ||
        (baseline != null && baselineDate != null))
      throw new IllegalArgumentException("Must provide baselineDate or baseline");

    // TODO check exclusive use
    if(windowSize == null && windowSizeInHours == null && windowSizeInDays == null)
      throw new IllegalArgumentException("Must provide either windowSize, windowSizeInHours, or windowSizeInDays");

    if(currentDate != null)
      current = ISO8601.parseMillis(currentDate);

    if(baselineDate != null)
      baseline = ISO8601.parseMillis(baselineDate);

    if(windowSizeInHours != null)
      windowSize = windowSizeInHours * HOUR_IN_MS;
    if(windowSizeInDays != null)
      windowSize = windowSizeInDays * DAY_IN_MS;

    // format input
    Set<Entity> input = new HashSet<>();
    input.add(TimeRangeEntity.fromRange(1.0, TimeRangeEntity.TYPE_CURRENT, current - windowSize, current));
    input.add(TimeRangeEntity.fromRange(1.0, TimeRangeEntity.TYPE_BASELINE, baseline - windowSize, baseline));
    for(String urn : urns) {
      input.add(EntityUtils.parseURN(urn, 1.0));
    }

    // run root-cause analysis
    RCAFrameworkExecutionResult result = this.rca.run(input);

    // format output
    if(!result.getPipelineResults().containsKey(pipeline))
      throw new IllegalArgumentException(String.format("Could not find pipeline '%s'", pipeline));

    List<RootCauseEntity> output = new ArrayList<>();
    for(Entity e : result.getPipelineResults().get(pipeline).getEntities()) {
      output.add(applyFormatters(e));
    }
    Collections.sort(output, new Comparator<RootCauseEntity>() {
      @Override
      public int compare(RootCauseEntity o1, RootCauseEntity o2) {
        return -1 * Double.compare(o1.getScore(), o2.getScore());
      }
    });

    return output;
  }

  RootCauseEntity applyFormatters(Entity e) {
    for(RootCauseEntityFormatter formatter : this.formatters) {
      if(formatter.applies(e)) {
        try {
          return formatter.format(e);
        } catch (Exception ex) {
          LOG.warn("Error applying formatter '{}'. Skipping.", ex);
        }
      }
    }
    throw new IllegalArgumentException(String.format("No formatter for Entity '%s'", e.getUrn()));
  }

}

@@ -4940,16 +4940,48 @@
 pping.%22,
+ formatter.getClass().getName(),
  ex);%0A  

fix warn message (#1442)