package spark;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static spark.Spark.after;
import static spark.Spark.before;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.List;
import java.util.Map;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import spark.examples.books.Books;
import spark.utils.IOUtils;

public class BooksIntegrationTest {

    private static int PORT = 4567;

    private static String AUTHOR = "FOO";
    private static String TITLE = "BAR";
    private static String NEW_TITLE = "SPARK";

    private String bookId;

    @After
    public void tearDown() {
        Books.books.clear();
        Spark.stop();
    }

    @Before
    public void setup() {
        before((request, response) -> {
            response.header("FOZ", "BAZ");
        });

        Books.main(null);

        after((request, response) -> {
            response.header("FOO", "BAR");
        });

        try {
            Thread.sleep(500);
        } catch (Exception e) {
        }
    }

    @Test
    public void canCreateBook() {
        UrlResponse response = createBookViaPOST();

        assertNotNull(response);
        assertNotNull(response.body);
        assertTrue(Integer.valueOf(response.body) > 0);
        assertEquals(201, response.status);
    }

    @Test
    public void canListBooks() {
        bookId = createBookViaPOST().body.trim();

        UrlResponse response = doMethod("GET", "/books", null);

        assertNotNull(response);
        String body = response.body.trim();
        assertNotNull(body);
        assertTrue(Integer.valueOf(body) > 0);
        assertEquals(200, response.status);
        assertTrue(response.body.contains(bookId));
    }

    @Test
    public void canGetBook() {
        bookId = createBookViaPOST().body.trim();

        UrlResponse response = doMethod("GET", "/books/" + bookId, null);

        String result = response.body;
        assertNotNull(response);
        assertNotNull(response.body);
        assertEquals(200, response.status);
        assertTrue(result.contains(AUTHOR));
        assertTrue(result.contains(TITLE));
        assertTrue(beforeFilterIsSet(response));
        assertTrue(afterFilterIsSet(response));
    }

    @Test
    public void canUpdateBook() {
        bookId = createBookViaPOST().body.trim();

        UrlResponse response = updateBook();

        String result = response.body;
        assertNotNull(response);
        assertNotNull(response.body);
        assertEquals(200, response.status);
        assertTrue(result.contains(bookId));
        assertTrue(result.contains("updated"));
    }

    @Test
    public void canGetUpdatedBook() {
        bookId = createBookViaPOST().body.trim();
        updateBook();

        UrlResponse response = doMethod("GET", "/books/" + bookId, null);

        String result = response.body;
        assertNotNull(response);
        assertNotNull(response.body);
        assertEquals(200, response.status);
        assertTrue(result.contains(AUTHOR));
        assertTrue(result.contains(NEW_TITLE));
    }

    @Test
    public void canDeleteBook() {
        bookId = createBookViaPOST().body.trim();

        UrlResponse response = doMethod("DELETE", "/books/" + bookId, null);

        String result = response.body;
        assertNotNull(response);
        assertNotNull(response.body);
        assertEquals(200, response.status);
        assertTrue(result.contains(bookId));
        assertTrue(result.contains("deleted"));
    }

    @Test(expected = FileNotFoundException.class)
    public void wontFindBook() throws IOException {
        getResponse("GET", "/books/" + bookId, null);
    }

    private static UrlResponse doMethod(String requestMethod, String path, String body) {
        UrlResponse response = new UrlResponse();

        try {
            getResponse(requestMethod, path, response);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return response;
    }

    private static void getResponse(String requestMethod, String path, UrlResponse response)
            throws MalformedURLException, IOException, ProtocolException {
        URL url = new URL("http://localhost:" + PORT + path);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod(requestMethod);
        connection.connect();
        String res = IOUtils.toString(connection.getInputStream());
        response.body = res;
        response.status = connection.getResponseCode();
        response.headers = connection.getHeaderFields();
    }

    private static class UrlResponse {
        public Map<String, List<String>> headers;
        private String body;
        private int status;
    }

    private UrlResponse createBookViaPOST() {
        return doMethod("POST", "/books?author=" + AUTHOR + "&title=" + TITLE, null);
    }

    private UrlResponse updateBook() {
        return doMethod("PUT", "/books/" + bookId + "?title=" + NEW_TITLE, null);
    }

    private boolean afterFilterIsSet(UrlResponse response) {
        return response.headers.get("FOO").get(0).equals("BAR");
    }

    private boolean beforeFilterIsSet(UrlResponse response) {
        return response.headers.get("FOZ").get(0).equals("BAZ");
    }
}

@@ -473,24 +473,53 @@
 unit.After;%0A
+import org.junit.AfterClass;%0A
 import org.j
@@ -529,16 +529,21 @@
 t.Before
+Class
 ;%0Aimport
@@ -867,16 +867,21 @@
   @After
+Class
 %0A    pub
@@ -880,24 +880,31 @@
 %0A    public 
+static 
 void tearDow
@@ -921,47 +921,96 @@
     
-Books.books.clear();%0A        Spark.stop
+Spark.stop();%0A    %7D%0A%0A    @After%0A    public void clearBooks() %7B%0A        Books.books.clear
 ();%0A
@@ -1027,16 +1027,21 @@
  @Before
+Class
 %0A    pub
@@ -1040,24 +1040,31 @@
 %0A    public 
+static 
 void setup()

use BeforeClass, AfterClass to improve test executing speed