#pragma once

#include <string>
#include <map>
#include <memory>
#include <mutex>
#include <functional>
#include <vector>
#include <mosis/sandbox/http_validator.h>

struct lua_State;

namespace mosis {

struct WebSocketLimits {
    int max_connections_per_app = 5;
    size_t max_message_size = 1 * 1024 * 1024;     // 1 MB
    int idle_timeout_ms = 5 * 60 * 1000;           // 5 minutes
    int connect_timeout_ms = 30000;                 // 30 seconds
};

enum class WebSocketState {
    Connecting,
    Open,
    Closing,
    Closed
};

class WebSocket {
public:
    using MessageCallback = std::function<void(const std::string& data, bool binary)>;
    using CloseCallback = std::function<void(int code, const std::string& reason)>;
    using ErrorCallback = std::function<void(const std::string& error)>;
    using OpenCallback = std::function<void()>;

    WebSocket(int id, const std::string& url, size_t max_message_size);
    ~WebSocket();

    int GetId() const { return m_id; }
    const std::string& GetUrl() const { return m_url; }
    WebSocketState GetState() const { return m_state; }

    // Send message (returns false if not connected or message too large)
    bool Send(const std::string& data, bool binary = false);

    // Close connection
    void Close(int code = 1000, const std::string& reason = "");

    // Event callbacks
    void SetOnOpen(OpenCallback cb) { m_on_open = std::move(cb); }
    void SetOnMessage(MessageCallback cb) { m_on_message = std::move(cb); }
    void SetOnClose(CloseCallback cb) { m_on_close = std::move(cb); }
    void SetOnError(ErrorCallback cb) { m_on_error = std::move(cb); }

    // For mock mode - simulate events
    void SimulateOpen();
    void SimulateMessage(const std::string& data, bool binary);
    void SimulateClose(int code, const std::string& reason);
    void SimulateError(const std::string& error);

private:
    int m_id;
    std::string m_url;
    WebSocketState m_state;
    size_t m_max_message_size;

    OpenCallback m_on_open;
    MessageCallback m_on_message;
    CloseCallback m_on_close;
    ErrorCallback m_on_error;
};

class WebSocketManager {
public:
    WebSocketManager(const std::string& app_id, const WebSocketLimits& limits = WebSocketLimits{});
    ~WebSocketManager();

    // Configure domain restrictions (reuses HttpValidator)
    void SetAllowedDomains(const std::vector<std::string>& domains);
    void ClearDomainRestrictions();

    // Create new WebSocket connection
    // Returns WebSocket on success, nullptr on failure (sets error)
    std::shared_ptr<WebSocket> Connect(const std::string& url, std::string& error);

    // Get connection by ID
    std::shared_ptr<WebSocket> GetConnection(int id);

    // Close specific connection
    void CloseConnection(int id, int code = 1000, const std::string& reason = "");

    // Close all connections (called on app stop)
    void CloseAll();

    // Stats
    int GetActiveConnectionCount() const;

    // Access validator for testing
    HttpValidator& GetValidator() { return m_validator; }
    const HttpValidator& GetValidator() const { return m_validator; }

    // For testing: set mock mode
    void SetMockMode(bool enabled) { m_mock_mode = enabled; }
    bool IsMockMode() const { return m_mock_mode; }

private:
    std::string m_app_id;
    WebSocketLimits m_limits;
    HttpValidator m_validator;
    std::map<int, std::shared_ptr<WebSocket>> m_connections;
    int m_next_id = 1;
    mutable std::mutex m_mutex;
    bool m_mock_mode = true;  // Default to mock mode for tests

    bool ValidateUrl(const std::string& url, std::string& error);
};

// Register websocket APIs as part of network global
void RegisterWebSocketAPI(lua_State* L, WebSocketManager* manager);

} // namespace mosis
