#include "websocket.hpp"
void EventEmitter::emit(const std::string& event, const nlohmann::json& data) {
    if (auto it = handlers.find(event); it != handlers.end()) {
        std::for_each(it->second.begin(), it->second.end(), [&data](const auto& handler) {
            handler(data);
        });
    }
}
void EventEmitter::getEvents() const {
    Log::create(CRITICAL, WEBSOCKET, "Event list");
    for (const auto& handler : handlers) {
        Log::create(INFO, WEBSOCKET, handler.first);
    }
    Log::create(CRITICAL, WEBSOCKET, "End of list");
}
void EventEmitter::start() const {
    while (true) std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
void EventEmitter::on(const std::string& event, eventHandlers handler) {
    handlers[event].emplace_back(std::move(handler));
}
void EventEmitter::once(const std::string& event, const eventHandlers& handler) {
    auto wrappedHandler = [this, event, handler](const nlohmann::json& data) {
        handler(data);
        off(event, handler);
    };
    handlers[event].emplace_back(std::move(wrappedHandler));
}
void EventEmitter::off(const std::string& event, eventHandlers handler) {
    if (auto it = handlers.find(event); it != handlers.cend()) {
        it->second.erase(std::ranges::remove_if(it->second, [&handler](const eventHandlers& h) {
            return h.target<eventHandlers>() == handler.target<eventHandlers>();
        }).begin(), it->second.cend());
    }
}
WebSocket::WebSocket(const std::string& token, int intents, bool isBot) : token(token), intents(intents), isBot(isBot) {
    nlohmann::json id = {
    {"op", GatewayOpcodes::Identify},
    {"d", {
        {"token", token},
        {"intents", intents},
        {"properties", {
            {"os", "linux"},
            {"browser", "firefox"},
            {"device", "firefox"}
        }},
    {"compress", 1},
    {"presence", {
        {"activities", nlohmann::json::array({
            {
                {"name", "meow"},
                {"type", 2}
            }
        })},
        {"status", "idle"},
        {"since", 0},
        {"afk", false}
    }}
}}
    };
    ix::initNetSystem();
    webSocket.setUrl("wss://gateway.discord.gg/?v=10&encoding=json");
    webSocket.disableAutomaticReconnection();
    Log::create(INFO, WEBSOCKET, "ixwebsocket init");
    webSocket.setOnMessageCallback([this, res = nlohmann::json(), heartbeat_interval = 0, connected = false, id](const ix::WebSocketMessagePtr& msg) mutable {
        switch (msg->type) {
            case ix::WebSocketMessageType::Message:
                res = nlohmann::json::parse(msg->str);
                Log::create(INFO, WEBSOCKET, std::to_string(res["op"].get<int>()) + " " + res["t"].dump());
                switch (res["op"].get<GatewayOpcodes>()) {
                    case GatewayOpcodes::Hello:
                        heartbeat_interval = res["d"]["heartbeat_interval"].get<int>();
                        if (connected) connected = true; else webSocket.send(id.dump());
                        std::thread([this, &heartbeat_interval, &connected]() {
                            while (connected && heartbeat_interval != -1) {
                                Log::create(INFO, WEBSOCKET, "Heartbeat " + std::to_string(heartbeat_interval));
                                std::this_thread::sleep_for(std::chrono::milliseconds(heartbeat_interval));
                                webSocket.send(payload.dump());
                            }
                        }).detach();
                        break;
                    case GatewayOpcodes::Dispatch:
                        Log::create(INFO, WEBSOCKET, res.dump());
                        emit(res["t"].get<std::string>(), res);
                        break;
                    default:
                        break;
                }
                break;
            case ix::WebSocketMessageType::Error:
                Log::force_create(ERROR, WEBSOCKET, msg->errorInfo.reason);
                exit(-1);
                break;
            default:
                break;
        }

    });
    webSocket.start();
}
auto WebSocket::getInstance(const std::string& token, int intents, bool bot) -> WebSocket& {
    Log::create(INFO, WEBSOCKET, "Instance event");
    static WebSocket instance(token, intents, bot);
    return instance;
}
WebSocket::~WebSocket() {
    webSocket.close();
    ix::uninitNetSystem();
}
auto WebSocket::getToken() const -> std::string {
    return this->isBot ? std::string("Bot " + this->token) : this->token;
}
auto WebSocket::getIntents() const -> int {
    return this->intents;
}
_maybe_unused void WebSocket::sendPresenceUpdate(const char* statusType, const std::string& activityName, const int& activityType) {
    nlohmann::json prsUpdate = {
        {"op", 3},
        {"d", {
            {"since", 0},
            {"activities", nlohmann::json::array({{"name", activityName}, {"type", activityType}})},
            {"status", statusType},
            {"afk", false}
        }}
    };
    webSocket.send(prsUpdate.dump());
}