#include "Renderive_WebSocket_Controller.h"

#include "Web_Event_Adapter.h"
#include "Web_Plot_Session.h"

#include <trantor/utils/Logger.h>

#include <exception>
#include <memory>

namespace renderive::web {

void Renderive_WebSocket_Controller::handleNewConnection(
    const drogon::HttpRequestPtr&,
    const drogon::WebSocketConnectionPtr& connection) {
    connection->setContext(std::make_shared<Web_Plot_Session>());
    connection->setPingMessage("renderive", std::chrono::seconds(20));
    LOG_INFO << "Renderive WebSocket connected: " << connection->peerAddr().toIpPort();
}

void Renderive_WebSocket_Controller::handleNewMessage(
    const drogon::WebSocketConnectionPtr& connection,
    std::string&& message,
    const drogon::WebSocketMessageType& type) {
    if (type == drogon::WebSocketMessageType::Ping ||
        type == drogon::WebSocketMessageType::Pong ||
        type == drogon::WebSocketMessageType::Close) {
        return;
    }
    if (type != drogon::WebSocketMessageType::Text) {
        connection->shutdown(drogon::CloseCode::kInvalidMessage,
                             "Renderive accepts text events only");
        return;
    }
    if (message.size() > 16 * 1024) {
        connection->shutdown(drogon::CloseCode::kMessageTooBig,
                             "Renderive event is too large");
        return;
    }
    const auto event = Web_Event_Adapter::decode(message);
    if (!event) {
        connection->shutdown(drogon::CloseCode::kWrongMessageContent,
                             "Invalid Renderive event");
        return;
    }
    const auto session = connection->getContext<Web_Plot_Session>();
    if (!session) {
        connection->shutdown(drogon::CloseCode::kUnexpectedCondition,
                             "Renderive session is unavailable");
        return;
    }
    try {
        if (auto response = session->handle(*event)) {
            const auto message_type = response->type == Web_Response_Type::Pixels
                                          ? drogon::WebSocketMessageType::Binary
                                          : drogon::WebSocketMessageType::Text;
            connection->send(response->payload.data(), response->payload.size(),
                             message_type);
        }
    } catch (const std::exception& error) {
        LOG_ERROR << "Renderive WebSocket session failed: " << error.what();
        connection->shutdown(drogon::CloseCode::kUnexpectedCondition,
                             "Renderive rendering failed");
    }
}

void Renderive_WebSocket_Controller::handleConnectionClosed(
    const drogon::WebSocketConnectionPtr& connection) {
    LOG_INFO << "Renderive WebSocket closed: " << connection->peerAddr().toIpPort();
    connection->clearContext();
}

} // namespace renderive::web
