#pragma once

#include "BarkNotificationSubscription.hpp"
#include "WalletStateChangeSubscription.hpp"
#include "HybridNitroArkSpec.hpp"
#include "generated/ark_cxx.h"
#include "generated/cxx.h"
#include <cmath>
#include <cstdint>
#include <limits>
#include <memory>
#include <mutex>
#include <optional>
#include <stdexcept>
#include <string>
#include <sys/wait.h>
#include <vector>

namespace margelo::nitro::nitroark {

using namespace margelo::nitro;

inline OffboardResult toOffboardResult(const bark_cxx::OffboardResult& result) {
  OffboardResult converted;
  converted.txid = std::string(result.txid);
  converted.movement_id = static_cast<double>(result.movement_id);
  return converted;
}

inline std::string lightningPaymentOriginMethodToString(LightningPaymentOriginMethod method) {
  switch (method) {
    case LightningPaymentOriginMethod::LIGHTNING_ADDRESS:
      return "lightning-address";
    case LightningPaymentOriginMethod::LNURL:
      return "lnurl";
    case LightningPaymentOriginMethod::CUSTOM:
      return "custom";
  }
  throw std::invalid_argument("Unsupported Lightning payment origin method");
}

// Helper functions to convert rust vtxos to C++ values
inline BarkVtxo convertRustVtxo(const bark_cxx::BarkVtxo& vtxo_rs) {
  BarkVtxo vtxo;
  vtxo.id = std::string(vtxo_rs.id.data(), vtxo_rs.id.length());
  vtxo.amount = static_cast<double>(vtxo_rs.amount);
  vtxo.expiry_height = static_cast<double>(vtxo_rs.expiry_height);
  vtxo.server_pubkey = std::string(vtxo_rs.server_pubkey.data(), vtxo_rs.server_pubkey.length());
  vtxo.exit_delta = static_cast<double>(vtxo_rs.exit_delta);
  vtxo.anchor_point = std::string(vtxo_rs.anchor_point.data(), vtxo_rs.anchor_point.length());
  vtxo.point = std::string(vtxo_rs.point.data(), vtxo_rs.point.length());
  vtxo.state = std::string(vtxo_rs.state.data(), vtxo_rs.state.length());
  return vtxo;
}

inline std::vector<BarkVtxo> convertRustVtxosToVector(const rust::Vec<bark_cxx::BarkVtxo>& rust_vtxos) {
  std::vector<BarkVtxo> vtxos;
  vtxos.reserve(rust_vtxos.size());

  for (const auto& vtxo_rs : rust_vtxos) {
    vtxos.push_back(convertRustVtxo(vtxo_rs));
  }

  return vtxos;
}

inline PendingRoundStatus convertRustPendingRoundStatus(const bark_cxx::PendingRoundStatus& status_rs) {
  PendingRoundStatus status;
  status.round_id = static_cast<double>(status_rs.round_id);
  status.status = std::string(status_rs.status.data(), status_rs.status.length());
  status.funding_txid = std::string(status_rs.funding_txid.data(), status_rs.funding_txid.length());
  status.unsigned_funding_txids.reserve(status_rs.unsigned_funding_txids.size());
  for (const auto& txid : status_rs.unsigned_funding_txids) {
    status.unsigned_funding_txids.emplace_back(std::string(txid.data(), txid.length()));
  }
  status.error = std::string(status_rs.error.data(), status_rs.error.length());
  status.is_final = status_rs.is_final;
  status.is_success = status_rs.is_success;
  return status;
}

inline std::optional<DelegatedRoundState>
convertRustDelegatedRoundState(const bark_cxx::DelegatedRoundState& state_rs) {
  if (!state_rs.has_round) {
    return std::nullopt;
  }
  DelegatedRoundState state;
  state.round_id = static_cast<double>(state_rs.round_id);
  return state;
}

inline LightningPaymentResult convertRustLightningPaymentResult(const bark_cxx::LightningPaymentResult& rust_result) {
  LightningPaymentResult result;
  result.state = std::string(rust_result.state.data(), rust_result.state.length());
  if (rust_result.invoice.length() == 0) {
    result.invoice = std::nullopt;
  } else {
    result.invoice = std::string(rust_result.invoice.data(), rust_result.invoice.length());
  }
  result.payment_hash = std::string(rust_result.payment_hash.data(), rust_result.payment_hash.length());
  result.amount = rust_result.has_amount ? std::make_optional(static_cast<double>(rust_result.amount)) : std::nullopt;
  result.htlc_vtxos = convertRustVtxosToVector(rust_result.htlc_vtxos);
  result.movement_id =
      rust_result.has_movement_id ? std::make_optional(static_cast<double>(rust_result.movement_id)) : std::nullopt;
  if (rust_result.preimage.length() == 0) {
    result.preimage = std::nullopt;
  } else {
    result.preimage = std::string(rust_result.preimage.data(), rust_result.preimage.length());
  }
  return result;
}

inline LightningReceive convertRustLightningReceive(const bark_cxx::LightningReceive& rust_result) {
  LightningReceive result;
  result.state = std::string(rust_result.state.data(), rust_result.state.length());
  if (!rust_result.phase.empty()) {
    result.phase = std::string(rust_result.phase.data(), rust_result.phase.length());
  }
  result.payment_hash = std::string(rust_result.payment_hash.data(), rust_result.payment_hash.length());
  result.payment_preimage =
      std::string(rust_result.payment_preimage.data(), rust_result.payment_preimage.length());
  result.invoice = std::string(rust_result.invoice.data(), rust_result.invoice.length());
  result.htlc_vtxo_ids.reserve(rust_result.htlc_vtxo_ids.size());
  for (const auto& id : rust_result.htlc_vtxo_ids) {
    result.htlc_vtxo_ids.emplace_back(std::string(id.data(), id.length()));
  }
  if (rust_result.has_movement_id) {
    result.movement_id = static_cast<double>(rust_result.movement_id);
  }
  if (rust_result.has_amount_sat) {
    result.amount_sat = static_cast<double>(rust_result.amount_sat);
  }
  if (rust_result.has_settled_at) {
    result.settled_at = static_cast<double>(rust_result.settled_at);
  }
  return result;
}

inline ExitBlockRefResult convertRustExitBlockRef(const bark_cxx::ExitBlockRefResult& block_rs) {
  ExitBlockRefResult block;
  block.height = static_cast<double>(block_rs.height);
  block.hash = std::string(block_rs.hash.data(), block_rs.hash.length());
  return block;
}

inline ExitTxOriginResult convertRustExitTxOrigin(const bark_cxx::ExitTxOriginResult& origin_rs) {
  ExitTxOriginResult origin;
  origin.kind = std::string(origin_rs.kind.data(), origin_rs.kind.length());
  if (origin_rs.has_confirmed_in) {
    origin.confirmed_in = convertRustExitBlockRef(origin_rs.confirmed_in);
  }
  return origin;
}

inline ExitTxStatusResult convertRustExitTxStatus(const bark_cxx::ExitTxStatusResult& status_rs) {
  ExitTxStatusResult status;
  status.kind = std::string(status_rs.kind.data(), status_rs.kind.length());

  if (!status_rs.txids.empty()) {
    status.txids = std::vector<std::string>();
    status.txids->reserve(status_rs.txids.size());
    for (const auto& txid : status_rs.txids) {
      status.txids->emplace_back(std::string(txid.data(), txid.length()));
    }
  }
  if (status_rs.child_txid.length() != 0) {
    status.child_txid = std::string(status_rs.child_txid.data(), status_rs.child_txid.length());
  }
  if (status_rs.has_origin) {
    status.origin = convertRustExitTxOrigin(status_rs.origin);
  }
  if (status_rs.has_block) {
    status.block = convertRustExitBlockRef(status_rs.block);
  }

  return status;
}

inline ExitTxResult convertRustExitTx(const bark_cxx::ExitTxResult& tx_rs) {
  ExitTxResult tx;
  tx.txid = std::string(tx_rs.txid.data(), tx_rs.txid.length());
  tx.status = convertRustExitTxStatus(tx_rs.status);
  return tx;
}

inline ExitStateDetailsResult convertRustExitStateDetails(const bark_cxx::ExitStateDetailsResult& state_rs) {
  ExitStateDetailsResult state;
  state.kind = std::string(state_rs.kind.data(), state_rs.kind.length());
  state.tip_height = static_cast<double>(state_rs.tip_height);

  if (state.kind == "vtxo-swept") {
    state.spent_inputs = std::vector<std::string>();
    state.spent_inputs->reserve(state_rs.spent_inputs.size());
    for (const auto& input : state_rs.spent_inputs) {
      state.spent_inputs->emplace_back(input.data(), input.length());
    }
  }
  if (!state_rs.transactions.empty()) {
    state.transactions = std::vector<ExitTxResult>();
    state.transactions->reserve(state_rs.transactions.size());
    for (const auto& tx_rs : state_rs.transactions) {
      state.transactions->push_back(convertRustExitTx(tx_rs));
    }
  }
  if (state_rs.has_confirmed_block) {
    state.confirmed_block = convertRustExitBlockRef(state_rs.confirmed_block);
  }
  if (state_rs.claimable_height != 0) {
    state.claimable_height = static_cast<double>(state_rs.claimable_height);
  }
  if (state_rs.has_claimable_since) {
    state.claimable_since = convertRustExitBlockRef(state_rs.claimable_since);
  }
  if (state_rs.has_last_scanned_block) {
    state.last_scanned_block = convertRustExitBlockRef(state_rs.last_scanned_block);
  }
  if (state_rs.claim_txid.length() != 0) {
    state.claim_txid = std::string(state_rs.claim_txid.data(), state_rs.claim_txid.length());
  }
  if (state_rs.txid.length() != 0) {
    state.txid = std::string(state_rs.txid.data(), state_rs.txid.length());
  }
  if (state_rs.has_block) {
    state.block = convertRustExitBlockRef(state_rs.block);
  }

  return state;
}

inline std::vector<ExitVtxoResult>
convertRustExitVtxosToVector(const rust::Vec<bark_cxx::ExitVtxoResult>& rust_results) {
  std::vector<ExitVtxoResult> results;
  results.reserve(rust_results.size());

  for (const auto& rust_result : rust_results) {
    ExitVtxoResult result;
    result.vtxo_id = std::string(rust_result.vtxo_id.data(), rust_result.vtxo_id.length());
    result.amount_sat = static_cast<double>(rust_result.amount_sat);
    result.state = std::string(rust_result.state.data(), rust_result.state.length());
    result.state_details = convertRustExitStateDetails(rust_result.state_details);

    result.history.reserve(rust_result.history.size());
    for (const auto& state : rust_result.history) {
      result.history.emplace_back(std::string(state.data(), state.length()));
    }

    result.history_details.reserve(rust_result.history_details.size());
    for (const auto& stateDetails : rust_result.history_details) {
      result.history_details.push_back(convertRustExitStateDetails(stateDetails));
    }

    result.txids.reserve(rust_result.txids.size());
    for (const auto& txid : rust_result.txids) {
      result.txids.emplace_back(std::string(txid.data(), txid.length()));
    }

    result.is_claimable = rust_result.is_claimable;
    result.is_initialized = rust_result.is_initialized;
    results.push_back(std::move(result));
  }

  return results;
}

inline BarkMovementDestination convertRustMovementDestination(const bark_cxx::BarkMovementDestination& destination_rs) {
  BarkMovementDestination destination;
  destination.destination = std::string(destination_rs.destination.data(), destination_rs.destination.length());
  destination.payment_method =
      std::string(destination_rs.payment_method.data(), destination_rs.payment_method.length());
  destination.amount_sat = static_cast<double>(destination_rs.amount_sat);
  return destination;
}

inline BarkMovement convertRustMovement(const bark_cxx::BarkMovement& movement_rs) {
  BarkMovement movement;
  movement.id = static_cast<double>(movement_rs.id);
  movement.status = std::string(movement_rs.status.data(), movement_rs.status.length());
  movement.metadata_json = std::string(movement_rs.metadata_json.data(), movement_rs.metadata_json.length());
  movement.intended_balance_sat = static_cast<double>(movement_rs.intended_balance_sat);
  movement.effective_balance_sat = static_cast<double>(movement_rs.effective_balance_sat);
  movement.offchain_fee_sat = static_cast<double>(movement_rs.offchain_fee_sat);
  movement.created_at = std::string(movement_rs.created_at.data(), movement_rs.created_at.length());
  movement.updated_at = std::string(movement_rs.updated_at.data(), movement_rs.updated_at.length());
  if (movement_rs.completed_at.length() == 0) {
    movement.completed_at = std::nullopt;
  } else {
    movement.completed_at = std::string(movement_rs.completed_at.data(), movement_rs.completed_at.length());
  }

  movement.subsystem.name = std::string(movement_rs.subsystem_name.data(), movement_rs.subsystem_name.length());
  movement.subsystem.kind = std::string(movement_rs.subsystem_kind.data(), movement_rs.subsystem_kind.length());

  movement.sent_to.reserve(movement_rs.sent_to.size());
  for (const auto& destination_rs : movement_rs.sent_to) {
    movement.sent_to.push_back(convertRustMovementDestination(destination_rs));
  }

  movement.received_on.reserve(movement_rs.received_on.size());
  for (const auto& destination_rs : movement_rs.received_on) {
    movement.received_on.push_back(convertRustMovementDestination(destination_rs));
  }

  movement.input_vtxos.reserve(movement_rs.input_vtxos.size());
  for (const auto& vtxo_id : movement_rs.input_vtxos) {
    movement.input_vtxos.emplace_back(std::string(vtxo_id.data(), vtxo_id.length()));
  }

  movement.output_vtxos.reserve(movement_rs.output_vtxos.size());
  for (const auto& vtxo_id : movement_rs.output_vtxos) {
    movement.output_vtxos.emplace_back(std::string(vtxo_id.data(), vtxo_id.length()));
  }

  movement.exited_vtxos.reserve(movement_rs.exited_vtxos.size());
  for (const auto& vtxo_id : movement_rs.exited_vtxos) {
    movement.exited_vtxos.emplace_back(std::string(vtxo_id.data(), vtxo_id.length()));
  }

  return movement;
}

class NitroArk : public HybridNitroArkSpec {

private:
  void trackSubscription(const std::shared_ptr<HybridBarkNotificationSubscriptionSpec>& subscription) {
    std::lock_guard<std::mutex> lock(subscriptions_mutex_);
    subscriptions_.push_back(subscription);
  }

  void trackStateChangeSubscription(
      const std::shared_ptr<HybridWalletStateChangeSubscriptionSpec>& subscription) {
    std::lock_guard<std::mutex> lock(state_change_subscriptions_mutex_);
    state_change_subscriptions_.push_back(subscription);
  }

  void stopAllSubscriptions() {
    std::vector<std::shared_ptr<HybridBarkNotificationSubscriptionSpec>> active_subscriptions;
    {
      std::lock_guard<std::mutex> lock(subscriptions_mutex_);
      auto it = subscriptions_.begin();
      while (it != subscriptions_.end()) {
        if (auto subscription = it->lock()) {
          active_subscriptions.push_back(std::move(subscription));
          ++it;
        } else {
          it = subscriptions_.erase(it);
        }
      }
    }

    for (const auto& subscription : active_subscriptions) {
      try {
        subscription->stop();
      } catch (...) {
      }
    }

    std::vector<std::shared_ptr<HybridWalletStateChangeSubscriptionSpec>> active_state_change_subscriptions;
    {
      std::lock_guard<std::mutex> lock(state_change_subscriptions_mutex_);
      auto it = state_change_subscriptions_.begin();
      while (it != state_change_subscriptions_.end()) {
        if (auto subscription = it->lock()) {
          active_state_change_subscriptions.push_back(std::move(subscription));
          ++it;
        } else {
          it = state_change_subscriptions_.erase(it);
        }
      }
    }
    for (const auto& subscription : active_state_change_subscriptions) {
      try {
        subscription->stop();
      } catch (...) {
      }
    }
  }

  static WalletSnapshotInfo convertWalletSnapshotInfo(const bark_cxx::WalletSnapshotInfo& info) {
    WalletSnapshotInfo result;
    result.path = std::string(info.path.data(), info.path.length());
    result.sizeBytes = static_cast<double>(info.size_bytes);
    result.sha256 = std::string(info.sha256.data(), info.sha256.length());
    result.network = std::string(info.network.data(), info.network.length());
    result.walletFingerprint =
        std::string(info.wallet_fingerprint.data(), info.wallet_fingerprint.length());
    if (!info.server_pubkey.empty()) {
      result.serverPubkey = std::string(info.server_pubkey.data(), info.server_pubkey.length());
    }
    if (!info.mailbox_pubkey.empty()) {
      result.mailboxPubkey = std::string(info.mailbox_pubkey.data(), info.mailbox_pubkey.length());
    }
    result.schemaVersion = static_cast<double>(info.schema_version);
    return result;
  }

  // Helper function to create ConfigOpts from BarkConfigOpts
  static bark_cxx::ConfigOpts createConfigOpts(const std::optional<BarkConfigOpts>& config) {
    bark_cxx::ConfigOpts config_opts;
    config_opts.vtxo_key_gap_limit = 250;
    if (config.has_value()) {
      config_opts.ark = config->ark;
      config_opts.user_agent = config->user_agent.value_or("");
      config_opts.esplora = config->esplora.value_or("");
      config_opts.bitcoind = config->bitcoind.value_or("");
      config_opts.bitcoind_cookie = config->bitcoind_cookie.value_or("");
      config_opts.bitcoind_user = config->bitcoind_user.value_or("");
      config_opts.bitcoind_pass = config->bitcoind_pass.value_or("");
      config_opts.vtxo_refresh_expiry_threshold =
          static_cast<uint32_t>(config->vtxo_refresh_expiry_threshold);
      const double gap_limit = config->vtxo_key_gap_limit.value_or(250);
      if (!std::isfinite(gap_limit) || std::floor(gap_limit) != gap_limit ||
          gap_limit < 0 || gap_limit > 100000) {
        throw std::invalid_argument("vtxo_key_gap_limit must be an integer between 0 and 100000");
      }
      config_opts.vtxo_key_gap_limit = static_cast<uint32_t>(gap_limit);
      config_opts.fallback_fee_rate = static_cast<uint64_t>(config->fallback_fee_rate);
      config_opts.htlc_recv_claim_delta = static_cast<uint32_t>(config->htlc_recv_claim_delta);
      config_opts.vtxo_exit_margin = static_cast<uint32_t>(config->vtxo_exit_margin);
      config_opts.round_tx_required_confirmations = static_cast<uint32_t>(config->round_tx_required_confirmations);
    }
    return config_opts;
  }

public:
  NitroArk() : HybridObject(TAG) {
    // Initialize the Rust logger once when a NitroArk object is created.
    bark_cxx::init_logger();
  }

  ~NitroArk() override {
    stopAllSubscriptions();
  }

  // --- Management ---

  std::string getBarkVersion() override {
    rust::String version = bark_cxx::bark_version();
    return std::string(version.data(), version.length());
  }

  std::shared_ptr<Promise<std::string>> createMnemonic() override {
    return Promise<std::string>::async([]() {
      try {
        rust::String mnemonic_rs = bark_cxx::create_mnemonic();
        return std::string(mnemonic_rs.data(), mnemonic_rs.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> createWallet(const std::string& datadir, const BarkCreateOpts& opts) override {
    return Promise<void>::async([datadir, opts]() {
      try {
        bark_cxx::CreateOpts create_opts;
        create_opts.regtest = opts.regtest.value_or(false);
        create_opts.signet = opts.signet.value_or(false);
        create_opts.bitcoin = opts.bitcoin.value_or(true);
        create_opts.mnemonic = opts.mnemonic;

        uint32_t birthday_height_val;
        if (opts.birthday_height.has_value()) {
          birthday_height_val = static_cast<uint32_t>(opts.birthday_height.value());
          create_opts.birthday_height = &birthday_height_val;
        } else {
          create_opts.birthday_height = nullptr;
        }

        create_opts.config = createConfigOpts(opts.config);

        bark_cxx::create_wallet(datadir, create_opts);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> loadWallet(const std::string& datadir, const BarkCreateOpts& opts) override {
    return Promise<void>::async([datadir, opts]() {
      try {
        bark_cxx::CreateOpts create_opts;
        create_opts.regtest = opts.regtest.value_or(false);
        create_opts.signet = opts.signet.value_or(false);
        create_opts.bitcoin = opts.bitcoin.value_or(true);
        create_opts.mnemonic = opts.mnemonic;

        uint32_t birthday_height_val;
        if (opts.birthday_height.has_value()) {
          birthday_height_val = static_cast<uint32_t>(opts.birthday_height.value());
          create_opts.birthday_height = &birthday_height_val;
        } else {
          create_opts.birthday_height = nullptr;
        }

        create_opts.config = createConfigOpts(opts.config);

        bark_cxx::load_wallet(datadir, create_opts);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<RestoreWalletResult>> restoreWalletFromArkServer(const std::string& datadir, const BarkCreateOpts& opts) override {
    return Promise<RestoreWalletResult>::async([datadir, opts]() {
      try {
        bark_cxx::CreateOpts create_opts;
        create_opts.regtest = opts.regtest.value_or(false);
        create_opts.signet = opts.signet.value_or(false);
        create_opts.bitcoin = opts.bitcoin.value_or(true);
        create_opts.mnemonic = opts.mnemonic;
        uint32_t birthday_height_val;
        if (opts.birthday_height.has_value()) {
          birthday_height_val = static_cast<uint32_t>(opts.birthday_height.value());
          create_opts.birthday_height = &birthday_height_val;
        } else {
          create_opts.birthday_height = nullptr;
        }
        create_opts.config = createConfigOpts(opts.config);
        auto restored = bark_cxx::restore_wallet_from_ark_server(datadir, create_opts);
        RestoreWalletResult result;
        result.status = std::string(restored.status);
        if (restored.has_report) {
          auto group = [](const bark_cxx::RecoveryGroup& entry) {
            RecoveryGroup converted;
            for (const auto& id : entry.vtxo_ids) converted.vtxo_ids.emplace_back(std::string(id));
            converted.known_amount_sat = static_cast<double>(entry.known_amount_sat);
            return converted;
          };
          RecoveryReport report;
          report.is_complete = restored.report.is_complete;
          report.recovered = group(restored.report.recovered);
          report.skipped = group(restored.report.skipped);
          report.exited = group(restored.report.exited);
          report.failed = group(restored.report.failed);
          report.foreign = group(restored.report.foreign);
          report.not_found = group(restored.report.not_found);
          result.report = std::move(report);
        }
        if (!restored.error.empty()) result.error = std::string(restored.error);
        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> closeWallet() override {
    stopAllSubscriptions();

    return Promise<void>::async([]() {
      try {
        bark_cxx::close_wallet();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<WalletSnapshotInfo>>
  createWalletSnapshot(const std::string& destinationPath) override {
    return Promise<WalletSnapshotInfo>::async([destinationPath]() {
      try {
        return convertWalletSnapshotInfo(bark_cxx::create_wallet_snapshot(destinationPath));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<WalletSnapshotInfo>>
  validateWalletSnapshot(const std::string& path,
                         const std::optional<WalletSnapshotExpectation>& expected) override {
    return Promise<WalletSnapshotInfo>::async([path, expected]() {
      try {
        bark_cxx::WalletSnapshotExpectation expectedRs;
        expectedRs.has_network = expected.has_value() && expected->network.has_value();
        expectedRs.network = expectedRs.has_network ? *expected->network : "";
        expectedRs.has_wallet_fingerprint =
            expected.has_value() && expected->walletFingerprint.has_value();
        expectedRs.wallet_fingerprint =
            expectedRs.has_wallet_fingerprint ? *expected->walletFingerprint : "";
        expectedRs.has_server_pubkey = expected.has_value() && expected->serverPubkey.has_value();
        expectedRs.server_pubkey = expectedRs.has_server_pubkey ? *expected->serverPubkey : "";
        return convertWalletSnapshotInfo(bark_cxx::validate_wallet_snapshot(path, expectedRs));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<HybridWalletStateChangeSubscriptionSpec>
  subscribeWalletStateChanges(const std::function<void(const WalletStateChangeEvent&)>& onEvent) override {
    try {
      auto subscription = std::make_shared<WalletStateChangeSubscription>(
          bark_cxx::subscribe_wallet_state_changes(),
          std::function<void(const WalletStateChangeEvent&)>(onEvent));
      trackStateChangeSubscription(subscription);
      return subscription;
    } catch (const rust::Error& e) {
      throw std::runtime_error(e.what());
    }
  }

  std::shared_ptr<Promise<void>> refreshServer() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::refresh_server();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<bool>> isWalletLoaded() override {
    return Promise<bool>::async([]() { return bark_cxx::is_wallet_loaded(); });
  }

  std::shared_ptr<Promise<void>> syncPendingBoards() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::sync_pending_boards();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> maintenance() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::maintenance();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> maintenanceDelegated() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::maintenance_delegated();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> maintenanceRefresh() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::maintenance_refresh();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> sync() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::sync();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> startExitForEntireWallet() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::start_exit_for_entire_wallet();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> startExitForVtxos(const std::vector<std::string>& vtxoIds) override {
    return Promise<void>::async([vtxoIds]() {
      try {
        rust::Vec<rust::String> rust_vtxo_ids;
        rust_vtxo_ids.reserve(vtxoIds.size());
        for (const auto& vtxoId : vtxoIds) {
          rust_vtxo_ids.push_back(vtxoId);
        }
        bark_cxx::start_exit_for_vtxos(std::move(rust_vtxo_ids));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> cancelExit(const std::string& vtxoId) override {
    return Promise<void>::async([vtxoId]() {
      try {
        bark_cxx::cancel_exit(vtxoId);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> syncExit() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::sync_exit();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::vector<ExitProgressStatusResult>>>
  progressExits(std::optional<double> feeRateSatPerKvb) override {
    return Promise<std::vector<ExitProgressStatusResult>>::async([feeRateSatPerKvb]() {
      try {
        uint64_t feeRateVal;
        rust::Vec<bark_cxx::ExitProgressStatusResult> rust_results;
        if (feeRateSatPerKvb.has_value()) {
          feeRateVal = static_cast<uint64_t>(feeRateSatPerKvb.value());
          rust_results = bark_cxx::progress_exits(&feeRateVal);
        } else {
          rust_results = bark_cxx::progress_exits(nullptr);
        }

        std::vector<ExitProgressStatusResult> results;
        results.reserve(rust_results.size());
        for (const auto& rust_result : rust_results) {
          ExitProgressStatusResult result;
          result.vtxo_id = std::string(rust_result.vtxo_id.data(), rust_result.vtxo_id.length());
          result.state = std::string(rust_result.state.data(), rust_result.state.length());
          result.state_details = convertRustExitStateDetails(rust_result.state_details);
          if (rust_result.error.length() == 0) {
            result.error = std::nullopt;
          } else {
            result.error = std::string(rust_result.error.data(), rust_result.error.length());
          }
          results.push_back(std::move(result));
        }
        return results;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<ExitFeeEstimate>>
  estimateEmergencyExitFee(const std::vector<std::string>& vtxoIds,
                           std::optional<double> feeRateSatPerKvb,
                           const std::optional<std::string>& destinationAddress,
                           std::optional<double> feeMargin) override {
    return Promise<ExitFeeEstimate>::async([vtxoIds, feeRateSatPerKvb, destinationAddress, feeMargin]() {
      try {
        rust::Vec<rust::String> rust_vtxo_ids;
        rust_vtxo_ids.reserve(vtxoIds.size());
        for (const auto& vtxoId : vtxoIds) {
          rust_vtxo_ids.push_back(vtxoId);
        }

        uint64_t feeRateVal;
        const uint64_t* feeRatePtr = nullptr;
        if (feeRateSatPerKvb.has_value()) {
          const double rate = feeRateSatPerKvb.value();
          if (!std::isfinite(rate) || std::trunc(rate) != rate || rate < 0 || rate > 9007199254740991.0) {
            throw std::invalid_argument("feeRateSatPerKvb must be a nonnegative safe integer");
          }
          feeRateVal = static_cast<uint64_t>(feeRateSatPerKvb.value());
          feeRatePtr = &feeRateVal;
        }

        rust::String destinationAddressVal;
        const rust::String* destinationAddressPtr = nullptr;
        if (destinationAddress.has_value()) {
          destinationAddressVal = rust::String(destinationAddress.value());
          destinationAddressPtr = &destinationAddressVal;
        }

        bark_cxx::ExitFeeEstimate rust_result = bark_cxx::estimate_emergency_exit_fee(
            std::move(rust_vtxo_ids), feeRatePtr, destinationAddressPtr, feeMargin ? &feeMargin.value() : nullptr);

        ExitFeeEstimate result;
        result.exit_broadcast_fee_sat = static_cast<double>(rust_result.exit_broadcast_fee_sat);
        result.claim_fee_sat = static_cast<double>(rust_result.claim_fee_sat);
        result.total_fee_sat = static_cast<double>(rust_result.total_fee_sat);
        result.fee_rate_sat_per_vb = static_cast<double>(rust_result.fee_rate_sat_per_vb);
        result.txs_to_broadcast = static_cast<double>(rust_result.txs_to_broadcast);
        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::vector<ExitVtxoResult>>> getExitVtxos() override {
    return Promise<std::vector<ExitVtxoResult>>::async([]() {
      try {
        rust::Vec<bark_cxx::ExitVtxoResult> rust_results = bark_cxx::get_exit_vtxos();
        return convertRustExitVtxosToVector(rust_results);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::vector<ExitVtxoResult>>> listClaimable() override {
    return Promise<std::vector<ExitVtxoResult>>::async([]() {
      try {
        rust::Vec<bark_cxx::ExitVtxoResult> rust_results = bark_cxx::list_claimable();
        return convertRustExitVtxosToVector(rust_results);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::optional<ExitStatusResult>>>
  getExitStatus(const std::string& vtxoId, std::optional<bool> includeHistory,
                std::optional<bool> includeTransactions) override {
    return Promise<std::optional<ExitStatusResult>>::async([vtxoId, includeHistory, includeTransactions]() {
      try {
        const bark_cxx::ExitStatusResult* status_ptr =
            bark_cxx::get_exit_status(vtxoId, includeHistory.value_or(false), includeTransactions.value_or(false));
        if (status_ptr == nullptr) {
          return std::optional<ExitStatusResult>();
        }

        std::unique_ptr<const bark_cxx::ExitStatusResult> rust_status(status_ptr);
        ExitStatusResult result;
        result.vtxo_id = std::string(rust_status->vtxo_id.data(), rust_status->vtxo_id.length());
        result.state = std::string(rust_status->state.data(), rust_status->state.length());
        result.state_details = convertRustExitStateDetails(rust_status->state_details);

        result.history.reserve(rust_status->history.size());
        for (const auto& state : rust_status->history) {
          result.history.emplace_back(std::string(state.data(), state.length()));
        }

        result.history_details.reserve(rust_status->history_details.size());
        for (const auto& stateDetails : rust_status->history_details) {
          result.history_details.push_back(convertRustExitStateDetails(stateDetails));
        }

        result.transactions.reserve(rust_status->transactions.size());
        for (const auto& rust_tx : rust_status->transactions) {
          ExitTransactionPackageResult tx;
          tx.exit_txid = std::string(rust_tx.exit_txid.data(), rust_tx.exit_txid.length());
          tx.exit_tx_hex = std::string(rust_tx.exit_tx_hex.data(), rust_tx.exit_tx_hex.length());
          tx.child_txid = std::string(rust_tx.child_txid.data(), rust_tx.child_txid.length());
          tx.child_tx_hex = std::string(rust_tx.child_tx_hex.data(), rust_tx.child_tx_hex.length());
          tx.child_origin = std::string(rust_tx.child_origin.data(), rust_tx.child_origin.length());
          tx.has_child = rust_tx.has_child;
          result.transactions.push_back(std::move(tx));
        }

        return std::optional<ExitStatusResult>(result);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<bool>> hasPendingExits() override {
    return Promise<bool>::async([]() {
      try {
        return bark_cxx::has_pending_exits();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<double>> pendingExitTotal() override {
    return Promise<double>::async([]() {
      try {
        return static_cast<double>(bark_cxx::pending_exit_total());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::optional<double>>> allClaimableAtHeight() override {
    return Promise<std::optional<double>>::async([]() {
      try {
        const uint32_t* result_ptr = bark_cxx::all_claimable_at_height();
        if (result_ptr == nullptr) {
          return std::optional<double>(std::nullopt);
        }
        double value = static_cast<double>(*result_ptr);
        delete result_ptr;
        return std::optional<double>(value);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> drainExits(const std::vector<std::string>& vtxoIds,
                                                   const std::string& destinationAddress,
                                                   std::optional<double> feeRateSatPerKvb) override {
    return Promise<std::string>::async([vtxoIds, destinationAddress, feeRateSatPerKvb]() {
      try {
        rust::Vec<rust::String> rust_vtxo_ids;
        rust_vtxo_ids.reserve(vtxoIds.size());
        for (const auto& vtxoId : vtxoIds) {
          rust_vtxo_ids.push_back(vtxoId);
        }

        uint64_t feeRateVal;
        rust::String result;
        if (feeRateSatPerKvb.has_value()) {
          feeRateVal = static_cast<uint64_t>(feeRateSatPerKvb.value());
          result = bark_cxx::drain_exits(std::move(rust_vtxo_ids), destinationAddress, &feeRateVal);
        } else {
          result = bark_cxx::drain_exits(std::move(rust_vtxo_ids), destinationAddress, nullptr);
        }

        return std::string(result.data(), result.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> extractTransaction(const std::string& psbt) override {
    return Promise<std::string>::async([psbt]() {
      try {
        rust::String result = bark_cxx::extract_transaction(psbt);
        return std::string(result.data(), result.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> broadcastTransaction(const std::string& txHex) override {
    return Promise<std::string>::async([txHex]() {
      try {
        rust::String result = bark_cxx::broadcast_transaction(txHex);
        return std::string(result.data(), result.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::vector<PendingRoundStatus>>> syncPendingRounds() override {
    return Promise<std::vector<PendingRoundStatus>>::async([]() {
      try {
        rust::Vec<bark_cxx::PendingRoundStatus> statuses_rs = bark_cxx::sync_pending_rounds();
        std::vector<PendingRoundStatus> statuses;
        statuses.reserve(statuses_rs.size());
        for (const auto& status_rs : statuses_rs) {
          statuses.push_back(convertRustPendingRoundStatus(status_rs));
        }
        return statuses;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  // --- Wallet Info ---

  std::shared_ptr<Promise<BarkArkInfo>> getArkInfo() override {
    return Promise<BarkArkInfo>::async([]() {
      try {
        bark_cxx::CxxArkInfo rust_info = bark_cxx::get_ark_info();
        BarkArkInfo info;
        info.network = std::string(rust_info.network.data(), rust_info.network.length());
        info.server_pubkey = std::string(rust_info.server_pubkey.data(), rust_info.server_pubkey.length());
        info.mailbox_pubkey = std::string(rust_info.mailbox_pubkey.data(), rust_info.mailbox_pubkey.length());
        info.round_interval = static_cast<double>(rust_info.round_interval);
        info.nb_round_nonces = static_cast<double>(rust_info.nb_round_nonces);
        info.vtxo_exit_delta = static_cast<double>(rust_info.vtxo_exit_delta);
        info.vtxo_lifetime = static_cast<double>(rust_info.vtxo_lifetime);
        info.htlc_send_expiry_delta = static_cast<double>(rust_info.htlc_send_expiry_delta);
        info.max_vtxo_amount = static_cast<double>(rust_info.max_vtxo_amount);
        info.required_board_confirmations = static_cast<double>(rust_info.required_board_confirmations);
        info.min_board_amount = static_cast<double>(rust_info.min_board_amount);
        info.ln_receive_anti_dos_required = rust_info.ln_receive_anti_dos_required;
        return info;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<WalletDebugInfo>> debugInfo() override {
    return Promise<WalletDebugInfo>::async([]() {
      try {
        bark_cxx::WalletDebugInfo rust_info = bark_cxx::debug_info();
        WalletDebugInfo info;
        info.network = std::string(rust_info.network.data(), rust_info.network.length());
        info.mailbox_id = std::string(rust_info.mailbox_id.data(), rust_info.mailbox_id.length());
        info.vtxo_xpub = std::string(rust_info.vtxo_xpub.data(), rust_info.vtxo_xpub.length());
        return info;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<OffchainBalanceResult>> offchainBalance() override {
    return Promise<OffchainBalanceResult>::async([]() {
      try {
        bark_cxx::OffchainBalance rust_balance = bark_cxx::offchain_balance();
        OffchainBalanceResult balance;
        balance.spendable = static_cast<double>(rust_balance.spendable);
        balance.needs_refresh = static_cast<double>(rust_balance.needs_refresh);
        balance.pending = static_cast<double>(rust_balance.pending);
        balance.total = static_cast<double>(rust_balance.total);
        balance.pending_arkoor_send = static_cast<double>(rust_balance.pending_arkoor_send);
        balance.pending_lightning_send = static_cast<double>(rust_balance.pending_lightning_send);
        balance.claimable_lightning_receive = static_cast<double>(rust_balance.claimable_lightning_receive);
        balance.pending_in_round = static_cast<double>(rust_balance.pending_in_round);
        balance.pending_exit = static_cast<double>(rust_balance.pending_exit);
        balance.pending_board = static_cast<double>(rust_balance.pending_board);
        balance.pending_offboard = static_cast<double>(rust_balance.pending_offboard);

        return balance;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<KeyPairResult>> deriveStoreNextKeypair() override {
    return Promise<KeyPairResult>::async([]() {
      try {
        bark_cxx::KeyPairResult keypair_rs = bark_cxx::derive_store_next_keypair();
        KeyPairResult keypair;
        keypair.public_key = std::string(keypair_rs.public_key.data(), keypair_rs.public_key.length());
        keypair.secret_key = std::string(keypair_rs.secret_key.data(), keypair_rs.secret_key.length());

        return keypair;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<KeyPairResult>> peekKeyPair(double index) override {
    return Promise<KeyPairResult>::async([index]() {
      try {
        uint32_t index_val = static_cast<uint32_t>(index);
        bark_cxx::KeyPairResult keypair_rs = bark_cxx::peek_keypair(index_val);
        KeyPairResult keypair;
        keypair.public_key = std::string(keypair_rs.public_key.data(), keypair_rs.public_key.length());
        keypair.secret_key = std::string(keypair_rs.secret_key.data(), keypair_rs.secret_key.length());
        return keypair;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<NewAddressResult>> newAddress() override {
    return Promise<NewAddressResult>::async([]() {
      try {
        bark_cxx::NewAddressResult address_rs = bark_cxx::new_address();
        NewAddressResult address;
        address.user_pubkey = std::string(address_rs.user_pubkey.data(), address_rs.user_pubkey.length());
        address.ark_id = std::string(address_rs.ark_id.data(), address_rs.ark_id.length());
        address.address = std::string(address_rs.address.data(), address_rs.address.length());
        return address;

      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<NewAddressResult>> peekAddress(double index) override {
    return Promise<NewAddressResult>::async([index]() {
      try {
        bark_cxx::NewAddressResult address_rs = bark_cxx::peek_address(static_cast<uint32_t>(index));
        NewAddressResult address;
        address.user_pubkey = std::string(address_rs.user_pubkey.data(), address_rs.user_pubkey.length());
        address.ark_id = std::string(address_rs.ark_id.data(), address_rs.ark_id.length());
        address.address = std::string(address_rs.address.data(), address_rs.address.length());
        return address;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> signMessage(const std::string& message, double index) override {
    return Promise<std::string>::async([message, index]() {
      try {
        uint32_t index_val = static_cast<uint32_t>(index);
        rust::String signature_rs = bark_cxx::sign_message(message, index_val);
        return std::string(signature_rs.data(), signature_rs.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> signMesssageWithMnemonic(const std::string& message,
                                                                 const std::string& mnemonic,
                                                                 const std::string& network, double index) override {
    return Promise<std::string>::async([message, mnemonic, network, index]() {
      try {
        uint32_t index_val = static_cast<uint32_t>(index);
        rust::String signature_rs = bark_cxx::sign_messsage_with_mnemonic(message, mnemonic, network, index_val);
        return std::string(signature_rs.data(), signature_rs.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<KeyPairResult>> deriveKeypairFromMnemonic(const std::string& mnemonic,
                                                                    const std::string& network, double index) override {
    return Promise<KeyPairResult>::async([mnemonic, network, index]() {
      try {
        uint32_t index_val = static_cast<uint32_t>(index);
        bark_cxx::KeyPairResult keypair_rs = bark_cxx::derive_keypair_from_mnemonic(mnemonic, network, index_val);
        KeyPairResult keypair;
        keypair.public_key = std::string(keypair_rs.public_key.data(), keypair_rs.public_key.length());
        keypair.secret_key = std::string(keypair_rs.secret_key.data(), keypair_rs.secret_key.length());
        return keypair;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<bool>> verifyMessage(const std::string& message, const std::string& signature,
                                               const std::string& publicKey) override {
    return Promise<bool>::async([message, signature, publicKey]() {
      try {
        return bark_cxx::verify_message(message, signature, publicKey);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<KeyPairResult>> mailboxKeypair() override {
    return Promise<KeyPairResult>::async([]() {
      try {
        bark_cxx::KeyPairResult keypair_rs = bark_cxx::mailbox_keypair();
        KeyPairResult keypair;
        keypair.public_key = std::string(keypair_rs.public_key.data(), keypair_rs.public_key.length());
        keypair.secret_key = std::string(keypair_rs.secret_key.data(), keypair_rs.secret_key.length());
        return keypair;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<MailboxAuthorizationResult>> mailboxAuthorization(double authorizationExpiry) override {
    return Promise<MailboxAuthorizationResult>::async([authorizationExpiry]() {
      try {
        int64_t expiry_val = static_cast<int64_t>(authorizationExpiry);
        bark_cxx::MailboxAuthorizationResult auth_rs = bark_cxx::mailbox_authorization(expiry_val);
        MailboxAuthorizationResult result;
        result.mailbox_id = std::string(auth_rs.mailbox_id.data(), auth_rs.mailbox_id.length());
        result.expiry = static_cast<double>(auth_rs.expiry);
        result.encoded = std::string(auth_rs.encoded.data(), auth_rs.encoded.length());
        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<HybridBarkNotificationSubscriptionSpec>
  subscribeNotifications(const std::function<void(const BarkNotificationEvent&)>& onEvent) override {
    try {
      auto subscription = std::make_shared<BarkNotificationSubscription>(
          bark_cxx::subscribe_notifications(), std::function<void(const BarkNotificationEvent&)>(onEvent));
      trackSubscription(subscription);
      return subscription;
    } catch (const rust::Error& e) {
      throw std::runtime_error(e.what());
    }
  }

  std::shared_ptr<HybridBarkNotificationSubscriptionSpec>
  subscribeArkoorAddressMovements(const std::string& address,
                                  const std::function<void(const BarkNotificationEvent&)>& onEvent) override {
    try {
      auto subscription =
          std::make_shared<BarkNotificationSubscription>(bark_cxx::subscribe_arkoor_address_movements(address),
                                                         std::function<void(const BarkNotificationEvent&)>(onEvent));
      trackSubscription(subscription);
      return subscription;
    } catch (const rust::Error& e) {
      throw std::runtime_error(e.what());
    }
  }

  std::shared_ptr<HybridBarkNotificationSubscriptionSpec>
  subscribeLightningPaymentMovements(const std::string& paymentHash,
                                     const std::function<void(const BarkNotificationEvent&)>& onEvent) override {
    try {
      auto subscription =
          std::make_shared<BarkNotificationSubscription>(bark_cxx::subscribe_lightning_payment_movements(paymentHash),
                                                         std::function<void(const BarkNotificationEvent&)>(onEvent));
      trackSubscription(subscription);
      return subscription;
    } catch (const rust::Error& e) {
      throw std::runtime_error(e.what());
    }
  }

  std::shared_ptr<Promise<std::vector<BarkMovement>>> history() override {
    return Promise<std::vector<BarkMovement>>::async([]() {
      try {
        rust::Vec<bark_cxx::BarkMovement> movements_rs = bark_cxx::history();

        std::vector<BarkMovement> movements;
        movements.reserve(movements_rs.size());

        for (const auto& movement_rs : movements_rs) {
          movements.push_back(convertRustMovement(movement_rs));
        }

        return movements;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> updateHistoryMetadata(double movementId, const std::string& patchJson) override {
    return Promise<void>::async([movementId, patchJson]() {
      if (!std::isfinite(movementId) || std::trunc(movementId) != movementId || movementId < 0 ||
          movementId > static_cast<double>(std::numeric_limits<uint32_t>::max())) {
        throw std::invalid_argument("movementId must be a finite unsigned 32-bit integer");
      }

      try {
        bark_cxx::update_history_metadata(static_cast<uint32_t>(movementId), patchJson);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::vector<BarkVtxo>>> vtxos() override {
    return Promise<std::vector<BarkVtxo>>::async([]() {
      try {
        rust::Vec<bark_cxx::BarkVtxo> rust_vtxos = bark_cxx::vtxos();
        return convertRustVtxosToVector(rust_vtxos);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkVtxo>> decodeVtxoHex(const std::string& vtxoHex) override {
    return Promise<BarkVtxo>::async([vtxoHex]() {
      try {
        bark_cxx::BarkVtxo rust_vtxo = bark_cxx::decode_vtxo_hex(vtxoHex);
        return convertRustVtxo(rust_vtxo);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkVtxo>> importVtxo(const std::string& vtxoHex) override {
    return Promise<BarkVtxo>::async([vtxoHex]() {
      try {
        bark_cxx::BarkVtxo rust_vtxo = bark_cxx::import_vtxo(vtxoHex);
        return convertRustVtxo(rust_vtxo);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> dangerousDropVtxo(const std::string& vtxoId) override {
    return Promise<void>::async([vtxoId]() {
      try {
        bark_cxx::dangerous_drop_vtxo(vtxoId);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> unlockVtxos(const std::vector<std::string>& vtxoIds) override {
    return Promise<void>::async([vtxoIds]() {
      try {
        rust::Vec<rust::String> rust_vtxo_ids;
        rust_vtxo_ids.reserve(vtxoIds.size());
        for (const auto& vtxoId : vtxoIds) {
          rust_vtxo_ids.push_back(vtxoId);
        }
        bark_cxx::unlock_vtxos(std::move(rust_vtxo_ids));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::vector<BarkVtxo>>> getExpiringVtxos(double threshold) override {
    return Promise<std::vector<BarkVtxo>>::async([threshold]() {
      try {
        rust::Vec<bark_cxx::BarkVtxo> rust_vtxos = bark_cxx::get_expiring_vtxos(static_cast<uint32_t>(threshold));
        return convertRustVtxosToVector(rust_vtxos);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::optional<DelegatedRoundState>>>
  refreshVtxosDelegated(const std::vector<std::string>& vtxoIds) override {
    return Promise<std::optional<DelegatedRoundState>>::async([vtxoIds]() {
      try {
        rust::Vec<rust::String> rust_vtxo_ids;
        rust_vtxo_ids.reserve(vtxoIds.size());
        for (const auto& vtxoId : vtxoIds) {
          rust_vtxo_ids.push_back(vtxoId);
        }
        bark_cxx::DelegatedRoundState state = bark_cxx::refresh_vtxos_delegated(std::move(rust_vtxo_ids));
        return convertRustDelegatedRoundState(state);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::optional<double>>> getFirstExpiringVtxoBlockheight() override {
    return Promise<std::optional<double>>::async([]() {
      try {
        const uint32_t* result_ptr = bark_cxx::get_first_expiring_vtxo_blockheight();
        if (result_ptr == nullptr) {
          return std::optional<double>(std::nullopt);
        }
        double value = static_cast<double>(*result_ptr);
        delete result_ptr; // Free the heap-allocated memory from Rust
        return std::optional<double>(value);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::optional<double>>> getNextRequiredRefreshBlockheight() override {
    return Promise<std::optional<double>>::async([]() {
      try {
        const uint32_t* result_ptr = bark_cxx::get_next_required_refresh_blockheight();
        if (result_ptr == nullptr) {
          return std::optional<double>(std::nullopt);
        }
        double value = static_cast<double>(*result_ptr);
        delete result_ptr; // Free the heap-allocated memory from Rust
        return std::optional<double>(value);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  // --- Onchain Operations ---

  std::shared_ptr<Promise<OnchainBalanceResult>> onchainBalance() override {
    return Promise<OnchainBalanceResult>::async([]() {
      try {
        bark_cxx::OnChainBalance rust_balance = bark_cxx::onchain_balance();
        OnchainBalanceResult balance;
        balance.immature = static_cast<double>(rust_balance.immature);
        balance.trusted_pending = static_cast<double>(rust_balance.trusted_pending);
        balance.untrusted_pending = static_cast<double>(rust_balance.untrusted_pending);
        balance.confirmed = static_cast<double>(rust_balance.confirmed);
        return balance;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> onchainSync() override {
    return Promise<void>::async([]() {
      try {
        bark_cxx::onchain_sync();
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<bool>> onchainIsMine(const std::string& address) override {
    return Promise<bool>::async([address]() {
      try {
        return bark_cxx::onchain_is_mine(address);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> onchainListUnspent() override {
    return Promise<std::string>::async([]() {
      try {
        rust::String json_rs = bark_cxx::onchain_list_unspent();
        return std::string(json_rs.data(), json_rs.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> onchainUtxos() override {
    return Promise<std::string>::async([]() {
      try {
        rust::String json_rs = bark_cxx::onchain_utxos();
        return std::string(json_rs.data(), json_rs.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkFeeRates>> onchainFeeRates() override {
    return Promise<BarkFeeRates>::async([]() {
      try {
        bark_cxx::BarkFeeRates rust_result = bark_cxx::onchain_fee_rates();

        BarkFeeRates result;
        result.fast = rust_result.fast;
        result.regular = rust_result.regular;
        result.slow = rust_result.slow;

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::vector<OnchainTransactionInfo>>> onchainTransactions() override {
    return Promise<std::vector<OnchainTransactionInfo>>::async([]() {
      try {
        rust::Vec<bark_cxx::OnchainTransactionInfo> rust_results = bark_cxx::onchain_transactions();

        std::vector<OnchainTransactionInfo> results;
        results.reserve(rust_results.size());

        for (const auto& rust_result : rust_results) {
          OnchainTransactionInfo result;
          result.txid = std::string(rust_result.txid.data(), rust_result.txid.length());
          result.tx_hex = std::string(rust_result.tx_hex.data(), rust_result.tx_hex.length());
          result.has_onchain_fee = rust_result.has_onchain_fee;
          result.onchain_fee_sat = static_cast<double>(rust_result.onchain_fee_sat);
          result.balance_change_sat = static_cast<double>(rust_result.balance_change_sat);
          result.has_confirmation = rust_result.has_confirmation;
          result.confirmation_height = static_cast<double>(rust_result.confirmation_height);
          result.confirmation_hash =
              std::string(rust_result.confirmation_hash.data(), rust_result.confirmation_hash.length());
          results.push_back(std::move(result));
        }

        return results;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> onchainAddress() override {
    return Promise<std::string>::async([]() {
      try {
        rust::String address_rs = bark_cxx::onchain_address();
        return std::string(address_rs.data(), address_rs.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<OnchainPaymentResult>> onchainSend(const std::string& destination, double amountSat,
                                                             std::optional<double> feeRate) override {
    return Promise<OnchainPaymentResult>::async([destination, amountSat, feeRate]() {
      try {
        uint64_t feeRate_val;
        bark_cxx::OnchainPaymentResult rust_result;
        if (feeRate.has_value()) {
          feeRate_val = static_cast<uint64_t>(feeRate.value());
          rust_result = bark_cxx::onchain_send(destination, static_cast<uint64_t>(amountSat), &feeRate_val);
        } else {
          rust_result = bark_cxx::onchain_send(destination, static_cast<uint64_t>(amountSat), nullptr);
        }

        OnchainPaymentResult result;
        result.txid = std::string(rust_result.txid.data(), rust_result.txid.length());
        result.amount_sat = static_cast<double>(rust_result.amount_sat);
        result.destination_address =
            std::string(rust_result.destination_address.data(), rust_result.destination_address.length());

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> onchainDrain(const std::string& destination,
                                                     std::optional<double> feeRate) override {
    return Promise<std::string>::async([destination, feeRate]() {
      try {
        uint64_t feeRate_val;
        rust::String txid_rs;
        if (feeRate.has_value()) {
          feeRate_val = static_cast<uint64_t>(feeRate.value());
          txid_rs = bark_cxx::onchain_drain(destination, &feeRate_val);
        } else {
          txid_rs = bark_cxx::onchain_drain(destination, nullptr);
        }
        return std::string(txid_rs.data(), txid_rs.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<std::string>> onchainSendMany(const std::vector<BarkSendManyOutput>& outputs,
                                                        std::optional<double> feeRate) override {
    return Promise<std::string>::async([outputs, feeRate]() {
      try {
        rust::Vec<bark_cxx::SendManyOutput> cxx_outputs;
        for (const auto& output : outputs) {
          cxx_outputs.push_back({rust::String(output.destination), static_cast<uint64_t>(output.amountSat)});
        }
        uint64_t feeRate_val;
        rust::String txid_rs;
        if (feeRate.has_value()) {
          feeRate_val = static_cast<uint64_t>(feeRate.value());
          txid_rs = bark_cxx::onchain_send_many(std::move(cxx_outputs), &feeRate_val);
        } else {
          txid_rs = bark_cxx::onchain_send_many(std::move(cxx_outputs), nullptr);
        }
        return std::string(txid_rs.data(), txid_rs.length());
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  // --- Lightning Operations ---

  std::shared_ptr<Promise<LightningPaymentResult>> payLightningInvoice(const std::string& destination, bool wait,
                                                                       std::optional<double> amountSat,
                                                                       std::optional<double> retryForSeconds) override {
    return Promise<LightningPaymentResult>::async([destination, wait, amountSat, retryForSeconds]() {
      try {
        bark_cxx::LightningSendOptions options{wait, retryForSeconds.has_value(), retryForSeconds.value_or(0)};
        bark_cxx::LightningPaymentResult rust_result;
        if (amountSat.has_value()) {
          uint64_t amountSat_val = static_cast<uint64_t>(amountSat.value());
          rust_result = bark_cxx::pay_lightning_invoice(destination, &amountSat_val, options);
        } else {
          rust_result = bark_cxx::pay_lightning_invoice(destination, nullptr, options);
        }

        return convertRustLightningPaymentResult(rust_result);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  // Pay an invoice resolved by the caller while preserving the durable,
  // user-facing origin in Bark instead of storing only the one-time invoice.
  std::shared_ptr<Promise<LightningPaymentResult>>
  payLightningInvoiceWithOrigin(const std::string& invoice, const LightningPaymentOrigin& origin, bool wait,
                                std::optional<double> retryForSeconds) override {
    return Promise<LightningPaymentResult>::async([invoice, origin, wait, retryForSeconds]() {
      try {
        bark_cxx::LightningSendOptions options{wait, retryForSeconds.has_value(), retryForSeconds.value_or(0)};
        bark_cxx::LightningPaymentResult rust_result = bark_cxx::pay_lightning_invoice_with_origin(
            invoice, lightningPaymentOriginMethodToString(origin.method), origin.value, options);

        return convertRustLightningPaymentResult(rust_result);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<LightningPaymentResult>> payLightningOffer(const std::string& offer, bool wait,
                                                                     std::optional<double> amountSat,
                                                                     std::optional<double> retryForSeconds) override {
    return Promise<LightningPaymentResult>::async([offer, wait, amountSat, retryForSeconds]() {
      try {
        bark_cxx::LightningSendOptions options{wait, retryForSeconds.has_value(), retryForSeconds.value_or(0)};
        bark_cxx::LightningPaymentResult rust_result;
        if (amountSat.has_value()) {
          uint64_t amountSat_val = static_cast<uint64_t>(amountSat.value());
          rust_result = bark_cxx::pay_lightning_offer(offer, &amountSat_val, options);
        } else {
          rust_result = bark_cxx::pay_lightning_offer(offer, nullptr, options);
        }

        return convertRustLightningPaymentResult(rust_result);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<LightningPaymentResult>> payLightningAddress(const std::string& addr, double amountSat,
                                                                       const std::string& comment, bool wait,
                                                                       std::optional<double> retryForSeconds) override {
    return Promise<LightningPaymentResult>::async([addr, amountSat, comment, wait, retryForSeconds]() {
      try {
        bark_cxx::LightningSendOptions options{wait, retryForSeconds.has_value(), retryForSeconds.value_or(0)};
        bark_cxx::LightningPaymentResult rust_result =
            bark_cxx::pay_lightning_address(addr, static_cast<uint64_t>(amountSat), comment, options);

        return convertRustLightningPaymentResult(rust_result);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkFeeEstimate>> estimateLightningSendFee(double amountSat) override {
    return Promise<BarkFeeEstimate>::async([amountSat]() {
      try {
        bark_cxx::BarkFeeEstimate rust_result = bark_cxx::estimate_lightning_send_fee(static_cast<uint64_t>(amountSat));

        BarkFeeEstimate result;
        result.gross_amount_sat = static_cast<double>(rust_result.gross_amount_sat);
        result.fee_sat = static_cast<double>(rust_result.fee_sat);
        result.net_amount_sat = static_cast<double>(rust_result.net_amount_sat);

        std::vector<std::string> vtxos_spent;
        vtxos_spent.reserve(rust_result.vtxos_spent.size());
        for (const auto& vtxo_id : rust_result.vtxos_spent) {
          vtxos_spent.push_back(std::string(vtxo_id.data(), vtxo_id.length()));
        }
        result.vtxos_spent = vtxos_spent;

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<Bolt11Invoice>> bolt11Invoice(double amountSat,
                                                        const std::optional<std::string>& description,
                                                        const std::optional<std::string>& token) override {
    return Promise<Bolt11Invoice>::async([amountSat, description, token]() {
      try {
        std::unique_ptr<rust::String> description_rs;
        std::unique_ptr<rust::String> token_rs;
        if (description.has_value()) {
          description_rs = std::make_unique<rust::String>(description.value());
        }
        if (token.has_value()) {
          token_rs = std::make_unique<rust::String>(token.value());
        }
        const auto invoice_rs = bark_cxx::bolt11_invoice(
            static_cast<uint64_t>(amountSat), description_rs.get(), token_rs.get());
        return Bolt11Invoice(std::string(invoice_rs.bolt11_invoice.data(), invoice_rs.bolt11_invoice.length()),
                             std::string(invoice_rs.payment_secret.data(), invoice_rs.payment_secret.length()),
                             std::string(invoice_rs.payment_hash.data(), invoice_rs.payment_hash.length()));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<LightningReceive>>
  tryClaimLightningReceive(const std::string& paymentHash, bool wait) override {
    return Promise<LightningReceive>::async([paymentHash, wait]() -> LightningReceive {
      try {
        const auto result = bark_cxx::try_claim_lightning_receive(paymentHash, wait);
        return convertRustLightningReceive(result);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> tryClaimAllLightningReceives(bool wait) override {
    return Promise<void>::async([wait]() {
      try {
        bark_cxx::try_claim_all_lightning_receives(wait);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<LightningReceive>>
  lightningReceiveStatus(const std::string& paymentHash) override {
    return Promise<LightningReceive>::async([paymentHash]() {
      try {
        return convertRustLightningReceive(bark_cxx::lightning_receive_status(paymentHash));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<LightningPaymentResult>> checkLightningPayment(const std::string& paymentHash,
                                                                         bool wait) override {
    return Promise<LightningPaymentResult>::async([paymentHash, wait]() {
      try {
        return convertRustLightningPaymentResult(bark_cxx::check_lightning_payment(paymentHash, wait));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  // --- Ark Operations ---
  std::shared_ptr<Promise<BoardResult>> boardAmount(double amountSat) override {
    return Promise<BoardResult>::async([amountSat]() {
      try {
        bark_cxx::BoardResult result_rs = bark_cxx::board_amount(static_cast<uint64_t>(amountSat));
        BoardResult result;
        result.funding_txid = std::string(result_rs.funding_txid.data(), result_rs.funding_txid.length());
        std::vector<std::string> vtxos_vec;
        for (const auto& vtxo : result_rs.vtxos) {
          vtxos_vec.push_back(std::string(vtxo.data(), vtxo.length()));
        }
        result.vtxos = vtxos_vec;
        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BoardResult>> boardAll() override {
    return Promise<BoardResult>::async([]() {
      try {
        bark_cxx::BoardResult result_rs = bark_cxx::board_all();
        BoardResult result;
        result.funding_txid = std::string(result_rs.funding_txid.data(), result_rs.funding_txid.length());
        std::vector<std::string> vtxos_vec;
        for (const auto& vtxo : result_rs.vtxos) {
          vtxos_vec.push_back(std::string(vtxo.data(), vtxo.length()));
        }
        result.vtxos = vtxos_vec;
        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<void>> validateArkoorAddress(const std::string& address) override {
    return Promise<void>::async([address]() {
      try {
        bark_cxx::validate_arkoor_address(address);
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<ArkoorPaymentResult>> sendArkoorPayment(const std::string& destination,
                                                                  double amountSat) override {
    return Promise<ArkoorPaymentResult>::async([destination, amountSat]() {
      try {
        bark_cxx::ArkoorPaymentResult rust_result =
            bark_cxx::send_arkoor_payment(destination, static_cast<uint64_t>(amountSat));

        ArkoorPaymentResult result;
        result.amount_sat = static_cast<double>(rust_result.amount_sat);
        result.destination_pubkey =
            std::string(rust_result.destination_pubkey.data(), rust_result.destination_pubkey.length());
        result.movement_id = static_cast<double>(rust_result.movement_id);

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<ParsedPaymentRequest>> parsePaymentRequest(const std::string& payment) override {
    return Promise<ParsedPaymentRequest>::async([payment]() {
      try {
        bark_cxx::ParsedPaymentRequest parsed = bark_cxx::parse_payment_request(payment);

        ParsedPaymentRequest result;
        if (parsed.has_amount) result.amount_sat = static_cast<double>(parsed.amount_sat);
        if (!parsed.label.empty()) result.label = std::string(parsed.label);
        if (!parsed.message.empty()) result.message = std::string(parsed.message);
        for (const auto& option : parsed.options) {
          PaymentRequestOption converted;
          converted.method_type = std::string(option.method_type);
          converted.value = std::string(option.value);
          for (const auto& error : option.errors) converted.errors.emplace_back(std::string(error));
          result.options.push_back(std::move(converted));
        }
        if (parsed.has_default_option) {
          result.default_option_index = static_cast<double>(parsed.default_option_index);
        }

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkFeeEstimate>> estimateArkoorPaymentFee(double amountSat) override {
    return Promise<BarkFeeEstimate>::async([amountSat]() {
      try {
        bark_cxx::BarkFeeEstimate rust_result = bark_cxx::estimate_arkoor_payment_fee(static_cast<uint64_t>(amountSat));

        BarkFeeEstimate result;
        result.gross_amount_sat = static_cast<double>(rust_result.gross_amount_sat);
        result.fee_sat = static_cast<double>(rust_result.fee_sat);
        result.net_amount_sat = static_cast<double>(rust_result.net_amount_sat);

        std::vector<std::string> vtxos_spent;
        vtxos_spent.reserve(rust_result.vtxos_spent.size());
        for (const auto& vtxo_id : rust_result.vtxos_spent) {
          vtxos_spent.push_back(std::string(vtxo_id.data(), vtxo_id.length()));
        }
        result.vtxos_spent = vtxos_spent;

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkFeeEstimate>> estimateBoardOffchainFee(double amountSat) override {
    return Promise<BarkFeeEstimate>::async([amountSat]() {
      try {
        bark_cxx::BarkFeeEstimate rust_result =
            bark_cxx::estimate_board_offchain_fee(static_cast<uint64_t>(amountSat));

        BarkFeeEstimate result;
        result.gross_amount_sat = static_cast<double>(rust_result.gross_amount_sat);
        result.fee_sat = static_cast<double>(rust_result.fee_sat);
        result.net_amount_sat = static_cast<double>(rust_result.net_amount_sat);

        std::vector<std::string> vtxos_spent;
        vtxos_spent.reserve(rust_result.vtxos_spent.size());
        for (const auto& vtxo_id : rust_result.vtxos_spent) {
          vtxos_spent.push_back(std::string(vtxo_id.data(), vtxo_id.length()));
        }
        result.vtxos_spent = vtxos_spent;

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkFeeEstimate>> estimateRefreshFee(const std::vector<std::string>& vtxoIds) override {
    return Promise<BarkFeeEstimate>::async([vtxoIds]() {
      try {
        rust::Vec<rust::String> rust_vtxo_ids;
        rust_vtxo_ids.reserve(vtxoIds.size());
        for (const auto& vtxoId : vtxoIds) {
          rust_vtxo_ids.push_back(vtxoId);
        }
        bark_cxx::BarkFeeEstimate rust_result = bark_cxx::estimate_refresh_fee(std::move(rust_vtxo_ids));

        BarkFeeEstimate result;
        result.gross_amount_sat = static_cast<double>(rust_result.gross_amount_sat);
        result.fee_sat = static_cast<double>(rust_result.fee_sat);
        result.net_amount_sat = static_cast<double>(rust_result.net_amount_sat);

        std::vector<std::string> vtxos_spent;
        vtxos_spent.reserve(rust_result.vtxos_spent.size());
        for (const auto& vtxo_id : rust_result.vtxos_spent) {
          vtxos_spent.push_back(std::string(vtxo_id.data(), vtxo_id.length()));
        }
        result.vtxos_spent = vtxos_spent;

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<OffboardResult>> sendOnchain(const std::string& destination, double amountSat) override {
    return Promise<OffboardResult>::async([destination, amountSat]() {
      try {
        return toOffboardResult(bark_cxx::send_onchain(destination, static_cast<uint64_t>(amountSat)));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkFeeEstimate>> estimateSendOnchain(const std::string& destination,
                                                                double amountSat) override {
    return Promise<BarkFeeEstimate>::async([destination, amountSat]() {
      try {
        bark_cxx::BarkFeeEstimate rust_result =
            bark_cxx::estimate_send_onchain(destination, static_cast<uint64_t>(amountSat));

        BarkFeeEstimate result;
        result.gross_amount_sat = static_cast<double>(rust_result.gross_amount_sat);
        result.fee_sat = static_cast<double>(rust_result.fee_sat);
        result.net_amount_sat = static_cast<double>(rust_result.net_amount_sat);

        std::vector<std::string> vtxos_spent;
        vtxos_spent.reserve(rust_result.vtxos_spent.size());
        for (const auto& vtxo_id : rust_result.vtxos_spent) {
          vtxos_spent.push_back(std::string(vtxo_id.data(), vtxo_id.length()));
        }
        result.vtxos_spent = vtxos_spent;

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  // --- Offboarding / Exiting ---

  std::shared_ptr<Promise<OffboardResult>> offboardSpecific(const std::vector<std::string>& vtxoIds,
                                                            const std::string& destinationAddress) override {
    return Promise<OffboardResult>::async([vtxoIds, destinationAddress]() {
      try {
        rust::Vec<rust::String> rust_vtxo_ids;
        for (const auto& id : vtxoIds) {
          rust_vtxo_ids.push_back(rust::String(id));
        }
        return toOffboardResult(bark_cxx::offboard_specific(std::move(rust_vtxo_ids), destinationAddress));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<OffboardResult>> offboardAll(const std::string& destinationAddress) override {
    return Promise<OffboardResult>::async([destinationAddress]() {
      try {
        return toOffboardResult(bark_cxx::offboard_all(destinationAddress));
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

  std::shared_ptr<Promise<BarkFeeEstimate>> estimateOffboardAll(const std::string& destinationAddress) override {
    return Promise<BarkFeeEstimate>::async([destinationAddress]() {
      try {
        bark_cxx::BarkFeeEstimate rust_result = bark_cxx::estimate_offboard_all(destinationAddress);

        BarkFeeEstimate result;
        result.gross_amount_sat = static_cast<double>(rust_result.gross_amount_sat);
        result.fee_sat = static_cast<double>(rust_result.fee_sat);
        result.net_amount_sat = static_cast<double>(rust_result.net_amount_sat);

        std::vector<std::string> vtxos_spent;
        vtxos_spent.reserve(rust_result.vtxos_spent.size());
        for (const auto& vtxo_id : rust_result.vtxos_spent) {
          vtxos_spent.push_back(std::string(vtxo_id.data(), vtxo_id.length()));
        }
        result.vtxos_spent = vtxos_spent;

        return result;
      } catch (const rust::Error& e) {
        throw std::runtime_error(e.what());
      }
    });
  }

private:
  // Tag for logging/debugging within Nitro
  std::mutex subscriptions_mutex_;
  std::vector<std::weak_ptr<HybridBarkNotificationSubscriptionSpec>> subscriptions_;
  std::mutex state_change_subscriptions_mutex_;
  std::vector<std::weak_ptr<HybridWalletStateChangeSubscriptionSpec>> state_change_subscriptions_;
  static constexpr auto TAG = "NitroArk";
};

} // namespace margelo::nitro::nitroark
