// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#pragma once

#include <cstdint>
#include <string>
#include <vector>

namespace dragonx {
namespace data {

/**
 * @brief A single address book entry
 */
struct AddressBookEntry {
    std::string label;
    std::string address;
    std::string notes;
    // Per-wallet visibility scope: "global" (shown in every wallet) or a wallet-identity hash
    // (shown only when that wallet is the active one). Empty is treated as "global" so legacy
    // entries — written before multi-wallet scoping — keep showing everywhere.
    std::string scope = "global";
    // Contact avatar: "" = default type badge (Z/T), "icon:<name>" = a Material wallet-icon,
    // "img:<path>" = a custom image (copied into <config>/contact-avatars/).
    std::string avatar;

    AddressBookEntry() = default;
    AddressBookEntry(const std::string& l, const std::string& a, const std::string& n = "",
                     const std::string& s = "global")
        : label(l), address(a), notes(n), scope(s) {}

    bool isGlobal() const { return scope.empty() || scope == "global"; }
    // Visible under the wallet whose identity hash is walletHash (or globally).
    bool visibleInWallet(const std::string& walletHash) const {
        return isGlobal() || scope == walletHash;
    }
};

/**
 * @brief Address book manager
 * 
 * Stores labeled addresses for easy access when sending.
 */
class AddressBook {
public:
    AddressBook();
    ~AddressBook();

    /**
     * @brief Load address book from disk
     * @return true if loaded successfully
     */
    bool load();

    /**
     * @brief Save address book to disk
     * @return true if saved successfully
     */
    bool save();

    /**
     * @brief Get default address book file path
     */
    static std::string getDefaultPath();

    /**
     * @brief Add a new entry
     * @param entry The entry to add
     * @return true if added (false if duplicate address)
     */
    bool addEntry(const AddressBookEntry& entry);

    /**
     * @brief Update an existing entry
     * @param index Index of entry to update
     * @param entry New entry data
     * @return true if updated
     */
    bool updateEntry(size_t index, const AddressBookEntry& entry);

    /**
     * @brief Remove an entry
     * @param index Index of entry to remove
     * @return true if removed
     */
    bool removeEntry(size_t index);

    /**
     * @brief Re-attach contacts stuck on a legacy (drifting address-hash) scope to a stable wallet id.
     *        Rewrites every non-global entry whose scope is NOT already a stable "w:"-prefixed id to
     *        `scopeId`, and saves once if anything changed. Recovery for contacts orphaned when the
     *        old address-set-hash scope shifted (e.g. after creating a new address).
     * @return number of entries re-scoped.
     */
    int reattachLegacyScopes(const std::string& scopeId);

    /**
     * @brief Find entry by address (any scope). Used for contact-label lookups.
     * @param address Address to search for
     * @return Index or -1 if not found
     */
    int findByAddress(const std::string& address) const;

    /**
     * @brief Is there an existing entry with this address that would be visible ALONGSIDE a new
     *        entry of the given scope? (Same wallet, or either side global.) Used to reject
     *        duplicates within a wallet while still allowing the same address in different wallets.
     * @param excludeIndex Storage index to skip (for edit), or -1.
     */
    bool hasVisibleDuplicate(const std::string& address, const std::string& scope,
                             int excludeIndex = -1) const;

    /**
     * @brief Get all entries
     */
    const std::vector<AddressBookEntry>& entries() const { return entries_; }

    /**
     * @brief Get entry count
     */
    size_t size() const { return entries_.size(); }

    /**
     * @brief Monotonic counter bumped on every content change (add/update/remove/load/sweep). Consumers
     * (e.g. the chat conversation-list memo) key their caches off this so an IN-PLACE edit — a rename or
     * address change that keeps size() constant — still invalidates them.
     */
    std::uint64_t revision() const { return revision_; }

    /**
     * @brief UI-sweep ONLY: replace the in-memory entries WITHOUT persisting to disk, so the sweep can
     * seed demo contacts and restore the real book without a disk write. Do not use outside the sweep.
     */
    void sweepSetEntries(std::vector<AddressBookEntry> e) { entries_ = std::move(e); ++revision_; }

    /**
     * @brief Check if empty
     */
    bool empty() const { return entries_.empty(); }

private:
    std::vector<AddressBookEntry> entries_;
    std::uint64_t revision_ = 0;
    std::string file_path_;
};

} // namespace data
} // namespace dragonx
