package com.hubspot.singularity.resources;

import java.io.IOException;
import java.util.Enumeration;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;

import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Charsets;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
import com.ning.http.client.Request;
import com.ning.http.client.Response;

public class AbstractLeaderAwareResource {
  private static final Logger LOG = LoggerFactory.getLogger(AbstractLeaderAwareResource.class);

  protected final AsyncHttpClient httpClient;
  protected final LeaderLatch leaderLatch;
  protected final ObjectMapper objectMapper;

  public AbstractLeaderAwareResource(AsyncHttpClient httpClient, LeaderLatch leaderLatch, ObjectMapper objectMapper) {
    this.httpClient = httpClient;
    this.leaderLatch = leaderLatch;
    this.objectMapper = objectMapper;
  }

  protected <T, Q> T maybeProxyToLeader(HttpServletRequest request, Class<T> clazz, Q body, Supplier<T> runnable) {
    if (leaderLatch.hasLeadership()) {
      return runnable.get();
    }

    String leaderUri;
    try {
      leaderUri = leaderLatch.getLeader().getId();
    } catch (Exception e) {
      throw new RuntimeException("Could not get leader uri to proxy request");
    }

    if (leaderUri.equals(leaderLatch.getId())) {
      LOG.warn("Got own leader id when not the leader! There is likely no leader, will not proxy");
      return runnable.get();
    }

    String url = "http://" + leaderUri + request.getContextPath() + request.getPathInfo();
    LOG.debug("Not the leader, proxying request to {}", url);

    BoundRequestBuilder requestBuilder;
    switch (request.getMethod().toUpperCase()) {
      case "POST":
        requestBuilder = httpClient.preparePost(url);
        break;
      case "PUT":
        requestBuilder = httpClient.preparePut(url);
        break;
      case "DELETE":
        requestBuilder = httpClient.prepareDelete(url);
        break;
      default:
        throw new WebApplicationException(String.format("Not meant to proxy request of method %s", request.getMethod()), 400);
    }

    try {
      if (body != null) {
        requestBuilder.setBody(objectMapper.writeValueAsBytes(body));
        LOG.trace("Added body {} to reqeust", body);
      }
    } catch (JsonProcessingException jpe) {
      LOG.error("Could not write body from object {}", body);
      throw new WebApplicationException(jpe, 500);
    }

    copyHeadersAndParams(requestBuilder, request);
    Request httpRequest = requestBuilder.build();

    Response response;
    try {
      LOG.trace("Sending request to leader: {}", httpRequest);
      response = httpClient.executeRequest(httpRequest).get();
    } catch (IOException|ExecutionException|InterruptedException e) {
      LOG.error("Could not proxy request {} to leader", e);
      throw new WebApplicationException(e, 500);
    }

    try {
      if (response.getStatusCode() > 399) {
        throw new WebApplicationException(response.getResponseBody(Charsets.UTF_8.toString()), response.getStatusCode());
      } else {
        return objectMapper.readValue(response.getResponseBodyAsStream(), clazz);
      }
    } catch (IOException ioe) {
      String message = String.format("Request to leader succeeded with status {}, but could not interpret response", response.getStatusCode());
      LOG.error(message, ioe);
      throw new WebApplicationException(message, ioe, 500);
    }
  }

  private void copyHeadersAndParams(BoundRequestBuilder requestBuilder, HttpServletRequest request) {
    Enumeration<String> headerNames = request.getHeaderNames();
    if (headerNames != null) {
      while (headerNames.hasMoreElements()) {
        String headerName = headerNames.nextElement();
        requestBuilder.addHeader(headerName, request.getHeader(headerName));
        LOG.trace("Copied header {}:{}", headerName, request.getHeader(headerName));
      }
    }
    Enumeration<String> parameterNames = request.getParameterNames();
    if (parameterNames != null) {
      while (parameterNames.hasMoreElements()) {
        String parameterName = parameterNames.nextElement();
        requestBuilder.addQueryParameter(parameterName, request.getParameter(parameterName));
        LOG.trace("Copied query param {}={}", parameterName, request.getParameter(parameterName));
      }
    }
  }

  protected boolean useWebCache(Boolean useWebCache) {
    return useWebCache != null && useWebCache;
  }
}

@@ -3637,18 +3637,18 @@
  status 
-%7B%7D
+%25s
 , but co

fix string format