#pragma once

// DragonX Wallet - HushChat in-memory message store: the fast read model / dedup view. Durable
// persistence lives in ChatDatabase; ChatService rehydrates this store from it on unlock.

#include "chat_message.h"

#include <cstdint>
#include <functional>
#include <string>
#include <unordered_set>
#include <vector>

namespace dragonx::chat {

// Threads messages by conversation_id (cid) and deduplicates by (txid, payload_position) so
// re-scanning the chain never double-inserts. Not thread-safe — drive from the main thread.
class ChatStore {
public:
    // Returns true if newly inserted, false if a duplicate was ignored.
    bool append(const ChatMessage& message);

    // Messages in a conversation, in insertion order.
    std::vector<ChatMessage> conversation(const std::string& conversationId) const;

    // Update an outgoing echo's delivery status by its local txid. Returns a pointer to the updated
    // message (for the caller to persist), or nullptr if no message has that txid.
    const ChatMessage* updateDelivery(const std::string& txid, ChatDelivery delivery);

    // Distinct conversation ids, in first-seen order.
    std::vector<std::string> conversationIds() const;

    // The set of on-chain txids that carried a chat message (sent or received, messages + contact
    // requests) — used by the History tab to badge / filter chat transactions. O(messages), no copies.
    std::unordered_set<std::string> chatTxids() const {
        std::unordered_set<std::string> out;
        out.reserve(messages_.size());
        for (const auto& m : messages_)
            if (!m.txid.empty()) out.insert(m.txid);
        return out;
    }

    // Remove every message in a conversation from the in-memory view and return the removed messages
    // (so the caller can delete/tombstone their persisted rows). Re-appends are prevented by the ingest
    // guard (blocked-cid predicate / DB tombstone), not here.
    std::vector<ChatMessage> eraseConversation(const std::string& conversationId);

    std::size_t size() const { return messages_.size(); }
    bool empty() const { return messages_.empty(); }
    void clear();

    // Monotonic counter bumped on every mutation (append / updateDelivery change / eraseConversation /
    // clear). Callers memoize expensive per-frame reads (conversation-list build, unread count) against it
    // so they only rebuild when the store actually changed.
    std::uint64_t revision() const { return revision_; }

    // Count unread incoming messages in a SINGLE pass over the store: an incoming message counts when its
    // cid is not `excluded` and its timestamp is newer than `seenFor(cid)`. Avoids the per-conversation
    // copy+sort that conversation() does — the count doesn't need ordering.
    int countUnread(const std::function<bool(const std::string&)>& excluded,
                    const std::function<std::int64_t(const std::string&)>& seenFor) const;

private:
    static std::string dedupKey(const ChatMessage& message);

    std::vector<ChatMessage> messages_;
    std::unordered_set<std::string> seen_;
    std::uint64_t revision_ = 0;
};

} // namespace dragonx::chat
