// DragonX Wallet - HushChat outgoing memo construction (implementation).

#include "chat_outgoing.h"

#include "chat_protocol.h"  // kHushChat* constants

#include <nlohmann/json.hpp>

namespace dragonx::chat {

namespace {

// Serialize the HushChat header. nlohmann emits object keys in sorted (alphabetical) order —
// cid,e,h,p,t,v,z — which is exactly SilentDragonXLite's on-wire key order.
std::string buildHeaderMemo(const std::string& replyZaddr,
                            const std::string& conversationId,
                            const char* type,
                            const std::string& streamHeaderHex,
                            const std::string& publicKeyHex,
                            std::int64_t sentAt)
{
    nlohmann::json header;
    header["h"] = 1;                          // header number (>= 1)
    header["v"] = kHushChatSupportedVersion;  // 0
    header["z"] = replyZaddr;                 // where the peer should reply (my address)
    header["cid"] = conversationId;
    header["t"] = type;                       // "Memo" or "Cont"
    header["e"] = streamHeaderHex;            // 48-hex secretstream header (Memo) / "" (Cont)
    header["p"] = publicKeyHex;               // my 64-hex crypto_kx public key
    if (sentAt > 0) header["ts"] = sentAt;    // optional sender compose time (Unix s) — receiver shows this
    return header.dump();
}

bool present(const std::string& value) { return !value.empty(); }

} // namespace

std::array<ChatSendOutput, 2> chatSendOutputs(const OutgoingChatMemos& memos, bool utf8Prefix)
{
    const std::string prefix = utf8Prefix ? "utf8:" : "";
    return {{
        { memos.recipientZaddr, prefix + memos.headerMemo },   // header — the lower memo position
        { memos.recipientZaddr, prefix + memos.payloadMemo },
    }};
}

ChatComposeStatus buildOutgoingMessage(const ChatKeyPair& mine,
                                       const std::string& myPublicKeyHex,
                                       const std::string& myReplyZaddr,
                                       const std::string& peerPublicKeyHex,
                                       const std::string& peerZaddr,
                                       const std::string& conversationId,
                                       const std::string& plaintext,
                                       OutgoingChatMemos& out)
{
    if (plaintext.empty()) return ChatComposeStatus::EmptyBody;
    if (!present(myPublicKeyHex) || !present(myReplyZaddr) || !present(peerZaddr) ||
        !present(conversationId)) {
        return ChatComposeStatus::MissingField;
    }
    if (peerPublicKeyHex.size() != kHushChatPublicKeyHexLength) return ChatComposeStatus::BadPeerKey;

    std::string streamHeaderHex;
    std::string ciphertextHex;
    if (encryptOutgoing(mine, peerPublicKeyHex, plaintext, streamHeaderHex, ciphertextHex)
        != ChatCryptoStatus::Ok) {
        return ChatComposeStatus::EncryptFailed;
    }

    OutgoingChatMemos memos;
    memos.recipientZaddr = peerZaddr;
    memos.headerMemo = buildHeaderMemo(myReplyZaddr, conversationId, "Memo", streamHeaderHex, myPublicKeyHex,
                                       static_cast<std::int64_t>(std::time(nullptr)));
    memos.payloadMemo = ciphertextHex;
    if (memos.headerMemo.size() > kHushChatMemoByteLimit ||
        memos.payloadMemo.size() > kHushChatMemoByteLimit) {
        return ChatComposeStatus::TooLong;
    }
    out = std::move(memos);
    return ChatComposeStatus::Ok;
}

ChatComposeStatus buildOutgoingContactRequest(const std::string& myPublicKeyHex,
                                              const std::string& myReplyZaddr,
                                              const std::string& peerZaddr,
                                              const std::string& conversationId,
                                              const std::string& requestText,
                                              OutgoingChatMemos& out)
{
    if (requestText.empty()) return ChatComposeStatus::EmptyBody;
    // The receive parser treats any memo starting with '{' as a header, so a request payload must
    // not start with one (see isContactPayloadCandidate in chat_protocol.cpp).
    if (requestText.front() == '{') return ChatComposeStatus::BadRequestText;
    if (!present(myPublicKeyHex) || !present(myReplyZaddr) || !present(peerZaddr) ||
        !present(conversationId)) {
        return ChatComposeStatus::MissingField;
    }

    OutgoingChatMemos memos;
    memos.recipientZaddr = peerZaddr;
    memos.headerMemo = buildHeaderMemo(myReplyZaddr, conversationId, "Cont", "", myPublicKeyHex,
                                       static_cast<std::int64_t>(std::time(nullptr)));
    memos.payloadMemo = requestText;
    if (memos.headerMemo.size() > kHushChatMemoByteLimit ||
        memos.payloadMemo.size() > kHushChatMemoByteLimit) {
        return ChatComposeStatus::TooLong;
    }
    out = std::move(memos);
    return ChatComposeStatus::Ok;
}

} // namespace dragonx::chat
