#pragma once

// DragonX Wallet - HushChat persistent message store (Phase 2).
//
// Sqlite-backed, encrypted at rest with a SEED-DERIVED key (no wallet passphrase). Every record —
// message bodies, peer z-addresses, threading (conversation id), and timestamps — is AEAD-encrypted
// under a key derived from the wallet's own seed secret (the same secret used for the chat
// identity), and even the per-message dedup key is a KEYED hash of the txid — so the database
// reveals nothing about your conversations to disk-level access without the seed. Rows are
// partitioned by a seed-derived wallet tag so one file can hold several wallets, each readable only
// with its own seed. Not thread-safe — drive from the main thread. Mirrors the lifecycle of
// data::TransactionHistoryCache.

#include "chat_message.h"

#include <array>
#include <cstddef>
#include <string>
#include <unordered_set>
#include <vector>

struct sqlite3;

namespace dragonx::chat {

class ChatDatabase {
public:
    ChatDatabase();
    explicit ChatDatabase(std::string databasePath);
    ~ChatDatabase();

    ChatDatabase(const ChatDatabase&) = delete;
    ChatDatabase& operator=(const ChatDatabase&) = delete;

    static std::string defaultDatabasePath();

    // Derive the storage key + wallet tag from the wallet's seed secret and open the DB. The caller
    // still owns and must wipe `secret`. Returns false on sodium/db failure (DB then stays locked).
    bool unlockWithSecret(const std::string& secret);
    void lock();  // wipe the key material (DB handle stays open); load()/append() then no-op
    bool hasKey() const { return key_ready_; }

    // Persist one message (INSERT OR IGNORE, deduped by a keyed hash of txid+payload_position).
    // Returns true if newly inserted; false on duplicate or while locked.
    bool append(const ChatMessage& message);

    // Persist-or-overwrite one message by its (txid+position) dedup key. Unlike append(), this
    // updates an existing row's payload — used for outgoing echoes whose delivery status changes
    // (Sending → Sent/Failed). Returns true on success; false while locked / on error.
    bool upsert(const ChatMessage& message);

    // Decrypt and return every stored message for the unlocked wallet, in insertion order. Empty
    // while locked or if none. Rows that fail to decrypt/parse are skipped.
    std::vector<ChatMessage> load();

    void clearWallet();  // delete the unlocked wallet's rows

    // Per-conversation local delete. Removes the given messages' rows; when `tombstone` is true it also
    // records their (txid,position) dedup keys so a chain re-scan never re-imports them — this backs the
    // "delete, but a NEW message revives the thread" path. With `tombstone` false the rows are simply
    // removed (used by "delete & block", where a settings-level cid block suppresses re-import until the
    // user unblocks, at which point the history re-imports from chain). Atomic; no-op while locked.
    bool deleteMessages(const std::vector<ChatMessage>& messages, bool tombstone);

    // True if this message's (txid,position) was locally deleted with a tombstone. Checked against an
    // in-memory cache loaded on unlock — O(1), no SQL. False while locked.
    bool isTombstoned(const ChatMessage& message) const;

private:
    bool ensureOpen();
    void loadTombstones();  // populate tombstones_ from chat_deleted for the unlocked wallet
    bool exec(const char* sql);
    bool createSchema();
    std::string dedupHash(const std::string& txid, std::size_t position) const;
    std::string serialize(const ChatMessage& message) const;
    bool deserialize(const std::string& json, ChatMessage& out) const;
    bool encrypt(const std::string& plain,
                 std::vector<unsigned char>& nonce,
                 std::vector<unsigned char>& cipher) const;
    bool decrypt(const std::vector<unsigned char>& nonce,
                 const std::vector<unsigned char>& cipher,
                 std::string& plain) const;
    void close();

    sqlite3* db_ = nullptr;
    std::string database_path_;
    std::array<unsigned char, 32> key_{};  // AEAD storage key (seed-derived)
    std::string wallet_tag_;               // seed-derived row partition (a keyed hash, hex)
    bool key_ready_ = false;
    std::unordered_set<std::string> tombstones_;  // dedup_hash cache of locally-deleted messages
};

} // namespace dragonx::chat
