#include "ChatClient.hpp"
#include "Chat.pb.h"
#include "MessageRegistry.hpp"
#include "SendChatMessage.hpp"

namespace {

tl::expected<void, tw::chat::ChatClientError> from_error_code(mmo::chat::ChatErrorCode code) {
    switch (code) {
        case mmo::chat::CHAT_ERROR_CODE_OK:
            return {};
        case mmo::chat::CHAT_ERROR_CODE_CHANNEL_NOT_FOUND:
        case mmo::chat::CHAT_ERROR_CODE_ALREADY_IN_CHANNEL:
        case mmo::chat::CHAT_ERROR_CODE_NOT_IN_CHANNEL:
            return tl::make_unexpected(tw::chat::ChatClientError::ChannelNotFound);
        default:
            return tl::make_unexpected(tw::chat::ChatClientError::PermissionDenied);
    }
}

} // namespace

namespace tw::chat {

namespace {

std::unique_ptr<tw::msg::MessageEndpoint> create_endpoint() {
    auto endpoint_r = tw::msg::MessageEndpoint::create();
    if (!endpoint_r) {
        throw std::runtime_error("Chat client failed to create an endpoint: " +
                                 endpoint_r.error().message());
    }

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

tw::msg::MessageConnection* connect_to_server(tw::msg::MessageEndpoint* endpoint,
                                              const std::string&        server_address,
                                              int16_t                   port) {
    auto server_r = endpoint->connect(server_address, port);
    if (!server_r) {
        throw std::runtime_error("Chat client failed to connect: " + server_r.error().message());
    }

    return server_r.value();
}

}

ChatClient::ChatClient(const std::string& server_address, int16_t port)
    : m_endpoint(create_endpoint())
    , m_server(connect_to_server(m_endpoint.get(), server_address, port))
    , m_messages(m_endpoint.get())
{
    m_messages.set_handler<mmo::chat::ChatMessageBroadcastRequest>(
        [this](msg::PeerId, const mmo::chat::ChatMessageBroadcastRequest& proto) {
            if (!m_on_message) return;
            ChatMessage msg;
            msg.channel_id = proto.channel_id();
            msg.client_id  = proto.sender_id();
            msg.message    = proto.message();
            msg.timestamp  = ChatMessage::Clock::now();
            m_on_message(std::move(msg));
        });

    m_messages.set_handler<mmo::chat::SendChatMessageResponse>(
        [this](msg::PeerId, const mmo::chat::SendChatMessageResponse& proto) {
            if (!m_on_send_response) return;
            m_on_send_response(from_error_code(proto.error()));
        });

    m_messages.set_handler<mmo::chat::JoinChannelResponse>(
        [this](msg::PeerId, const mmo::chat::JoinChannelResponse& proto) {
            if (!m_on_join_response) return;
            m_on_join_response(static_cast<uint32_t>(proto.channel_id()), from_error_code(proto.error()));
        });

    m_messages.set_handler<mmo::chat::LeaveChannelResponse>(
        [this](msg::PeerId, const mmo::chat::LeaveChannelResponse& proto) {
            if (!m_on_leave_response) return;
            m_on_leave_response(static_cast<uint32_t>(proto.channel_id()), from_error_code(proto.error()));
        });
}

void ChatClient::update() {
    m_endpoint->update();
}

tl::expected<void, ChatClientError> ChatClient::send_mesg(SendChatMessage message) {
    mmo::chat::SendChatMessageRequest mesg;
    mesg.set_channel_id(message.channel_id);
    mesg.set_message(message.message);

    auto send_r = m_messages.send(m_server, mesg, true);
    if (!send_r)
        return tl::make_unexpected(ChatClientError::PermissionDenied);
    return {};
}

void ChatClient::set_on_message(std::function<void(ChatMessage)> handler) {
    m_on_message = std::move(handler);
}

void ChatClient::set_on_send_response(std::function<void(tl::expected<void, ChatClientError>)> handler) {
    m_on_send_response = std::move(handler);
}

void ChatClient::set_on_join_response(std::function<void(uint32_t, tl::expected<void, ChatClientError>)> handler) {
    m_on_join_response = std::move(handler);
}

void ChatClient::set_on_leave_response(std::function<void(uint32_t, tl::expected<void, ChatClientError>)> handler) {
    m_on_leave_response = std::move(handler);
}

} // namespace tw::chat
