/**
 * @file WSClient.h
 * @brief RFC 6455 WebSocket client (POSIX sockets, background receive thread).
 *
 * The receive thread continuously reads frames and places them in a queue.
 * The main (ImGui) thread drains the queue each frame and also sends frames.
 *
 * Reuses WSFrame.h / SHA1.h / Base64.h from Source/Applications/StreamHub/.
 */

#pragma once

#include <string>
#include <vector>
#include <queue>
#include <mutex>
#include <thread>
#include <functional>
#include <atomic>
#include <cstdint>

namespace StreamHubClient {

/**
 * @brief A received WebSocket frame.
 */
struct WSMessage {
    bool   isBinary;
    std::vector<uint8_t> data;   ///< Payload bytes (text frames are also stored here)
};

/**
 * @brief Minimal RFC 6455 WebSocket client.
 *
 * Usage:
 *   WSClient ws;
 *   ws.connect("127.0.0.1", 8090);
 *   // main loop:
 *   ws.poll(handler);        // drain received messages
 *   ws.sendText(json);       // send a JSON command
 *   ws.disconnect();
 */
class WSClient {
public:
    WSClient();
    ~WSClient();

    /**
     * @brief (Re)connect to host:port in the background.
     * Non-blocking — sets a reconnect target; the receive thread handles the actual
     * connect/reconnect loop.
     */
    void connect(const std::string& host, uint16_t port);

    /** @brief Gracefully close the WebSocket connection and stop the receive thread. */
    void disconnect();

    /**
     * @brief Non-blocking reconnect to a (possibly new) host:port.
     * Closes the current socket so the background thread detects the drop and
     * reconnects immediately. Does NOT join the thread — safe to call from the UI.
     */
    void reconnect(const std::string& host, uint16_t port);

    /** @return true if the WebSocket handshake completed and the connection is open. */
    bool isConnected() const;

    /**
     * @brief Drain all pending received messages and pass each to `handler`.
     * Must be called from the main thread.
     */
    void poll(const std::function<void(const WSMessage&)>& handler);

    /** @brief Send a JSON text frame. Thread-safe. */
    void sendText(const std::string& json);

    /** @brief Send a binary frame. Thread-safe. */
    void sendBinary(const uint8_t* data, size_t len);

private:
    void recvLoop();
    bool doConnect();
    bool doHandshake();
    bool recvExact(uint8_t* buf, size_t len);
    bool sendRaw(const uint8_t* buf, size_t len);

    std::string   host_;
    uint16_t      port_       = 0;
    int           sock_       = -1;
    std::atomic<bool> connected_   {false};
    std::atomic<bool> stopThread_  {false};
    std::atomic<bool> reconnect_   {false};

    std::thread           recvThread_;
    mutable std::mutex    sendMutex_;
    mutable std::mutex    queueMutex_;
    std::queue<WSMessage> recvQueue_;
};

} /* namespace StreamHubClient */
