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

#include "wallet_index.h"

#include <nlohmann/json.hpp>
#include <algorithm>
#include <filesystem>
#include <fstream>

#include "../util/logger.h"
#include "../util/platform.h"

namespace fs = std::filesystem;
using json = nlohmann::json;

namespace dragonx {
namespace data {

namespace {
// A field-wise "did anything change" check so we only rewrite wallets.json when needed.
bool sameEntry(const WalletIndexEntry& a, const WalletIndexEntry& b)
{
    return a.fileName == b.fileName
        && a.displayName == b.displayName
        && a.walletIdentityHash == b.walletIdentityHash
        && a.scopeId == b.scopeId
        && a.cachedBalance == b.cachedBalance
        && a.cachedAddressCount == b.cachedAddressCount
        && a.lastOpenedEpoch == b.lastOpenedEpoch
        && a.sizeBytesAtLastOpen == b.sizeBytesAtLastOpen
        && a.syncedHere == b.syncedHere;
}
}  // namespace

std::string WalletIndex::getDefaultPath()
{
    // Co-located with settings.json / addressbook.json in the per-variant config dir.
    const std::string dir = util::Platform::getConfigDir();
    fs::create_directories(dir);
    return (fs::path(dir) / "wallets.json").string();
}

bool WalletIndex::load()
{
    file_path_ = getDefaultPath();
    entries_.clear();
    extra_folders_.clear();

    std::ifstream file(file_path_);
    if (!file.is_open()) return true;  // no file yet is fine

    try {
        json j;
        file >> j;

        if (j.contains("entries") && j["entries"].is_array()) {
            for (const auto& e : j["entries"]) {
                WalletIndexEntry w;
                w.fileName = e.value("file", "");
                if (w.fileName.empty()) continue;
                w.displayName = e.value("name", w.fileName);
                w.walletIdentityHash = e.value("identity", "");
                w.scopeId = e.value("scopeId", "");
                w.cachedBalance = e.value("balance", -1.0);
                w.cachedAddressCount = e.value("addresses", (long long)-1);
                w.lastOpenedEpoch = e.value("lastOpened", (long long)0);
                w.sizeBytesAtLastOpen = e.value("size", (long long)0);
                w.syncedHere = e.value("syncedHere", false);
                entries_.push_back(std::move(w));
            }
        }
        if (j.contains("extraFolders") && j["extraFolders"].is_array()) {
            for (const auto& d : j["extraFolders"]) {
                if (d.is_string() && !d.get<std::string>().empty())
                    extra_folders_.push_back(d.get<std::string>());
            }
        }
        DEBUG_LOGF("Wallet index loaded: %zu wallets, %zu extra folders\n",
                   entries_.size(), extra_folders_.size());
        return true;
    } catch (const std::exception& e) {
        DEBUG_LOGF("Error loading wallet index: %s\n", e.what());
        return false;
    }
}

bool WalletIndex::save()
{
    if (file_path_.empty()) file_path_ = getDefaultPath();

    try {
        json j;
        j["entries"] = json::array();
        for (const auto& w : entries_) {
            json e;
            e["file"] = w.fileName;
            e["name"] = w.displayName;
            e["identity"] = w.walletIdentityHash;
            e["scopeId"] = w.scopeId;
            e["balance"] = w.cachedBalance;
            e["addresses"] = w.cachedAddressCount;
            e["lastOpened"] = w.lastOpenedEpoch;
            e["size"] = w.sizeBytesAtLastOpen;
            e["syncedHere"] = w.syncedHere;
            j["entries"].push_back(std::move(e));
        }
        j["extraFolders"] = extra_folders_;

        // No secrets here (file names + coarse metadata) but keep it owner-only for consistency.
        if (!util::Platform::writeFileAtomically(file_path_, j.dump(2), /*restrictPermissions=*/true)) {
            DEBUG_LOGF("Could not write wallet index: %s\n", file_path_.c_str());
            return false;
        }
        return true;
    } catch (const std::exception& e) {
        DEBUG_LOGF("Error saving wallet index: %s\n", e.what());
        return false;
    }
}

bool WalletIndex::upsert(const WalletIndexEntry& e)
{
    for (auto& w : entries_) {
        if (w.fileName == e.fileName) {
            if (sameEntry(w, e)) return false;  // nothing changed -> caller can skip save()
            w = e;
            return true;
        }
    }
    entries_.push_back(e);
    return true;
}

const WalletIndexEntry* WalletIndex::find(const std::string& fileName) const
{
    for (const auto& w : entries_) {
        if (w.fileName == fileName) return &w;
    }
    return nullptr;
}

bool WalletIndex::addExtraFolder(const std::string& dir)
{
    if (dir.empty()) return false;
    if (std::find(extra_folders_.begin(), extra_folders_.end(), dir) != extra_folders_.end())
        return false;
    extra_folders_.push_back(dir);
    return true;
}

bool WalletIndex::removeExtraFolder(const std::string& dir)
{
    auto it = std::find(extra_folders_.begin(), extra_folders_.end(), dir);
    if (it == extra_folders_.end()) return false;
    extra_folders_.erase(it);
    return true;
}

} // namespace data
} // namespace dragonx
