package meerkat.pollingstation;

import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation.ErrorMsg;
import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.rest.Constants;
import meerkat.rest.ProtobufMessageBodyReader;
import meerkat.rest.ProtobufMessageBodyWriter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.util.concurrent.Semaphore;

import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
import static org.hamcrest.MatcherAssert.assertThat;

/**
 * Created by Arbel on 25/05/2016.
 */
public class ReceiverTest {

    private PollingStationScanner.PollingStationServer scanner;
    private static final String ADDRESS = "http://localhost";
    private static final String SUB_ADDRESS = "";
    private static final int PORT = 8080;

    private Semaphore semaphore;
    private Throwable thrown;
    private boolean dataIsAsExpected;

    private class ScanHandler implements FutureCallback<ScannedData> {

        private final ScannedData expectedData;

        public ScanHandler(ScannedData expectedData) {
            this.expectedData = expectedData;
        }

        @Override
        public void onSuccess(ScannedData result) {
            dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
            semaphore.release();
        }

        @Override
        public void onFailure(Throwable t) {
            dataIsAsExpected = false;
            thrown = t;
            semaphore.release();
        }
    }

    private class ErrorHandler implements FutureCallback<ScannedData> {

        private final String expectedErrorMessage;

        public ErrorHandler(String expectedErrorMessage) {
            this.expectedErrorMessage = expectedErrorMessage;
        }

        @Override
        public void onSuccess(ScannedData result) {
            dataIsAsExpected = false;
            semaphore.release();
        }

        @Override
        public void onFailure(Throwable t) {
            dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
            semaphore.release();
        }
    }

    @Before
    public void init() {

        System.err.println("Setting up Scanner WebApp!");

        scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);

        semaphore = new Semaphore(0);
        thrown = null;

        try {
            scanner.start();
        } catch (Exception e) {
            assertThat("Could not start server: " + e.getMessage(), false);
        }

    }

    @Test
    public void testSuccessfulScan() throws InterruptedException {

        Client client = ClientBuilder.newClient();
        client.register(ProtobufMessageBodyReader.class);
        client.register(ProtobufMessageBodyWriter.class);
        WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
                .path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH);

        byte[] data = {(byte) 1, (byte) 2};

        ScannedData scannedData = ScannedData.newBuilder()
                .setChannel(ByteString.copyFrom(data))
                .build();

        scanner.subscribe(new ScanHandler(scannedData));

        Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF));
        response.close();

        semaphore.acquire();
        assertThat("Scanner has thrown an error", thrown == null);
        assertThat("Scanned data received was incorrect", dataIsAsExpected);

    }

    @Test
    public void testErroneousScan() throws InterruptedException {

        Client client = ClientBuilder.newClient();
        client.register(ProtobufMessageBodyReader.class);
        client.register(ProtobufMessageBodyWriter.class);
        WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
                .path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_ERROR_PATH);

        ErrorMsg errorMsg = ErrorMsg.newBuilder()
                .setMsg("!Error Message!")
                .build();

        scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));

        Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(errorMsg, Constants.MEDIATYPE_PROTOBUF));
        response.close();

        semaphore.acquire();
        assertThat("Scanner error received was incorrect", dataIsAsExpected);

    }

    @After
    public void close() {

        System.err.println("ReceiverScanHandler shutting down...");

        try {
            scanner.stop();
        } catch (Exception e) {
            assertThat("Could not stop server: " + e.getMessage(), false);
        }

    }

}