package com.librato.metrics;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.concurrent.*;

/**
 * Implementation of HttpPoster that just uses built-in stdlib classes to
 * reduce dependencies.
 */
public class DefaultHttpPoster implements HttpPoster {
    private static final Logger log = LoggerFactory.getLogger(DefaultHttpPoster.class);
    private final URL url;
    private final String authHeader;
    private final ExecutorService executor;

    public DefaultHttpPoster(String url, String email, String token) {
        this.authHeader = Authorization.buildAuthHeader(email, token);
        try {
            this.url = new URL(url);
        } catch (MalformedURLException e) {
            throw new RuntimeException("Could not parse URL", e);
        }
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(1);
        ThreadFactory threadFactory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread();
                thread.setName("librato-http-poster");
                thread.setDaemon(true);
                return thread;
            }
        };
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
        //this.executor = new ThreadPoolExecutor(1, 1, 120, TimeUnit.SECONDS, queue, threadFactory, handler);
        this.executor = Executors.newSingleThreadExecutor();
    }

    class CouldNotPostMeasurementsException extends RuntimeException {
        public CouldNotPostMeasurementsException(Throwable cause) {
            super("Could not post measures to " + url, cause);
        }
    }

    @Override
    public Future<Response> post(final String userAgent, final String payload) throws IOException {
        return executor.submit(new Callable<Response>() {
            @Override
            public Response call() throws Exception {
                try {
                    return doPost(userAgent, payload);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new CouldNotPostMeasurementsException(e);
                }
            }
        });
    }

    Response doPost(String userAgent, final String payload) throws IOException {
        HttpURLConnection connection = open(url);
        connection.setDoOutput(true);
        connection.setConnectTimeout(5000);
        connection.setReadTimeout(5000);
        connection.setRequestMethod("POST");
        connection.setInstanceFollowRedirects(false);
        connection.setRequestProperty("Authorization", authHeader);
        connection.setRequestProperty("Content-Type", "application/json");
        connection.setRequestProperty("User-Agent", userAgent);
        connection.connect();
        OutputStream outputStream = connection.getOutputStream();
        try {
            outputStream.write(payload.getBytes(Charset.forName("UTF-8")));
        } finally {
            close(outputStream);
        }
        final int responseCode = connection.getResponseCode();
        return new Response() {
            @Override
            public int getStatusCode() {
                return responseCode;
            }

            @Override
            public String getBody() throws IOException {
                return payload;
            }
        };
    }

    void close(OutputStream outputStream) {
        try {
            outputStream.close();
        } catch (IOException ignore) {
        }
    }

    HttpURLConnection open(URL url) throws IOException {
        try {
            return (HttpURLConnection) url.openConnection();
        } catch (ClassCastException ignore) {
            throw new RuntimeException("URL " + url + " must use either http or https");
        }
    }

    @Override
    public void close() throws IOException {
        executor.shutdown();
        try {
            long timeout = 1L;
            TimeUnit unit = TimeUnit.MINUTES;
            if (!executor.awaitTermination(timeout, unit)) {
                log.warn("Could not shutdown after {} {}", timeout, unit);
            }
        } catch (InterruptedException ignored) {
            Thread.currentThread().interrupt();
            log.warn("Interrupted during shutdown");
        }
    }
}

@@ -100,48 +100,9 @@
 .io.
-IOException;%0Aimport java.io.OutputStream
+*
 ;%0Aim
@@ -508,16 +508,64 @@
 class);%0A
+    public static final String UTF_8 = %22UTF-8%22;%0A
     priv
@@ -982,611 +982,8 @@
   %7D%0A
-        BlockingQueue%3CRunnable%3E queue = new LinkedBlockingQueue%3CRunnable%3E(1);%0A        ThreadFactory threadFactory = new ThreadFactory() %7B%0A            @Override%0A            public Thread newThread(Runnable r) %7B%0A                Thread thread = new Thread();%0A                thread.setName(%22librato-http-poster%22);%0A                thread.setDaemon(true);%0A                return thread;%0A            %7D%0A        %7D;%0A        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();%0A        //this.executor = new ThreadPoolExecutor(1, 1, 120, TimeUnit.SECONDS, queue, threadFactory, handler);%0A
     
@@ -2524,23 +2524,21 @@
 forName(
-%22
 UTF
--8%22
+_8
 )));%0A   
@@ -2589,32 +2589,158 @@
 eam);%0A        %7D%0A
+        InputStream inputStream = connection.getInputStream();%0A        final String responseBody = readResponse(inputStream);%0A
         final in
@@ -3035,23 +3035,28 @@
  return 
-payload
+responseBody
 ;%0A      
@@ -3089,86 +3089,587 @@
     
-void close(OutputStream outputStream) %7B%0A        try %7B%0A            outputStream
+private String readResponse(InputStream in) throws IOException %7B%0A        try %7B%0A            ByteArrayOutputStream bos = new ByteArrayOutputStream();%0A            byte%5B%5D buffer = new byte%5B4096%5D;%0A            int bytesRead;%0A            while ((bytesRead = in.read(buffer)) %3E 0) %7B%0A                bos.write(buffer, 0, bytesRead);%0A            %7D%0A            byte%5B%5D bytes = bos.toByteArray();%0A            return new String(bytes, Charset.forName(UTF_8));%0A        %7D finally %7B%0A            close(in);%0A        %7D%0A    %7D%0A%0A    void close(Closeable closeable) %7B%0A        try %7B%0A            closeable
 .clo

Read response body