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

#include "address_book.h"

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

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

#ifdef _WIN32
#include <shlobj.h>
#else
#include <pwd.h>
#include <unistd.h>
#endif

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

namespace dragonx {
namespace data {

AddressBook::AddressBook() = default;
AddressBook::~AddressBook() = default;

std::string AddressBook::getDefaultPath()
{
#ifdef _WIN32
    char path[MAX_PATH];
    if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, path))) {
        std::string dir = std::string(path) + "\\ObsidianDragon";
        fs::create_directories(dir);
        return dir + "\\addressbook.json";
    }
    return "addressbook.json";
#elif defined(__APPLE__)
    const char* home = getenv("HOME");
    if (!home) {
        struct passwd* pw = getpwuid(getuid());
        home = pw->pw_dir;
    }
    std::string dir = std::string(home) + "/Library/Application Support/ObsidianDragon";
    fs::create_directories(dir);
    return dir + "/addressbook.json";
#else
    const char* home = getenv("HOME");
    if (!home) {
        struct passwd* pw = getpwuid(getuid());
        home = pw->pw_dir;
    }
    std::string dir = std::string(home) + "/.config/ObsidianDragon";
    fs::create_directories(dir);
    return dir + "/addressbook.json";
#endif
}

bool AddressBook::load()
{
    file_path_ = getDefaultPath();
    
    std::ifstream file(file_path_);
    if (!file.is_open()) {
        // No file yet - that's OK
        return true;
    }
    
    try {
        json j;
        file >> j;
        
        entries_.clear();
        
        if (j.contains("entries") && j["entries"].is_array()) {
            for (const auto& entry : j["entries"]) {
                AddressBookEntry e;
                e.label = entry.value("label", "");
                e.address = entry.value("address", "");
                e.notes = entry.value("notes", "");
                
                if (!e.address.empty()) {
                    entries_.push_back(e);
                }
            }
        }
        
        DEBUG_LOGF("Address book loaded: %zu entries\n", entries_.size());
        return true;
        
    } catch (const std::exception& e) {
        DEBUG_LOGF("Error loading address book: %s\n", e.what());
        return false;
    }
}

bool AddressBook::save()
{
    if (file_path_.empty()) {
        file_path_ = getDefaultPath();
    }
    
    try {
        json j;
        j["entries"] = json::array();
        
        for (const auto& entry : entries_) {
            json e;
            e["label"] = entry.label;
            e["address"] = entry.address;
            e["notes"] = entry.notes;
            j["entries"].push_back(e);
        }
        
        // Atomic + durable: temp file + fsync + rename, so a crash mid-write can't
        // truncate addressbook.json (which is fully rewritten on every entry change).
        // Owner-only (0600) — it holds the user's saved contacts.
        if (!util::Platform::writeFileAtomically(file_path_, j.dump(2), /*restrictPermissions=*/true)) {
            DEBUG_LOGF("Could not write address book: %s\n", file_path_.c_str());
            return false;
        }
        DEBUG_LOGF("Address book saved: %zu entries\n", entries_.size());
        return true;

    } catch (const std::exception& e) {
        DEBUG_LOGF("Error saving address book: %s\n", e.what());
        return false;
    }
}

bool AddressBook::addEntry(const AddressBookEntry& entry)
{
    // Check for duplicate address
    if (findByAddress(entry.address) >= 0) {
        return false;
    }
    
    entries_.push_back(entry);
    return save();
}

bool AddressBook::updateEntry(size_t index, const AddressBookEntry& entry)
{
    if (index >= entries_.size()) {
        return false;
    }
    
    // Check for duplicate address (excluding current entry)
    int existing = findByAddress(entry.address);
    if (existing >= 0 && static_cast<size_t>(existing) != index) {
        return false;
    }
    
    entries_[index] = entry;
    return save();
}

bool AddressBook::removeEntry(size_t index)
{
    if (index >= entries_.size()) {
        return false;
    }
    
    entries_.erase(entries_.begin() + index);
    return save();
}

int AddressBook::findByAddress(const std::string& address) const
{
    for (size_t i = 0; i < entries_.size(); i++) {
        if (entries_[i].address == address) {
            return static_cast<int>(i);
        }
    }
    return -1;
}

} // namespace data
} // namespace dragonx
