package com.example.support;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.time.Duration;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * A tiny, owned WebSocket test client (L3git) built on the JDK's {@link java.net.http.WebSocket} -
 * NO dependency. Connect to a ws:// or wss:// endpoint, send text frames, and assert that an expected
 * message arrives within a timeout. Inbound frames are buffered so a message that arrives before you
 * assert on it is not lost. Generated / shipped by L3git.
 */
public class WsClient {

    private WebSocket ws;
    private final BlockingQueue<String> inbox = new LinkedBlockingQueue<>();
    private final StringBuilder partial = new StringBuilder();

    public void connect(String url) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        ws = client.newWebSocketBuilder()
                .connectTimeout(Duration.ofSeconds(15))
                .buildAsync(URI.create(url), new WebSocket.Listener() {
                    @Override
                    public CompletionStage<?> onText(WebSocket socket, CharSequence data, boolean last) {
                        partial.append(data);
                        if (last) { inbox.offer(partial.toString()); partial.setLength(0); }
                        socket.request(1);
                        return null;
                    }
                })
                .get(20, TimeUnit.SECONDS);
    }

    public void send(String message) {
        require();
        ws.sendText(message, true).join();
    }

    /** Wait up to timeoutMs for a buffered/arriving text frame containing {@code contains} (blank = any). */
    public String expect(String contains, long timeoutMs) throws InterruptedException {
        require();
        long end = System.currentTimeMillis() + timeoutMs;
        while (true) {
            long remaining = end - System.currentTimeMillis();
            if (remaining <= 0) { break; }
            String msg = inbox.poll(remaining, TimeUnit.MILLISECONDS);
            if (msg == null) { break; }
            if (contains == null || contains.isEmpty() || msg.contains(contains)) { return msg; }
        }
        throw new AssertionError("WebSocket: no message"
                + (contains == null || contains.isEmpty() ? "" : " containing \"" + contains + "\"")
                + " within " + timeoutMs + "ms");
    }

    public void close() {
        if (ws != null) {
            try { ws.sendClose(WebSocket.NORMAL_CLOSURE, "bye").join(); } catch (Exception ignored) { }
        }
    }

    private void require() {
        if (ws == null) { throw new IllegalStateException("WebSocket not connected - run a 'connect' step first."); }
    }
}
