package com.clxcommunications.xms;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
import org.apache.http.protocol.HttpContext;
import org.junit.Test;

public class ApiConnectionTest {

	private static abstract class DummyClient
	        implements HttpAsyncClient, Closeable {

		@Override
		public <T> Future<T> execute(HttpAsyncRequestProducer requestProducer,
		        HttpAsyncResponseConsumer<T> responseConsumer,
		        HttpContext context, FutureCallback<T> callback) {
			throw new AssertionError("unexpected");
		}

		@Override
		public <T> Future<T> execute(HttpAsyncRequestProducer requestProducer,
		        HttpAsyncResponseConsumer<T> responseConsumer,
		        FutureCallback<T> callback) {
			throw new AssertionError("unexpected");
		}

		@Override
		public Future<HttpResponse> execute(HttpHost target,
		        HttpRequest request, HttpContext context,
		        FutureCallback<HttpResponse> callback) {
			throw new AssertionError("unexpected");
		}

		@Override
		public Future<HttpResponse> execute(HttpHost target,
		        HttpRequest request, FutureCallback<HttpResponse> callback) {
			throw new AssertionError("unexpected");
		}

		@Override
		public Future<HttpResponse> execute(HttpUriRequest request,
		        HttpContext context, FutureCallback<HttpResponse> callback) {
			throw new AssertionError("unexpected");
		}

		@Override
		public Future<HttpResponse> execute(HttpUriRequest request,
		        FutureCallback<HttpResponse> callback) {
			throw new AssertionError("unexpected");
		}

	}

	@Test
	public void canBuildWithAllCustoms() throws Exception {
		ApiConnection.builder()
		        .token("token")
		        .servicePlanId("spid")
		        .endpoint("https://localhost:3000/basepath")
		        .callbackWrapper(CallbackWrapper.identity)
		        .prettyPrintJson(false)
		        .httpClient(HttpAsyncClients.createMinimal())
		        .build();
	}

	@Test(expected = IllegalStateException.class)
	public void rejectsEndpointWithQueryComponent() throws Exception {
		ApiConnection.builder()
		        .token("token")
		        .servicePlanId("spid")
		        .endpoint("https://localhost:3000/basepath?query=yes")
		        .build();
	}

	@Test(expected = IllegalStateException.class)
	public void rejectsBasePathWithFragmentComponent() throws Exception {
		ApiConnection.builder()
		        .token("token")
		        .servicePlanId("spid")
		        .endpoint("https://localhost:3000/basepath#fragment")
		        .build();
	}

	@Test
	public void leavesExternalHttpClientAlone() throws Exception {
		final AtomicBoolean clientClosed = new AtomicBoolean();

		ApiConnection conn = ApiConnection.builder()
		        .token("token")
		        .servicePlanId("spid")
		        .httpClient(new DummyClient() {

			        @Override
			        public void close() throws IOException {
				        clientClosed.set(true);
			        }

		        })
		        .build();

		conn.start();
		conn.close();

		assertThat(clientClosed.get(), is(false));
	}

	@Test
	public void leavesExternalApiDefaultHttpClientAlone() throws Exception {
		ApiDefaultHttpAsyncClient client = new ApiDefaultHttpAsyncClient();

		ApiConnection conn = ApiConnection.builder()
		        .token("token")
		        .servicePlanId("spid")
		        .httpClient(client)
		        .build();

		client.start();
		conn.start();
		conn.close();

		assertThat(client.isRunning(), is(true));
	}

}

@@ -73,16 +73,70 @@
 ers.is;%0A
+import static org.hamcrest.CoreMatchers.sameInstance;%0A
 import s
@@ -225,16 +225,37 @@
 eption;%0A
+import java.net.URI;%0A
 import j
@@ -543,16 +543,81 @@
 llback;%0A
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;%0A
 import o
@@ -910,16 +910,69 @@
 .Test;%0A%0A
+import com.clxcommunications.testsupport.TestUtils;%0A%0A
 public c
@@ -2405,32 +2405,272 @@
 s Exception %7B%0A%09%09
+String token = TestUtils.freshToken();%0A%09%09String spid = TestUtils.freshServicePlanId();%0A%09%09URI url = URI.create(%22https://localhost:3000/basepath%22);%0A%09%09CloseableHttpAsyncClient client = HttpAsyncClients.createMinimal();%0A%0A%09%09ApiConnection conn = 
 ApiConnection.bu
@@ -2686,39 +2686,37 @@
 %09        .token(
-%22
 token
-%22
 )%0A%09%09        .ser
@@ -2718,38 +2718,36 @@
  .servicePlanId(
-%22
 spid
-%22
 )%0A%09%09        .end
@@ -2756,41 +2756,11 @@
 int(
-%22https://localhost:3000/basepath%22
+url
 )%0A%09%09
@@ -2870,59 +2870,387 @@
 ent(
-HttpAsyncClients.createMinimal())%0A%09%09        .build(
+client)%0A%09%09        .build();%0A%0A%09%09assertThat(conn.token(), is(token));%0A%09%09assertThat(conn.servicePlanId(), is(spid));%0A%09%09assertThat(conn.endpoint(), is(url));%0A%09%09assertThat(conn.callbackWrapper(),%0A%09%09        is(sameInstance(CallbackWrapper.identity)));%0A%09%09assertThat(conn.prettyPrintJson(), is(false));%0A%09%09assertThat(conn.httpClient(),%0A%09%09        is(sameInstance((HttpAsyncClient) client))
 );%0A%09

Expand test case