package edu.kit.ipd.crowdcontrol.objectservice.rest.resources;

import com.google.protobuf.Message;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import edu.kit.ipd.crowdcontrol.objectservice.Main;
import edu.kit.ipd.crowdcontrol.objectservice.config.ConfigException;
import edu.kit.ipd.crowdcontrol.objectservice.proto.*;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import spark.Spark;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static org.junit.Assert.*;

@Ignore
public class ResourceIntegrationTest {
    protected static final String ORIGIN = "http://localhost:4567";

    @BeforeClass
    public static void setUp() throws IOException, ConfigException {
        Main.main(null);
    }

    @AfterClass
    public static void tearDown() {
        Spark.stop();
    }

    @Test
    public void calibrations() throws NoSuchMethodException, UnirestException, IllegalAccessException, IOException, InvocationTargetException {
        CalibrationList list = httpGet("/calibrations", CalibrationList.class);
        assertSame(0, list.getItemsCount());

        Calibration put = Calibration.newBuilder()
                .setName("Gender")
                .setQuestion("What's your gender?")
                .addAnswers(Calibration.Answer.newBuilder().setAnswer("male").build())
                .addAnswers(Calibration.Answer.newBuilder().setAnswer("female").build())
                .addAnswers(Calibration.Answer.newBuilder().setAnswer("other").build())
                .addAcceptedAnswers(Calibration.Answer.newBuilder().setAnswer("male").build())
                .build();

        Calibration calibration = httpPut("/calibrations", put, Calibration.class);

        // ID gets ignored
        assertTrue(calibration.getId() > 0);

        // Accepted answers must not be saved
        assertEquals(put.toBuilder().clearAcceptedAnswers().build(), calibration.toBuilder().clearId().build());

        // Returned calibration must be equal to the returned version when using GET
        Calibration received = httpGet("/calibrations/" + calibration.getId(), Calibration.class);
        assertEquals(calibration, received);

        list = httpGet("/calibrations", CalibrationList.class);
        assertSame(1, list.getItemsCount());

        assertNull(httpDelete("/calibrations/" + calibration.getId(), Calibration.class));

        list = httpGet("/calibrations", CalibrationList.class);
        assertSame(0, list.getItemsCount());

        ErrorResponse error = httpGet("/calibrations/42", ErrorResponse.class);
        assertEquals("notFound", error.getCode());
    }

    @Test
    public void templates() throws NoSuchMethodException, UnirestException, IllegalAccessException, IOException, InvocationTargetException {
        TemplateList list = httpGet("/templates", TemplateList.class);
        assertSame(0, list.getItemsCount());

        Template put = Template.newBuilder()
                .setName("name")
                .setContent("content")
                .setAnswerType(AnswerType.TEXT)
                .addConstraints(Constraint.newBuilder().setName("constraint").build())
                .addTags(Tag.newBuilder().setName("tag").build())
                .addRatingOptions(Template.RatingOption.newBuilder().setName("good").setValue(9).build())
                .addRatingOptions(Template.RatingOption.newBuilder().setName("bad").setValue(0).build())
                .build();

        Template template = httpPut("/templates", put, Template.class);

        // ID gets ignored
        assertTrue(template.getId() > 0);

        // All properties except ID must be equal
        assertEquals(put, template.toBuilder().clearId().build());

        // Returned template must be equal to the returned version when using GET
        Template received = httpGet("/templates/" + template.getId(), Template.class);
        assertEquals(template, received);

        list = httpGet("/templates", TemplateList.class);
        assertSame(1, list.getItemsCount());

        assertNull(httpDelete("/templates/" + template.getId(), Template.class));

        list = httpGet("/templates", TemplateList.class);
        assertSame(0, list.getItemsCount());

        ErrorResponse error = httpGet("/templates/42", ErrorResponse.class);
        assertEquals("notFound", error.getCode());
    }

    public static <T extends Message> T httpGet(String path, Class<T> type) throws UnirestException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        HttpResponse<InputStream> response = Unirest.get(ORIGIN + path)
                .header("accept", "application/protobuf")
                .asBinary();

        return fromResponse(response, type);
    }

    public static <T extends Message> T httpPut(String path, T input, Class<T> type) throws UnirestException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        HttpResponse<InputStream> response = Unirest.put(ORIGIN + path)
                .header("accept", "application/protobuf")
                .header("content-type", "application/protobuf")
                .body(input.toByteArray())
                .asBinary();

        return fromResponse(response, type);
    }

    public static <T extends Message> T httpPatch(String path, T input, Class<T> type) throws UnirestException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        HttpResponse<InputStream> response = Unirest.patch(ORIGIN + path)
                .header("accept", "application/protobuf")
                .header("content-type", "application/protobuf")
                .body(input.toByteArray())
                .asBinary();

        return fromResponse(response, type);
    }

    public static <T extends Message> T httpDelete(String path, Class<T> type) throws UnirestException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        HttpResponse<InputStream> response = Unirest.delete(ORIGIN + path)
                .header("accept", "application/protobuf")
                .asBinary();

        return fromResponse(response, type);
    }

    private static <T extends Message> T fromResponse(HttpResponse<InputStream> response, Class<T> type) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        if (response.getStatus() == 204) {
            return null;
        }

        Method method = type.getMethod("newBuilder");
        T.Builder builder = (T.Builder) method.invoke(null);

        builder.mergeFrom(response.getBody());

        return (T) builder.build();
    }
}

@@ -686,16 +686,75 @@
 .Method;
+%0Aimport java.util.List;%0Aimport java.util.stream.Collectors;
 %0A%0Aimport
@@ -2051,32 +2051,450 @@
 .getId() %3E 0);%0A%0A
+        // Clear all answer IDs%0A        Calibration.Builder calibrationWithoutAnswerIds = calibration.toBuilder().clearId();%0A        List%3CCalibration.Answer%3E answers = calibrationWithoutAnswerIds.getAnswersList().stream().map(answer -%3E answer.toBuilder().clearId().build()).collect(Collectors.toList());%0A%0A        calibrationWithoutAnswerIds.clearAnswers();%0A        calibrationWithoutAnswerIds.addAllAnswers(answers);%0A%0A
         // Accep
@@ -2603,38 +2603,32 @@
 ibration
-.toBuilder().clearId()
+WithoutAnswerIds
 .build()
@@ -5026,32 +5026,314 @@
 Code());%0A    %7D%0A%0A
+    @Test%0A    public void unsupportedMediaType() throws UnirestException %7B%0A        HttpResponse%3CString%3E response = Unirest.get(ORIGIN + %22/templates%22)%0A                .header(%22accept%22, %22text/plain%22)%0A                .asString();%0A%0A        assertSame(415, response.getStatus());%0A    %7D%0A%0A
     public stati

Clear answer IDs for comparison, add unsupported media type test