/*
 * server/media_relay.h — UDP SFU relay for M2 voice.
 *
 * Design: docs/architecture.md §5, docs/voice.md §2.
 * Receives encrypted UDP voice frames from clients, decrypts+authenticates them,
 * re-encrypts for each channel member, and forwards.
 *
 * Flow:
 *   1. Client sends kFrameUdpBinding UDP packet (plaintext) → MediaRelay looks up
 *      the 16-byte token in SessionRegistry, associates the sender endpoint with
 *      the ConnSession, and calls session->set_udp_endpoint().
 *   2. Client sends kFrameVoice UDP packets → MediaRelay decrypts via recv_crypto(),
 *      finds channel members via find_channel_sessions(), re-encrypts via send_crypto(),
 *      and sends to each member's UDP endpoint.
 */
#ifndef VOICECAT_SERVER_MEDIA_RELAY_H
#define VOICECAT_SERVER_MEDIA_RELAY_H

#ifdef VOICECAT_HAS_NET

#include <memory>

#define ASIO_STANDALONE 1
#include <asio.hpp>

#include "net/transport.h"

namespace voicecat::server {

class SessionRegistry;

class MediaRelay {
   public:
    MediaRelay(asio::io_context& io, std::shared_ptr<SessionRegistry> registry);
    ~MediaRelay();

    // Bind the UDP socket. port=0 lets the OS pick. Must be called before start().
    bool bind(uint16_t port = 0);

    // Begin async receive loop. Call once after bind().
    void start();

    // Stop receiving and close the socket.
    void stop();

    // Actual bound port (after bind()).
    uint16_t media_port() const;

   private:
    void on_udp_frame(const uint8_t* data, size_t len, asio::ip::udp::endpoint sender);

    // Count a dropped inbound voice frame (by reason) and emit a rate-limited summary
    // to stderr. Runs on the io thread, so plain counters are safe.
    void note_drop(const char* reason);

    asio::io_context&                io_;
    std::shared_ptr<SessionRegistry> registry_;
    voicecat::net::UdpMediaChannel   udp_;

    // Diagnostics: dropped-frame counters so a wedged media path is observable.
    uint64_t drop_no_endpoint_ = 0;  // voice from an unmapped UDP endpoint
    uint64_t drop_no_crypto_   = 0;  // session has no recv_crypto yet
    uint64_t drop_open_failed_ = 0;  // AEAD auth failure or replay reject
    int64_t  last_drop_log_ms_ = 0;

    // Scratch buffer for re-encrypted payloads (size = max_frame + 16 MAC)
    static constexpr size_t kMaxPayload = 1500;
    std::vector<uint8_t>    seal_buf_  = std::vector<uint8_t>(kMaxPayload + 16, uint8_t{0});
    std::vector<uint8_t>    plain_buf_ = std::vector<uint8_t>(kMaxPayload, uint8_t{0});
};

}  // namespace voicecat::server

#endif  // VOICECAT_HAS_NET
#endif  // VOICECAT_SERVER_MEDIA_RELAY_H
