package com.kurtraschke.nyctrtproxy.services;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.handlers.AsyncHandler;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsync;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsyncClientBuilder;
import com.amazonaws.services.cloudwatch.model.Dimension;
import com.amazonaws.services.cloudwatch.model.MetricDatum;
import com.amazonaws.services.cloudwatch.model.PutMetricDataRequest;
import com.amazonaws.services.cloudwatch.model.PutMetricDataResult;
import com.amazonaws.services.cloudwatch.model.StandardUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.Date;

public class CloudwatchProxyDataListener implements ProxyDataListener {
  private static final Logger _log = LoggerFactory.getLogger(CloudwatchProxyDataListener.class);

  @Inject
  @Named("cloudwatch.namespace")
  private String _namespace;

  @Inject
  @Named("cloudwatch.accessKey")
  private String _accessKey;

  @Inject
  @Named("cloudwatch.secretKey")
  private String _secretKey;

  private AmazonCloudWatchAsync _client;

  private AsyncHandler<PutMetricDataRequest, PutMetricDataResult> _handler;

  @PostConstruct
  public void init() {
    //_client = new AmazonCloudWatchAsyncClient(new BasicAWSCredentials(_accessKey, _secretKey));
    BasicAWSCredentials cred = new BasicAWSCredentials(_accessKey, _secretKey);
    _client = AmazonCloudWatchAsyncClientBuilder.standard()
            .withCredentials(new AWSStaticCredentialsProvider(cred))
            .build();
    _handler = new AsyncHandler<PutMetricDataRequest, PutMetricDataResult>() {
      @Override
      public void onError(Exception e) {
        _log.error("Error sending to cloudwatch: " + e);
      }

      @Override
      public void onSuccess(PutMetricDataRequest request, PutMetricDataResult putMetricDataResult) {
        // do nothing
      }
    };
  }

  @Override
  public void reportMatchesForRoute(String routeId, int nMatchedTrips, int nAddedTrips, int nCancelledTrips) {
    Date timestamp = new Date();
    Dimension dim = new Dimension();
    dim.setName("route");
    dim.setValue(routeId);
    reportMatches(timestamp, dim, nMatchedTrips, nAddedTrips, nCancelledTrips);
    _log.info("time={}, route={}, nMatchedTrips={}, nAddedTrips={}, nCancelledTrips={}", timestamp, routeId, nMatchedTrips, nAddedTrips, nCancelledTrips);

  }
  @Override
  public void reportMatchesForFeed(String feedId, int nMatchedTrips, int nAddedTrips, int nCancelledTrips) {
    Date timestamp = new Date();
    Dimension dim = new Dimension();
    dim.setName("feed");
    dim.setValue(feedId);
    reportMatches(timestamp, dim, nMatchedTrips, nAddedTrips, nCancelledTrips);
    _log.info("time={}, feed={}, nMatchedTrips={}, nAddedTrips={}, nCancelledTrips={}", timestamp, feedId, nMatchedTrips, nAddedTrips, nCancelledTrips);
  }

  private void reportMatches(Date timestamp, Dimension dim, int nMatchedTrips, int nAddedTrips, int nCancelledTrips) {
    double nStatic = nMatchedTrips + nCancelledTrips;
    double nMatchedPct = (double) nMatchedTrips / nStatic;
    double nCancelledPct = (double) nCancelledTrips / nStatic;

    MetricDatum dMatched = metricCount(timestamp, "MatchedTrips", nMatchedTrips, dim);
    MetricDatum dAdded = metricCount(timestamp, "AddedTrips", nAddedTrips, dim);
    MetricDatum dCancelled = metricCount(timestamp, "CancelledTrips", nCancelledTrips, dim);
    MetricDatum dMatchedPct = metricPct(timestamp, "MatchedTripsPct", nMatchedPct, dim);
    MetricDatum dCancelledPct = metricPct(timestamp, "CancelledTripsPct", nCancelledPct, dim);

    PutMetricDataRequest request = new PutMetricDataRequest()
            .withMetricData(dMatched, dAdded, dCancelled, dMatchedPct, dCancelledPct)
            .withNamespace(_namespace);

    _client.putMetricDataAsync(request, _handler);
  }

  private static MetricDatum metricCount(Date timestamp, String name, int value, Dimension dim) {
    return new MetricDatum().withMetricName(name)
            .withTimestamp(timestamp)
            .withValue((double) value)
            .withUnit(StandardUnit.Count)
            .withDimensions(dim);
  }

  private static MetricDatum metricPct(Date timestamp, String name, double value, Dimension dim) {
    return new MetricDatum().withMetricName(name)
            .withTimestamp(timestamp)
            .withValue(value * 100.0)
            .withUnit(StandardUnit.Percent)
            .withDimensions(dim);
  }
}

@@ -644,16 +644,49 @@
 rdUnit;%0A
+import com.google.inject.Inject;%0A
 import o
@@ -698,24 +698,24 @@
 f4j.Logger;%0A
-
 import org.s
@@ -778,36 +778,8 @@
 ct;%0A
-import javax.inject.Inject;%0A
 impo
@@ -996,32 +996,49 @@
 ass);%0A%0A  @Inject
+(optional = true)
 %0A  @Named(%22cloud
@@ -1086,32 +1086,49 @@
 pace;%0A%0A  @Inject
+(optional = true)
 %0A  @Named(%22cloud
@@ -1184,16 +1184,33 @@
  @Inject
+(optional = true)
 %0A  @Name
@@ -1265,16 +1265,54 @@
 etKey;%0A%0A
+  private boolean _disabled = false;%0A%0A
   privat
@@ -1470,65 +1470,103 @@
     
-//_client
+if (_secretKey
  =
+=
  n
-ew AmazonCloudWatchAsyncClient(new BasicAWSC
+ull %7C%7C _accessKey == null %7C%7C _namespace == null) %7B%0A      _log.info(%22No AWS c
 rede
@@ -1575,34 +1575,86 @@
 ials
-(_accessKey, _secretKey));
+ supplied, disabling cloudwatch%22);%0A      _disabled = true;%0A      return;%0A    %7D
 %0A   
@@ -2723,17 +2723,16 @@
 Trips);%0A
-%0A
   %7D%0A  @O
@@ -3199,32 +3199,32 @@
 ledTrips);%0A  %7D%0A%0A
-
   private void r
@@ -3322,24 +3322,58 @@
 ledTrips) %7B%0A
+    if (_disabled)%0A      return;%0A%0A
     double n

disable cloudwatch when no credentials