/*
 * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>,
 * released under GNU AGPL v3 license:
 * https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
 */

#include "PlayerGuideTransport.h"
#include "PlayerGuideMgr.h"

#include "Chat.h"
#include "Player.h"
#include "WorldPacket.h"
#include "WorldSession.h"

#include <algorithm>
#include <cstdio>
#include <vector>

namespace MoonWell::PlayerGuide
{
    namespace
    {
        // The client-visible chat body must stay strictly below the
        // 255-byte addon limit. We reserve room for the framing header.
        // The mgr clamps its configured chunk size into a safe range.
        constexpr std::size_t HARD_LIMIT = 250;

        void SendOneChunk(Player* player, std::string_view command,
                          uint32 seq, uint32 total, std::string_view body)
        {
            std::string msg;
            msg.reserve(ADDON_PREFIX[0] ? body.size() + 32 : body.size());
            msg.append(ADDON_PREFIX);
            msg.push_back('\t');
            msg.append(command.data(), command.size());
            msg.push_back('\t');

            char buf[24];
            std::snprintf(buf, sizeof(buf), "%u\t%u\t", seq, total);
            msg.append(buf);

            msg.append(body.data(), body.size());

            if (msg.size() > HARD_LIMIT)
                msg.resize(HARD_LIMIT);

            WorldPacket data;
            ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER,
                LANG_ADDON, player, player, msg);
            player->GetSession()->SendPacket(&data);
        }
    }

    void Transport::Send(Player* player, std::string_view command,
                         std::string_view body)
    {
        if (!player || !player->GetSession())
            return;

        // How much body fits into a chunk after framing.
        // Framing = prefix(4) + '\t' + cmd(N) + '\t' + "65535\t65535\t".
        // Use Mgr's configured chunk size when sensible.
        std::size_t framing = 4 + 1 + command.size() + 1 + 16;
        std::size_t budget  = sPlayerGuideMgr->ChunkSize() > framing
            ? (sPlayerGuideMgr->ChunkSize() - framing)
            : 64;

        if (body.size() <= budget)
        {
            SendOneChunk(player, command, 1, 1, body);
            return;
        }

        // Multi-chunk: split blindly by byte budget. JSON tolerates
        // concatenation on the client.
        uint32 total = static_cast<uint32>(
            (body.size() + budget - 1) / budget);
        uint32 seq   = 1;
        for (std::size_t off = 0; off < body.size(); off += budget, ++seq)
        {
            std::size_t take = std::min(budget, body.size() - off);
            SendOneChunk(player, command, seq, total,
                std::string_view(body.data() + off, take));
        }
    }

    void Transport::SendSetData(Player* player, std::string const& json)
    {
        Send(player, ServerCmd::SET_DATA, json);
    }

    void Transport::SendNotify(Player* player, std::string const& text)
    {
        Send(player, ServerCmd::NOTIFY, text);
    }
}
