package meerkat.pollingstation;

import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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;


public class Receiver_ClientTest {
    final Logger logger = LoggerFactory.getLogger(getClass());
    private PollingStationScanner.Consumer scanner;
    private static final String CONTEXT_PATH = "/scan";

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

    PollingStation.ConnectionServerData serverData = null;

    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();
        }
    }


    @Before
    public void init() {

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

        scanner = new PollingStationWebScanner(0, CONTEXT_PATH);

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

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

        logger.info("Started scanner web service. API endpoint: {}", serverData.getServerUrl());

    }

    @Test
    public void testSuccessfulScan() throws InterruptedException {

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

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

        scanner.subscribe(new ScanHandler(scannedData));

        PollingStationScanner.Producer scannerClient = new ScannerClientAPI()

                ScannerClientAPI(PSC_URL + POLLING_STATION_WEB_SCANNER_SCAN_PATH);
        scannerClient.newScan(scannedData, null);

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

    }

    @Test
    public void testErroneousScan() throws InterruptedException {

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

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

        ScannerClientAPI scannerClient = new ScannerClientAPI();


        PSC_URL + POLLING_STATION_WEB_SCANNER_ERROR_PATH);
        scannerClient.sendError(errorMsg);

        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);
        }

    }

}