#include "NetworkReceiver.hpp"

#include <stdexcept>

namespace tw::net {

namespace {

std::unique_ptr<msg::MessageEndpoint> bind_endpoint(int32_t port) {
    auto endpoint_r = msg::MessageEndpoint::bind(port);
    if(!endpoint_r) {
        throw std::runtime_error("NetworkReceiver: failed to bind to port " + std::to_string(port) +
                                 ": " + endpoint_r.error().message());
    }

    return std::move(endpoint_r.value());
}

}

NetworkReceiver::NetworkReceiver(
    PlayerSessionRegistry* session_registry,
    int32_t udp_port,
    NetworkMetricsReporter* metrics_reporter
) :
    m_session_registry(session_registry),
    m_endpoint(bind_endpoint(udp_port)),
    m_messages(m_endpoint.get()),
    m_metrics_reporter(metrics_reporter)
{
    // Registering the session here rather than after update() means a peer
    // already has one by the time its first message is dispatched.
    m_endpoint->set_on_peer_connected([this](msg::PeerId peer) {
        auto session_id = m_session_registry->register_session(peer, m_endpoint->peer(peer));
        m_new_sessions.push_back(session_id);
    });

    spdlog::info("Running on port: {}", udp_port);
}

void NetworkReceiver::report_traffic() {
    const uint64_t received = m_endpoint->bytes_received();
    const uint64_t sent     = m_endpoint->bytes_sent();

    m_metrics_reporter->add_inbound(received - m_reported_bytes_received);
    m_metrics_reporter->add_outbound(sent - m_reported_bytes_sent);

    m_reported_bytes_received = received;
    m_reported_bytes_sent     = sent;
}

void NetworkReceiver::update() {
    m_endpoint->update();

    report_traffic();
}

size_t NetworkReceiver::send_framed(SessionId session_id, std::span<const std::byte> message) {
    if(message.empty()) {
        return 0;
    }

    auto* session = m_session_registry->session(session_id);
    if(session == nullptr) {
        return 0;
    }

    auto send_r = session->connection->send_framed(message, false);
    if(!send_r) {
        spdlog::error("Failed to send to session {}: {}", session_id, send_r.error().message());
        return 0;
    }

    return message.size();
}

}
