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

#include "lite_client_bridge.h"
#include "lite_owned_string.h"

#ifndef DRAGONX_ENABLE_LITE_BACKEND
#define DRAGONX_ENABLE_LITE_BACKEND 0
#endif

#if DRAGONX_ENABLE_LITE_BACKEND
extern "C" {
bool litelib_wallet_exists(const char* chain_name);
char* litelib_initialize_new(bool dangerous, const char* server);
char* litelib_initialize_new_from_phrase(bool dangerous,
                                         const char* server,
                                         const char* seed,
                                         unsigned long long birthday,
                                         unsigned long long number,
                                         bool overwrite);
char* litelib_initialize_existing(bool dangerous, const char* server);
char* litelib_execute(const char* command, const char* args);
void litelib_rust_free_string(char* value);
bool litelib_check_server_online(const char* server);
void litelib_shutdown();
}
#endif

#include <utility>

namespace dragonx {
namespace wallet {

namespace {

bool hasRequiredApi(const LiteClientBridgeApi& api)
{
    return api.walletExists &&
           api.initializeNew &&
           api.initializeNewFromPhrase &&
           api.initializeExisting &&
           api.execute &&
           api.freeString &&
           api.checkServerOnline &&
           api.shutdown;
}

#if DRAGONX_ENABLE_LITE_BACKEND
LiteClientBridgeApi linkedSdxlApi()
{
    return LiteClientBridgeApi{
        &litelib_wallet_exists,
        &litelib_initialize_new,
        &litelib_initialize_new_from_phrase,
        &litelib_initialize_existing,
        &litelib_execute,
        &litelib_rust_free_string,
        &litelib_check_server_online,
        &litelib_shutdown
    };
}
#endif

} // namespace

LiteClientBridge::LiteClientBridge(LiteClientBridgeApi api, std::string unavailableReason)
    : api_(api), unavailableReason_(std::move(unavailableReason))
{
}

LiteClientBridge LiteClientBridge::unavailable(std::string reason)
{
    return LiteClientBridge({}, std::move(reason));
}

LiteClientBridge LiteClientBridge::fromApi(LiteClientBridgeApi api)
{
    if (!hasRequiredApi(api)) {
        return unavailable("lite client bridge API is incomplete");
    }
    return LiteClientBridge(api, {});
}

LiteClientBridge LiteClientBridge::linkedSdxl()
{
#if DRAGONX_ENABLE_LITE_BACKEND
    return fromApi(linkedSdxlApi());
#else
    return unavailable("lite backend is not linked");
#endif
}

LiteClientBridge::LiteClientBridge(LiteClientBridge&& other) noexcept
    : api_(other.api_),
      unavailableReason_(std::move(other.unavailableReason_)),
      shutdownCalled_(other.shutdownCalled_)
{
    other.api_ = {};
    other.shutdownCalled_ = true;
}

LiteClientBridge& LiteClientBridge::operator=(LiteClientBridge&& other) noexcept
{
    if (this == &other) return *this;
    shutdown();
    api_ = other.api_;
    unavailableReason_ = std::move(other.unavailableReason_);
    shutdownCalled_ = other.shutdownCalled_;
    other.api_ = {};
    other.shutdownCalled_ = true;
    return *this;
}

LiteClientBridge::~LiteClientBridge()
{
    shutdown();
}

bool LiteClientBridge::available() const
{
    return hasRequiredApi(api_);
}

bool LiteClientBridge::walletExists(const std::string& chainName) const
{
    if (!available()) return false;
    return api_.walletExists(chainName.c_str());
}

bool LiteClientBridge::checkServerOnline(const std::string& server) const
{
    if (!available()) return false;
    return api_.checkServerOnline(server.c_str());
}

LiteBridgeStringResult LiteClientBridge::initializeNew(bool dangerous, const std::string& server)
{
    if (!available()) return unavailableResult();
    return takeOwnedString(api_.initializeNew(dangerous, server.c_str()));
}

LiteBridgeStringResult LiteClientBridge::initializeNewFromPhrase(bool dangerous,
                                                                 const std::string& server,
                                                                 const std::string& seed,
                                                                 unsigned long long birthday,
                                                                 unsigned long long account,
                                                                 bool overwrite)
{
    if (!available()) return unavailableResult();
    return takeOwnedString(api_.initializeNewFromPhrase(
        dangerous, server.c_str(), seed.c_str(), birthday, account, overwrite));
}

LiteBridgeStringResult LiteClientBridge::initializeExisting(bool dangerous, const std::string& server)
{
    if (!available()) return unavailableResult();
    return takeOwnedString(api_.initializeExisting(dangerous, server.c_str()));
}

LiteBridgeStringResult LiteClientBridge::execute(const std::string& command, const std::string& args)
{
    if (!available()) return unavailableResult();
    if (command.empty()) return {false, {}, "lite command is empty"};
    return takeOwnedString(api_.execute(command.c_str(), args.c_str()));
}

void LiteClientBridge::shutdown()
{
    if (shutdownCalled_) return;
    shutdownCalled_ = true;
    if (available()) api_.shutdown();
}

LiteBridgeStringResult LiteClientBridge::unavailableResult() const
{
    return {false, {}, unavailableReason_.empty() ? "lite backend is unavailable" : unavailableReason_};
}

LiteBridgeStringResult LiteClientBridge::takeOwnedString(char* rawValue) const
{
    return liteBridgeRuntimeTakeOwnedString(rawValue, api_.freeString);
}

} // namespace wallet
} // namespace dragonx