/**
 * This test use two publicly available services;
 * 1. echo.websocket.org - which returns the data sent by the websocket client connected to it
 * 2. httpbin.org/get - return the data retated to the request itself ( parameter, etc )
 */

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.WebSocket;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.logging.Logger;

public class TestClient {

    private static final Logger LOG = Logger.getLogger("test");

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executorService = Executors.newCachedThreadPool();
        HttpClient httpClient = HttpClient.newBuilder().executor(executorService).build();

        WsApiClient wsApiClient = new WsApiClient(httpClient, "ws://echo.websocket.org");
        HttpApiClient httpApiClient = new HttpApiClient(httpClient, "http://httpbin.org/get");

        AtomicReference<String> result = new AtomicReference<>("failed");

        wsApiClient.listen(message -> httpApiClient.getData(message).map(s -> "succeeded").ifPresent(result::set));

        wsApiClient.sendData("TEST_DATA");

        LOG.info("Wait some time");
        Thread.sleep(3_000);

        executorService.shutdownNow();

        LOG.info("Result: test " + result.get());
    }

    static class WsApiClient {
        final HttpClient httpClient;
        final String server;
        WebSocket webSocket;

        WsApiClient(HttpClient httpClient, String server) {
            this.httpClient = httpClient;
            this.server = server;
        }

        public void listen(Consumer<String> consumer) {
            LOG.info("WS API client - Start listening for incoming messages");
            URI uri = URI.create(server);
            webSocket = httpClient.newWebSocketBuilder()
                    .buildAsync(uri, new WebSocket.Listener() {
                        @Override
                        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
                            LOG.info("WS API client - received data: " + data);
                            consumer.accept(data.toString());
                            return null;
                        }
                    }).join();
        }

        void sendData(String data) {
            LOG.info("WS API client - sending data via WebSocket: {}" + data);
            webSocket.sendText(data, true).join();
        }
    }

    static class HttpApiClient {
        final HttpClient httpClient;
        final String baseUrl;

        HttpApiClient(HttpClient httpClient, String baseUrl) {
            this.httpClient = httpClient;
            this.baseUrl = baseUrl;
        }

        private Optional<String> getData(String data) {
            try {
                URI uri = URI.create(baseUrl + "?param=" + data);
                HttpRequest request = HttpRequest.newBuilder().GET().uri(uri).build();
                LOG.info("Http API Client - send HTTP GET request with parameter {}" + data);
                HttpResponse<String> send = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
                Optional<String> responseData = Optional.ofNullable(send.body());
                responseData.ifPresent(s -> LOG.info("Http API Client - response for HTTP GET request received"));
                return responseData;
            } catch (Exception e) {
                LOG.warning("Http API Client - Error getting data: " + e.getMessage());
            }
            return Optional.empty();
        }
    }
} 