#include <myapp/rcon/server.h>

namespace myapp::rcon {

static std::string const g_tag = "RCON";

server::server(std::shared_ptr<logging::logger> logger, std::unique_ptr<rcon::peer_provider> peerProvider)
  : logger_(std::move(logger))
  , peerProvider_(std::move(peerProvider))
  , data_handler_([](std::string msg) { return std::string(); })
{
    peerProvider_->on_callback([this](std::unique_ptr<connection> conn_ptr) { on_accept(std::move(conn_ptr)); });
    peerProvider_->on_error([this](std::string err_str) { on_error(std::move(err_str)); });
}

void server::start(std::string unix_path)
{
    logger_->notice(g_tag, "Starting RCON listener at `" + unix_path + "'");
    peerProvider_->server(std::move(unix_path));
}

void server::publish(std::string message)
{
    logger_->info(g_tag, "Publishing message: " + message);
    for (auto& client : clients_)
        client->write(std::move(message));
}

void server::on_accept(std::unique_ptr<connection> client)
{
    logger_->notice(g_tag, "New client has been connected");
    client->on_peer_closed([this, conn = client.get()] { on_closed(*conn); });
    client->on_data([this, conn = client.get()](std::string msg) { on_data(*conn, std::move(msg)); });
    clients_.emplace_back(std::move(client));
}

void server::on_closed(connection& client)
{
    logger_->notice(g_tag, "Client has been disconnected");
    clients_.remove_if([ptr = &client](auto const& item) { return item.get() == ptr; });
}

void server::on_data(connection& conn, std::string msg)
{
    logger_->notice(g_tag, "Data received: " + msg);
    auto reply = data_handler_(std::move(msg));
    if (reply.empty())
        return;

    logger_->info(g_tag, "Response sent: " + reply);
    conn.write(std::move(reply));
}

void server::on_error(std::string error)
{
    logger_->error(g_tag, error);
}

void server::set_on_data(std::function<std::string(std::string)> handler) noexcept
{
    data_handler_ = std::move(handler);
}

} /* namespace myapp::rcon */
