#include "daemon/daemon_controller.h"
#include "data/transaction_history_cache.h"
#include "daemon/lifecycle_adapters.h"
#include "data/wallet_state.h"
#include "rpc/connection.h"
#include "resources/embedded_resources.h"
#include "services/network_refresh_service.h"
#include "services/refresh_scheduler.h"
#include "services/wallet_security_controller.h"
#include "services/wallet_security_workflow.h"
#include "services/wallet_security_workflow_executor.h"
#include "ui/explorer/explorer_block_cache.h"
#include "ui/windows/balance_address_list.h"
#include "ui/windows/balance_recent_tx.h"
#include "ui/windows/console_input_model.h"
#include "ui/windows/console_output_model.h"
#include "ui/windows/console_tab_helpers.h"
#include "ui/windows/mining_benchmark.h"
#include "ui/windows/mining_pool_panel.h"
#include "ui/windows/mining_tab_helpers.h"
#include "util/amount_format.h"
#include "util/payment_uri.h"
#include "wallet/lite_backend_artifact_contract.h"
#include "wallet/lite_bridge_runtime.h"
#include "wallet/lite_wallet_controller.h"
#include "wallet/lite_wallet_gateway.h"
#include "wallet/lite_wallet_state_mapper.h"
#include "config/settings.h"
#include "data/wallet_state.h"
#include "fake_lite_backend.h"

#include <chrono>
#include <cmath>
#include <thread>
#include <atomic>
#include <deque>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <new>
#include <set>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

namespace fs = std::filesystem;

namespace {

int g_failures = 0;

void expectTrue(bool value, const char* expr, const char* file, int line)
{
    if (value) return;
    std::cerr << file << ":" << line << " expected true: " << expr << "\n";
    ++g_failures;
}

template <typename T, typename U>
void expectEqual(const T& actual, const U& expected, const char* actualExpr,
                 const char* expectedExpr, const char* file, int line)
{
    if (actual == expected) return;
    std::cerr << file << ":" << line << " expected " << actualExpr << " == "
              << expectedExpr << "\n";
    ++g_failures;
}

void expectNear(double actual, double expected, double epsilon, const char* file, int line)
{
    if (std::abs(actual - expected) <= epsilon) return;
    std::cerr << file << ":" << line << " expected near " << expected
              << " (actual=" << actual << ")\n";
    ++g_failures;
}

#define EXPECT_TRUE(expr) expectTrue((expr), #expr, __FILE__, __LINE__)
#define EXPECT_FALSE(expr) expectTrue(!(expr), "!(" #expr ")", __FILE__, __LINE__)
#define EXPECT_EQ(actual, expected) expectEqual((actual), (expected), #actual, #expected, __FILE__, __LINE__)
#define EXPECT_NEAR(actual, expected, epsilon) expectNear((actual), (expected), (epsilon), __FILE__, __LINE__)

fs::path makeTempDir()
{
    auto now = std::chrono::steady_clock::now().time_since_epoch().count();
    fs::path dir = fs::temp_directory_path() / ("obsidian_phase4_tests_" + std::to_string(now));
    fs::create_directories(dir);
    return dir;
}

void writeTestFile(const fs::path& path, const std::string& content)
{
    std::ofstream output(path, std::ios::binary);
    output << content;
}

using LiteBackendArtifactContractInput = dragonx::wallet::LiteBackendArtifactContractInput;
using LiteBackendArtifactContractIssue = dragonx::wallet::LiteBackendArtifactContractIssue;
using LiteBackendArtifactContractLinkMode = dragonx::wallet::LiteBackendArtifactContractLinkMode;
using LiteBackendArtifactContractResult = dragonx::wallet::LiteBackendArtifactContractResult;
using LiteBackendArtifactContractStatus = dragonx::wallet::LiteBackendArtifactContractStatus;
using LiteBridgeRuntime = dragonx::wallet::LiteBridgeRuntime;
using LiteBridgeRuntimeBindingInput = dragonx::wallet::LiteBridgeRuntimeBindingInput;
using LiteBridgeRuntimeBindingResult = dragonx::wallet::LiteBridgeRuntimeBindingResult;
using LiteBridgeRuntimeFakeDynamicLoaderInput = dragonx::wallet::LiteBridgeRuntimeFakeDynamicLoaderInput;
using LiteBridgeRuntimeFakeDynamicLoaderResult = dragonx::wallet::LiteBridgeRuntimeFakeDynamicLoaderResult;
using LiteBridgeRuntimeIssue = dragonx::wallet::LiteBridgeRuntimeIssue;
using LiteBridgeRuntimeLinkMode = dragonx::wallet::LiteBridgeRuntimeLinkMode;
using LiteBridgeRuntimeDynamicLoaderSmokeGateInput = dragonx::wallet::LiteBridgeRuntimeDynamicLoaderSmokeGateInput;
using LiteBridgeRuntimeDynamicLoaderSmokeGateResult = dragonx::wallet::LiteBridgeRuntimeDynamicLoaderSmokeGateResult;
using LiteBridgeRuntimePlatformLoaderReviewInput = dragonx::wallet::LiteBridgeRuntimePlatformLoaderReviewInput;
using LiteBridgeRuntimePlatformLoaderReviewResult = dragonx::wallet::LiteBridgeRuntimePlatformLoaderReviewResult;
using LiteBridgeRuntimePlatformDynamicLoaderAdapterContractInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderAdapterContractInput;
using LiteBridgeRuntimePlatformDynamicLoaderAdapterContractResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderAdapterContractResult;
using LiteBridgeRuntimePlatformDynamicLoaderAdapter = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderAdapter;
using LiteBridgeRuntimePlatformDynamicLoaderAdapterCallResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderAdapterCallResult;
using LiteBridgeRuntimePlatformDynamicLoaderNoOpAdapterSeamInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderNoOpAdapterSeamInput;
using LiteBridgeRuntimePlatformDynamicLoaderNoOpAdapterSeamResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderNoOpAdapterSeamResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterImplementationReviewInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterImplementationReviewInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterImplementationReviewResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterImplementationReviewResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterDisabledScaffoldInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterDisabledScaffoldInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterDisabledScaffoldResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterDisabledScaffoldResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionPreflightInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionPreflightInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionPreflightResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionPreflightResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionApprovalGateInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionApprovalGateInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionApprovalGateResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionApprovalGateResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledOwnerHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledOwnerHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledOwnerHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledOwnerHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackBindingInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackBindingInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackBindingResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackBindingResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackPreInvocationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackPreInvocationGuardInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackPreInvocationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackPreInvocationGuardResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalResultInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalResultInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalResultResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalResultResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalResultPropagationInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalResultPropagationInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalResultPropagationResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalResultPropagationResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput;
using LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult;
using LiteBridgeRuntimeBatch46ResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput;
using LiteBridgeRuntimeBatch46ResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult;
using LiteBridgeRuntimeBatch47ReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimeBatch47ReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimeBatch48StatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimeBatch48StatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult;
using LiteBridgeRuntimeBatch49PublicationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput;
using LiteBridgeRuntimeBatch49PublicationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult;
using LiteBridgeRuntimeBatch50ResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput;
using LiteBridgeRuntimeBatch50ResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult;
using LiteBridgeRuntimeBatch51ReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimeBatch51ReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimeBatch52StatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimeBatch52StatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult;
using LiteBridgeRuntimeBatch53PublicationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput;
using LiteBridgeRuntimeBatch53PublicationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult;
using LiteBridgeRuntimeBatch54ResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput;
using LiteBridgeRuntimeBatch54ResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult;
using LiteBridgeRuntimeBatch55ReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimeBatch55ReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimeBatch56StatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimeBatch56StatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult;
using LiteBridgeRuntimeBatch57PublicationGuardInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardInput;
using LiteBridgeRuntimeBatch57PublicationGuardResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResult;
using LiteBridgeRuntimeBatch58ResultHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffInput;
using LiteBridgeRuntimeBatch58ResultHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffResult;
using LiteBridgeRuntimeBatch59ReadinessProjectionInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionInput;
using LiteBridgeRuntimeBatch59ReadinessProjectionResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionResult;
using LiteBridgeRuntimeBatch60StatusHandoffInput = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffInput;
using LiteBridgeRuntimeBatch60StatusHandoffResult = dragonx::wallet::LiteBridgeRuntimePlatformDynamicLoaderRealAdapterLoadOnlyExecutionDisabledCallbackRefusalPropagationConsumerReadinessDispatchConsumptionGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffPublicationGuardResultHandoffReadinessProjectionStatusHandoffResult;
static_assert(!std::is_same_v<LiteBridgeRuntimeBatch54ResultHandoffInput, LiteBridgeRuntimeBatch50ResultHandoffInput>);
static_assert(!std::is_same_v<LiteBridgeRuntimeBatch54ResultHandoffResult, LiteBridgeRuntimeBatch50ResultHandoffResult>);
static_assert(!std::is_same_v<LiteBridgeRuntimeBatch55ReadinessProjectionInput, LiteBridgeRuntimeBatch51ReadinessProjectionInput>);
static_assert(!std::is_same_v<LiteBridgeRuntimeBatch55ReadinessProjectionResult, LiteBridgeRuntimeBatch51ReadinessProjectionResult>);
static_assert(!std::is_same_v<LiteBridgeRuntimeBatch60StatusHandoffInput, LiteBridgeRuntimeBatch52StatusHandoffInput>);
static_assert(!std::is_same_v<LiteBridgeRuntimeBatch60StatusHandoffResult, LiteBridgeRuntimeBatch52StatusHandoffResult>);
using LiteBridgeRuntimeSharedArtifactSmokeEvidenceAcquisitionInput = dragonx::wallet::LiteBridgeRuntimeSharedArtifactSmokeEvidenceAcquisitionInput;
using LiteBridgeRuntimeSharedArtifactSmokeEvidenceAcquisitionResult = dragonx::wallet::LiteBridgeRuntimeSharedArtifactSmokeEvidenceAcquisitionResult;
using LiteBridgeRuntimeStatus = dragonx::wallet::LiteBridgeRuntimeStatus;



































































































































































template <typename T, typename Factory>
std::unique_ptr<T> heapConstructPlanResult(Factory&& factory)
{
    void* storage = ::operator new(sizeof(T));
    try {
        return std::unique_ptr<T>(::new (storage) T(factory()));
    } catch (...) {
        ::operator delete(storage);
        throw;
    }
}


dragonx::wallet::LiteBackendArtifactProvenanceMetadata makeReadyLiteBackendArtifactProvenance()
{
    dragonx::wallet::LiteBackendArtifactProvenanceMetadata provenance;
    provenance.ownerReady = true;
    provenance.metadataProvided = true;
    provenance.source = "test-fixture";
    provenance.builder = "phase4-tests";
    provenance.sourceRevision = "test-revision";
    provenance.artifactSetId = "phase1-contract";
    provenance.redacted = true;
    return provenance;
}


bool liteBackendArtifactContractHasIssue(
    const LiteBackendArtifactContractResult& result,
    LiteBackendArtifactContractIssue issue)
{
    for (const auto& issueInfo : result.issues) {
        if (issueInfo.issue == issue) return true;
    }
    return false;
}

int g_liteBridgeRuntimeFakeCallCount = 0;
int g_liteBridgeRuntimeTrackedFreeCount = 0;
int g_liteBridgeRuntimeTrackedShutdownCount = 0;
int g_liteBridgeRuntimeTrackedUnloadCount = 0;
std::vector<std::string> g_liteBridgeRuntimeTrackedFreedValues;
std::vector<std::string> g_liteBridgeRuntimeTrackedUnloadedHandles;
std::vector<std::string> g_liteBridgeRuntimeTeardownEvents;

char* makeLiteBridgeRuntimeOwnedCString(const std::string& value)
{
    char* rawValue = new char[value.size() + 1];
    for (std::size_t index = 0; index < value.size(); ++index) {
        rawValue[index] = value[index];
    }
    rawValue[value.size()] = '\0';
    return rawValue;
}

void resetLiteBridgeRuntimeTrackedFree()
{
    g_liteBridgeRuntimeTrackedFreeCount = 0;
    g_liteBridgeRuntimeTrackedShutdownCount = 0;
    g_liteBridgeRuntimeTrackedUnloadCount = 0;
    g_liteBridgeRuntimeTrackedFreedValues.clear();
    g_liteBridgeRuntimeTrackedUnloadedHandles.clear();
    g_liteBridgeRuntimeTeardownEvents.clear();
}

void fakeLiteBridgeRuntimeTrackedFreeString(char* value)
{
    ++g_liteBridgeRuntimeTrackedFreeCount;
    if (value) {
        g_liteBridgeRuntimeTrackedFreedValues.push_back(value);
        g_liteBridgeRuntimeTeardownEvents.push_back("free:" + std::string(value));
    }
    delete[] value;
}

void fakeLiteBridgeRuntimeTrackedShutdown()
{
    ++g_liteBridgeRuntimeTrackedShutdownCount;
    g_liteBridgeRuntimeTeardownEvents.push_back("shutdown");
}

void fakeLiteBridgeRuntimeTrackedUnload(const char* handleLabel)
{
    ++g_liteBridgeRuntimeTrackedUnloadCount;
    const std::string label = handleLabel ? handleLabel : "";
    g_liteBridgeRuntimeTrackedUnloadedHandles.push_back(label);
    g_liteBridgeRuntimeTeardownEvents.push_back("unload:" + label);
}

bool fakeLiteBridgeRuntimeWalletExists(const char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return true;
}

char* fakeLiteBridgeRuntimeInitializeNew(bool, const char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return nullptr;
}

char* fakeLiteBridgeRuntimeOwnedInitializeNew(bool, const char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return makeLiteBridgeRuntimeOwnedCString("initialize-new-ok");
}

char* fakeLiteBridgeRuntimeInitializeNewFromPhrase(bool,
                                                   const char*,
                                                   const char*,
                                                   unsigned long long,
                                                   unsigned long long,
                                                   bool)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return nullptr;
}

char* fakeLiteBridgeRuntimeOwnedInitializeNewFromPhrase(bool,
                                                        const char*,
                                                        const char*,
                                                        unsigned long long,
                                                        unsigned long long,
                                                        bool)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return makeLiteBridgeRuntimeOwnedCString("initialize-phrase-ok");
}

char* fakeLiteBridgeRuntimeInitializeExisting(bool, const char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return nullptr;
}

char* fakeLiteBridgeRuntimeOwnedInitializeExisting(bool, const char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return makeLiteBridgeRuntimeOwnedCString("initialize-existing-ok");
}

char* fakeLiteBridgeRuntimeExecute(const char*, const char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return nullptr;
}

char* fakeLiteBridgeRuntimeOwnedExecute(const char*, const char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return makeLiteBridgeRuntimeOwnedCString("bridge-runtime-ok");
}

void fakeLiteBridgeRuntimeFreeString(char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
}

bool fakeLiteBridgeRuntimeCheckServerOnline(const char*)
{
    ++g_liteBridgeRuntimeFakeCallCount;
    return true;
}

void fakeLiteBridgeRuntimeShutdown()
{
    ++g_liteBridgeRuntimeFakeCallCount;
}

dragonx::wallet::LiteClientBridgeApi makeCompleteFakeLiteBridgeRuntimeApi()
{
    return dragonx::wallet::LiteClientBridgeApi{
        &fakeLiteBridgeRuntimeWalletExists,
        &fakeLiteBridgeRuntimeInitializeNew,
        &fakeLiteBridgeRuntimeInitializeNewFromPhrase,
        &fakeLiteBridgeRuntimeInitializeExisting,
        &fakeLiteBridgeRuntimeExecute,
        &fakeLiteBridgeRuntimeFreeString,
        &fakeLiteBridgeRuntimeCheckServerOnline,
        &fakeLiteBridgeRuntimeShutdown,
    };
}

void clearLiteBridgeRuntimeSymbol(dragonx::wallet::LiteClientBridgeApi& bridgeApi,
                                  const std::string& logicalName)
{
    if (logicalName == "walletExists") bridgeApi.walletExists = nullptr;
    if (logicalName == "initializeNew") bridgeApi.initializeNew = nullptr;
    if (logicalName == "initializeNewFromPhrase") bridgeApi.initializeNewFromPhrase = nullptr;
    if (logicalName == "initializeExisting") bridgeApi.initializeExisting = nullptr;
    if (logicalName == "execute") bridgeApi.execute = nullptr;
    if (logicalName == "freeString") bridgeApi.freeString = nullptr;
    if (logicalName == "checkServerOnline") bridgeApi.checkServerOnline = nullptr;
    if (logicalName == "shutdown") bridgeApi.shutdown = nullptr;
}




LiteBridgeRuntimeFakeDynamicLoaderInput makeReadyFakeLiteBridgeRuntimeDynamicLoaderInput(
    const std::string& source = "fake-dynamic-library",
    const std::string& handleLabel = "phase6-handle")
{
    LiteBridgeRuntimeFakeDynamicLoaderInput input;
    input.artifactPathReviewed = true;
    input.platformLoaderStrategyReady = true;
    input.loadSequenceReady = true;
    input.unloadSequenceReady = true;
    input.handleStoreReady = true;
    input.symbolLookupReady = true;
    input.fakeHandleProvided = true;
    input.artifactPath = "/tmp/obsidian-dragon-phase6/" + handleLabel + ".so";
    input.displayPath = handleLabel + ".so";
    input.platform = "fake-linux";
    input.handleLabel = handleLabel;
    input.source = source;
    input.api = makeCompleteFakeLiteBridgeRuntimeApi();
    input.api.initializeNew = &fakeLiteBridgeRuntimeOwnedInitializeNew;
    input.api.initializeNewFromPhrase = &fakeLiteBridgeRuntimeOwnedInitializeNewFromPhrase;
    input.api.initializeExisting = &fakeLiteBridgeRuntimeOwnedInitializeExisting;
    input.api.execute = &fakeLiteBridgeRuntimeOwnedExecute;
    input.api.freeString = &fakeLiteBridgeRuntimeTrackedFreeString;
    input.api.shutdown = &fakeLiteBridgeRuntimeTrackedShutdown;
    input.unload = &fakeLiteBridgeRuntimeTrackedUnload;
    return input;
}









LiteBridgeRuntimePlatformDynamicLoaderAdapterCallResult
fakeLiteBridgeRuntimeNoOpAdapterBadSymbolLookup(const char*, const char*)
{
    LiteBridgeRuntimePlatformDynamicLoaderAdapterCallResult result;
    result.ok = true;
    result.disabled = false;
    result.noPlatformSymbolResolution = false;
    result.symbolAddressProduced = true;
    result.summary = "bad symbol lookup produced an address";
    return result;
}

LiteBridgeRuntimePlatformDynamicLoaderAdapterCallResult
fakeLiteBridgeRuntimeRealAdapterBadLoad(const char*, const char*)
{
    LiteBridgeRuntimePlatformDynamicLoaderAdapterCallResult result;
    result.ok = true;
    result.disabled = false;
    result.noPlatformDynamicLibraryLoaded = false;
    result.handleProduced = true;
    result.summary = "bad real adapter scaffold load produced a handle";
    return result;
}







































































bool liteBridgeRuntimeHasIssue(
    const LiteBridgeRuntimeBindingResult& result,
    LiteBridgeRuntimeIssue issue)
{
    for (const auto& issueInfo : result.issues) {
        if (issueInfo.issue == issue) return true;
    }
    return false;
}

class MockWalletSecurityRpc : public dragonx::services::WalletSecurityController::RpcGateway {
public:
    bool encryptResult = true;
    std::string encryptError;
    int encryptCalls = 0;
    std::string lastPassphrase;

    bool encryptWallet(const std::string& passphrase, std::string& error) override
    {
        ++encryptCalls;
        lastPassphrase = passphrase;
        error = encryptError;
        return encryptResult;
    }

    bool unlockWallet(const std::string&, int, std::string&) override { return true; }
    bool exportWallet(const std::string&, long, std::string&) override { return true; }
    bool importWallet(const std::string&, long, std::string&) override { return true; }
};

    class MockDaemonLifecycleRuntime : public dragonx::daemon::DaemonController::LifecycleRuntime {
    public:
        std::vector<std::string> calls;
        int deletedItems = 7;
        bool startResult = true;
        std::string rpcStopContext;
        std::string disconnectReason;

        void stopDaemonWithPolicy() override { calls.push_back("stop"); }
        bool startDaemon() override
        {
            calls.push_back("start");
            return startResult;
        }
        int deleteBlockchainData() override
        {
            calls.push_back("delete");
            return deletedItems;
        }
        void resetOutputOffset() override { calls.push_back("reset-output"); }
        void requestRpcStopAndDisconnect(const char* context, const char* reason) override
        {
            calls.push_back("rpc-stop-disconnect");
            rpcStopContext = context ? context : "";
            disconnectReason = reason ? reason : "";
        }
    };

    class MockDaemonLifecycleTask : public dragonx::daemon::DaemonController::LifecycleTaskContext {
    public:
        bool isCancelled = false;
        bool isShuttingDown = false;
        int cancelAfterSleeps = 0;
        int sleeps = 0;
        int sleptMs = 0;

        bool cancelled() const override { return isCancelled; }
        bool shuttingDown() const override { return isShuttingDown; }
        void sleepForMs(int milliseconds) override
        {
            ++sleeps;
            sleptMs += milliseconds;
            if (cancelAfterSleeps > 0 && sleeps >= cancelAfterSleeps) {
                isCancelled = true;
            }
        }
    };

class MockWalletSecurityVault : public dragonx::services::WalletSecurityController::VaultGateway {
public:
    bool storeResult = true;
    int storeCalls = 0;
    std::string lastPin;
    std::string lastPassphrase;

    bool storePin(const std::string& pin, const std::string& passphrase) override
    {
        ++storeCalls;
        lastPin = pin;
        lastPassphrase = passphrase;
        return storeResult;
    }
};

class MockWorkflowRpc : public dragonx::services::WalletSecurityWorkflowExecutor::RpcGateway {
public:
    bool unlockResult = true;
    bool exportResult = true;
    bool stopResult = true;
    int probeSuccessOnCall = 1;
    std::string unlockError;
    std::string exportError;
    std::string stopError;
    std::string probeError;
    int unlockCalls = 0;
    int exportCalls = 0;
    int stopCalls = 0;
    int probeCalls = 0;
    std::string lastPassphrase;
    std::string lastExportFile;

    bool unlockWallet(const std::string& passphrase, int, std::string& error) override
    {
        ++unlockCalls;
        lastPassphrase = passphrase;
        error = unlockError;
        return unlockResult;
    }

    bool exportWallet(const std::string& fileName, long, std::string& error) override
    {
        ++exportCalls;
        lastExportFile = fileName;
        error = exportError;
        return exportResult;
    }

    bool requestDaemonStop(std::string& error) override
    {
        ++stopCalls;
        error = stopError;
        return stopResult;
    }

    bool probeDaemon(std::string& error) override
    {
        ++probeCalls;
        error = probeError;
        return probeCalls >= probeSuccessOnCall;
    }
};

class MockWorkflowFiles : public dragonx::services::WalletSecurityWorkflowExecutor::FileGateway {
public:
    std::string dir = "/tmp/dragonx/";
    bool backupResult = true;
    std::string backupError;
    int backupCalls = 0;

    std::string dataDir() override { return dir; }
    bool backupEncryptedWallet(const dragonx::services::WalletSecurityWorkflowExecutor::WalletFilePlan&,
                               std::string& error) override
    {
        ++backupCalls;
        error = backupError;
        return backupResult;
    }
};

class MockWorkflowDaemon : public dragonx::services::WalletSecurityWorkflowExecutor::DaemonGateway {
public:
    bool usingEmbedded = true;
    bool isCancelled = false;
    bool isShuttingDown = false;
    int cancelAfterSleeps = 0;
    int sleepCalls = 0;
    int sleptMs = 0;
    int stopCalls = 0;
    int startCalls = 0;

    bool isUsingEmbeddedDaemon() const override { return usingEmbedded; }
    void stopEmbeddedDaemon() override { ++stopCalls; }
    bool startEmbeddedDaemon() override { ++startCalls; return true; }
    bool cancelled() const override { return isCancelled; }
    bool shuttingDown() const override { return isShuttingDown; }
    void sleepForMs(int milliseconds) override
    {
        ++sleepCalls;
        sleptMs += milliseconds;
        if (cancelAfterSleeps > 0 && sleepCalls >= cancelAfterSleeps) {
            isCancelled = true;
        }
    }
};

class MockWorkflowImporter : public dragonx::services::WalletSecurityWorkflowExecutor::ImportGateway {
public:
    bool result = true;
    std::string importError;
    int importCalls = 0;
    std::string lastExportPath;

    bool importWallet(const std::string& exportPath, long, std::string& error) override
    {
        ++importCalls;
        lastExportPath = exportPath;
        error = importError;
        return result;
    }
};

class FakeRefreshWorker {
public:
    std::size_t reportedPending = 0;
    std::vector<dragonx::rpc::RPCWorker::WorkFn> tasks;

    std::size_t pendingTaskCount() const { return reportedPending; }
    void post(dragonx::rpc::RPCWorker::WorkFn work)
    {
        tasks.push_back(std::move(work));
    }
    dragonx::rpc::RPCWorker::MainCb runNext()
    {
        auto work = std::move(tasks.front());
        tasks.erase(tasks.begin());
        return work();
    }
    dragonx::rpc::RPCWorker::MainCb runAt(std::size_t index)
    {
        auto work = std::move(tasks[index]);
        tasks.erase(tasks.begin() + static_cast<std::ptrdiff_t>(index));
        return work();
    }
};

class MockRefreshRpc : public dragonx::services::NetworkRefreshService::RefreshRpcGateway {
public:
    struct RecordedCall {
        std::string method;
        nlohmann::json params;
    };

    struct Response {
        nlohmann::json value;
        bool throws = false;
        std::string error = "mock rpc failure";
    };

    std::vector<RecordedCall> calls;
    std::unordered_map<std::string, std::deque<Response>> responses;

    void addResponse(const std::string& method, nlohmann::json value)
    {
        responses[method].push_back({std::move(value), false, {}});
    }

    void addFailure(const std::string& method, std::string error = "mock rpc failure")
    {
        responses[method].push_back({nlohmann::json(), true, std::move(error)});
    }

    nlohmann::json call(const std::string& method, const nlohmann::json& params) override
    {
        calls.push_back({method, params});
        auto& queue = responses[method];
        if (queue.empty()) {
            throw std::runtime_error("unexpected rpc call: " + method);
        }
        auto response = std::move(queue.front());
        queue.pop_front();
        if (response.throws) throw std::runtime_error(response.error);
        return response.value;
    }

    std::vector<std::string> methodNames() const
    {
        std::vector<std::string> names;
        names.reserve(calls.size());
        for (const auto& call : calls) names.push_back(call.method);
        return names;
    }
};

void testConnectionConfig()
{
    using dragonx::rpc::AuthSource;
    using dragonx::rpc::Connection;

    fs::path dir = makeTempDir();
    fs::path conf = dir / "DRAGONX.conf";
    {
        std::ofstream out(conf);
        out << "rpcuser=alice\n"
            << "rpcpassword = secret \r\n"
            << "rpcconnect=rpc.example.test\n"
            << "rpcport=12345\n"
            << "rpctls=yes\n";
    }

    auto config = Connection::parseConfFile(conf.string());
    EXPECT_EQ(config.rpcuser, std::string("alice"));
    EXPECT_EQ(config.rpcpassword, std::string("secret"));
    EXPECT_EQ(config.host, std::string("rpc.example.test"));
    EXPECT_EQ(config.port, std::string("12345"));
    EXPECT_TRUE(config.use_tls);
    EXPECT_EQ(config.auth_source, AuthSource::ConfigFile);
    EXPECT_FALSE(Connection::usesPlaintextRemote(config));

    fs::path tlsConf = dir / "tls.conf";
    {
        std::ofstream out(tlsConf);
        out << "rpcuser=bob\n"
            << "rpcpassword=remote-secret\n"
            << "rpcconnect=198.51.100.10\n"
            << "rpcssl=1\n";
    }
    auto tlsConfig = Connection::parseConfFile(tlsConf.string());
    EXPECT_TRUE(tlsConfig.use_tls);
    EXPECT_FALSE(Connection::usesPlaintextRemote(tlsConfig));

    config.use_tls = false;
    EXPECT_TRUE(Connection::usesPlaintextRemote(config));
    config.host = "127.4.5.6";
    EXPECT_FALSE(Connection::usesPlaintextRemote(config));
    config.host = "[::1]";
    EXPECT_FALSE(Connection::usesPlaintextRemote(config));

    fs::path cookieDir = dir / "cookie";
    fs::create_directories(cookieDir);
    {
        std::ofstream out(cookieDir / ".cookie");
        out << "__cookie__:cookie-secret\r\n";
    }

    dragonx::rpc::ConnectionConfig cookieBase;
    cookieBase.host = "rpc.example.test";
    cookieBase.hush_dir = cookieDir.string();
    cookieBase.auth_source = AuthSource::ConfigFile;
    cookieBase.use_tls = true;

    dragonx::rpc::ConnectionConfig cookieConfig;
    EXPECT_TRUE(Connection::buildCookieAuthConfig(cookieBase, cookieConfig));
    EXPECT_EQ(cookieConfig.rpcuser, std::string("__cookie__"));
    EXPECT_EQ(cookieConfig.rpcpassword, std::string("cookie-secret"));
    EXPECT_EQ(cookieConfig.auth_source, AuthSource::Cookie);
    EXPECT_TRUE(cookieConfig.use_tls);

    fs::remove_all(dir);
}

void testPaymentUri()
{
    std::string taddr = "R" + std::string(33, 'a');
    auto parsed = dragonx::util::parsePaymentURI(
        "drgx:" + taddr + "?amount=1.25000000&label=Main+Wallet&memo=hello%20there&message=thanks");

    EXPECT_TRUE(parsed.valid);
    EXPECT_EQ(parsed.address, taddr);
    EXPECT_NEAR(parsed.amount, 1.25, 0.00000001);
    EXPECT_EQ(parsed.label, std::string("Main Wallet"));
    EXPECT_EQ(parsed.memo, std::string("hello there"));
    EXPECT_EQ(parsed.message, std::string("thanks"));

    std::string zaddr = "zs" + std::string(76, 'b');
    auto zparsed = dragonx::util::parsePaymentURI("hush://" + zaddr + "?amt=0.5");
    EXPECT_TRUE(zparsed.valid);
    EXPECT_NEAR(zparsed.amount, 0.5, 0.00000001);

    auto invalid = dragonx::util::parsePaymentURI("drgx:" + taddr + "?amount=-1");
    EXPECT_FALSE(invalid.valid);
    EXPECT_EQ(invalid.error, std::string("Invalid negative amount"));
}

void testAmountFormatting()
{
    EXPECT_EQ(dragonx::util::formatAmountFixed(1.0), std::string("1.00000000"));
    EXPECT_EQ(dragonx::util::formatAmountFixed(0.1), std::string("0.10000000"));
    EXPECT_EQ(dragonx::util::formatAmountFixed(0.000000019), std::string("0.00000002"));
    EXPECT_EQ(dragonx::util::formatAmountFixed(12.3456, 2), std::string("12.35"));
}

void testSpendableFiltering()
{
    std::vector<dragonx::AddressInfo> addresses;
    addresses.push_back({"zs-view", 50.0, "shielded", false});
    addresses.push_back({"zs-low", 2.0, "shielded", true});
    addresses.push_back({"R-zero", 0.0, "transparent", true});
    addresses.push_back({"R-high", 5.0, "transparent", true});

    EXPECT_EQ(dragonx::bestSpendableAddressIndex(addresses), 3);

    auto indices = dragonx::sortedSpendableAddressIndices(addresses);
    EXPECT_EQ(indices.size(), static_cast<size_t>(2));
    EXPECT_EQ(indices[0], static_cast<size_t>(3));
    EXPECT_EQ(indices[1], static_cast<size_t>(1));

    auto includeZero = dragonx::sortedSpendableAddressIndices(addresses, false);
    EXPECT_EQ(includeZero.size(), static_cast<size_t>(3));
}

void testRefreshScheduler()
{
    using dragonx::services::RefreshScheduler;
    using Timer = RefreshScheduler::Timer;

    RefreshScheduler scheduler;
    scheduler.applyPage(dragonx::ui::NavPage::Overview);
    EXPECT_NEAR(scheduler.intervals().core, 2.0, 0.0001);
    EXPECT_NEAR(scheduler.intervals().peers, 0.0, 0.0001);

    scheduler.tick(1.99f);
    EXPECT_FALSE(scheduler.isDue(Timer::Core));
    scheduler.tick(0.02f);
    EXPECT_TRUE(scheduler.consumeDue(Timer::Core));
    EXPECT_FALSE(scheduler.isDue(Timer::Core));

    scheduler.markWalletMutationRefresh();
    EXPECT_TRUE(scheduler.isDue(Timer::Core));
    EXPECT_TRUE(scheduler.isDue(Timer::Transactions));
    EXPECT_TRUE(scheduler.isDue(Timer::Addresses));
    EXPECT_FALSE(scheduler.isDue(Timer::Peers));

    scheduler.applyPage(dragonx::ui::NavPage::Peers);
    scheduler.markDue(Timer::Peers);
    EXPECT_TRUE(scheduler.consumeDue(Timer::Peers));

    scheduler.applyPage(dragonx::ui::NavPage::Console);
    EXPECT_NEAR(scheduler.intervals().core, 10.0, 0.0001);
    EXPECT_NEAR(scheduler.intervals().transactions, 30.0, 0.0001);
    EXPECT_NEAR(scheduler.intervals().addresses, 30.0, 0.0001);
    EXPECT_NEAR(scheduler.intervals().peers, 0.0, 0.0001);

    EXPECT_FALSE(scheduler.isDue(Timer::Price));
    scheduler.markDue(Timer::Price);
    EXPECT_TRUE(scheduler.consumeDue(Timer::Price));

    EXPECT_FALSE(scheduler.shouldRefreshTransactions(100, 100, false));
    EXPECT_TRUE(scheduler.shouldRefreshTransactions(-1, 100, false));
    EXPECT_TRUE(scheduler.shouldRefreshTransactions(99, 100, false));
    EXPECT_TRUE(scheduler.shouldRefreshTransactions(100, 100, true));
    scheduler.tick(RefreshScheduler::kTxMaxAge);
    EXPECT_FALSE(scheduler.shouldRefreshTransactions(100, 100, false));
    EXPECT_TRUE(scheduler.isDue(Timer::TxAge));
}

void testNetworkRefreshService()
{
    using dragonx::services::NetworkRefreshService;
    using Job = NetworkRefreshService::Job;
    using Timer = NetworkRefreshService::Timer;

    NetworkRefreshService service;
    service.applyPage(dragonx::ui::NavPage::Peers);
    service.markImmediateRefresh();
    EXPECT_TRUE(service.consumeDue(Timer::Core));
    EXPECT_TRUE(service.consumeDue(Timer::Transactions));
    EXPECT_TRUE(service.consumeDue(Timer::Addresses));
    EXPECT_TRUE(service.consumeDue(Timer::Peers));

    EXPECT_TRUE(service.beginJob(Job::Core));
    EXPECT_TRUE(service.jobInProgress(Job::Core));
    EXPECT_FALSE(service.beginJob(Job::Core));
    service.finishJob(Job::Core);
    EXPECT_FALSE(service.jobInProgress(Job::Core));
    EXPECT_TRUE(service.beginJob(Job::Core));
    service.resetJobs();
    EXPECT_FALSE(service.jobInProgress(Job::Core));
    EXPECT_TRUE(service.beginJob(Job::ConnectionInit));
    EXPECT_TRUE(service.jobInProgress(Job::ConnectionInit));
    service.finishJob(Job::ConnectionInit);
    EXPECT_FALSE(service.jobInProgress(Job::ConnectionInit));

    auto cancelOld = service.beginDispatch(Job::Core);
    EXPECT_TRUE(cancelOld.accepted);
    service.resetJobs();
    auto cancelNew = service.beginDispatch(Job::Core);
    EXPECT_TRUE(cancelNew.accepted);
    service.cancelDispatch(cancelOld);
    service.cancelDispatch(cancelNew);
    EXPECT_EQ(service.stats(Job::Core).staleCallbacks, static_cast<uint64_t>(1));

    auto ticket = service.beginDispatch(Job::Addresses, 1, 4);
    EXPECT_TRUE(ticket.accepted);
    auto busyTicket = service.beginDispatch(Job::Addresses, 1, 4);
    EXPECT_FALSE(busyTicket.accepted);
    auto stats = service.stats(Job::Addresses);
    EXPECT_EQ(stats.started, static_cast<uint64_t>(1));
    EXPECT_EQ(stats.skippedInFlight, static_cast<uint64_t>(1));
    EXPECT_EQ(stats.lastQueueDepth, static_cast<size_t>(1));
    EXPECT_TRUE(service.completeDispatch(ticket));
    stats = service.stats(Job::Addresses);
    EXPECT_EQ(stats.finished, static_cast<uint64_t>(1));

    auto pressureTicket = service.beginDispatch(Job::Mining, 3, 3);
    EXPECT_FALSE(pressureTicket.accepted);
    stats = service.stats(Job::Mining);
    EXPECT_EQ(stats.skippedQueuePressure, static_cast<uint64_t>(1));
    EXPECT_EQ(stats.lastQueueDepth, static_cast<size_t>(3));

    auto staleTicket = service.beginDispatch(Job::Peers);
    EXPECT_TRUE(staleTicket.accepted);
    service.resetJobs();
    auto newerTicket = service.beginDispatch(Job::Peers);
    EXPECT_TRUE(newerTicket.accepted);
    EXPECT_FALSE(service.completeDispatch(staleTicket));
    service.cancelDispatch(newerTicket);
    stats = service.stats(Job::Peers);
    EXPECT_EQ(stats.started, static_cast<uint64_t>(2));
    EXPECT_EQ(stats.staleCallbacks, static_cast<uint64_t>(1));

    FakeRefreshWorker worker;
    int workerRuns = 0;
    int mainRuns = 0;
    auto enqueued = service.enqueue(Job::Price, worker, [&]() -> dragonx::rpc::RPCWorker::MainCb {
        ++workerRuns;
        return [&]() { ++mainRuns; };
    }, 3);
    EXPECT_TRUE(enqueued.enqueued);
    EXPECT_TRUE(service.jobInProgress(Job::Price));
    EXPECT_EQ(worker.tasks.size(), static_cast<size_t>(1));
    auto mainCallback = worker.runNext();
    EXPECT_EQ(workerRuns, 1);
    mainCallback();
    EXPECT_EQ(mainRuns, 1);
    EXPECT_FALSE(service.jobInProgress(Job::Price));
    stats = service.stats(Job::Price);
    EXPECT_EQ(stats.finished, static_cast<uint64_t>(1));

    auto nullMainEnqueued = service.enqueue(Job::Price, worker, []() -> dragonx::rpc::RPCWorker::MainCb {
        return nullptr;
    }, 3);
    EXPECT_TRUE(nullMainEnqueued.enqueued);
    EXPECT_TRUE(service.jobInProgress(Job::Price));
    auto nullMainCallback = worker.runNext();
    EXPECT_TRUE(static_cast<bool>(nullMainCallback));
    nullMainCallback();
    EXPECT_FALSE(service.jobInProgress(Job::Price));
    stats = service.stats(Job::Price);
    EXPECT_EQ(stats.finished, static_cast<uint64_t>(2));

    worker.reportedPending = 5;
    auto unboundedPrice = service.enqueue(Job::Price, worker, []() -> dragonx::rpc::RPCWorker::MainCb {
        return []() {};
    }, 0);
    EXPECT_TRUE(unboundedPrice.enqueued);
    EXPECT_EQ(unboundedPrice.queueDepth, static_cast<size_t>(5));
    auto unboundedPriceMain = worker.runNext();
    unboundedPriceMain();
    EXPECT_FALSE(service.jobInProgress(Job::Price));

    worker.reportedPending = 3;
    auto pressured = service.enqueue(Job::Mining, worker, []() -> dragonx::rpc::RPCWorker::MainCb {
        return []() {};
    }, 3);
    EXPECT_FALSE(pressured.enqueued);
    EXPECT_EQ(service.stats(Job::Mining).skippedQueuePressure, static_cast<uint64_t>(2));

    FakeRefreshWorker staleWorker;
    int staleMainRuns = 0;
    auto first = service.enqueue(Job::Encryption, staleWorker, [&]() -> dragonx::rpc::RPCWorker::MainCb {
        return [&]() { ++staleMainRuns; };
    }, 3);
    EXPECT_TRUE(first.enqueued);
    auto oldCallback = staleWorker.runNext();
    service.resetJobs();
    auto second = service.enqueue(Job::Encryption, staleWorker, [&]() -> dragonx::rpc::RPCWorker::MainCb {
        return [&]() { ++staleMainRuns; };
    }, 3);
    EXPECT_TRUE(second.enqueued);
    oldCallback();
    EXPECT_EQ(staleMainRuns, 0);
    auto freshCallback = staleWorker.runNext();
    freshCallback();
    EXPECT_EQ(staleMainRuns, 1);
    EXPECT_EQ(service.stats(Job::Encryption).staleCallbacks, static_cast<uint64_t>(1));

    FakeRefreshWorker orderingWorker;
    std::vector<std::string> callbackOrder;
    auto orderedCore = service.enqueue(Job::Core, orderingWorker, [&]() -> dragonx::rpc::RPCWorker::MainCb {
        callbackOrder.push_back("core-work");
        return [&]() { callbackOrder.push_back("core-main"); };
    }, 3);
    auto orderedPrice = service.enqueue(Job::Price, orderingWorker, [&]() -> dragonx::rpc::RPCWorker::MainCb {
        callbackOrder.push_back("price-work");
        return [&]() { callbackOrder.push_back("price-main"); };
    }, 3);
    EXPECT_TRUE(orderedCore.enqueued);
    EXPECT_TRUE(orderedPrice.enqueued);
    auto priceMain = orderingWorker.runAt(1);
    auto coreMain = orderingWorker.runAt(0);
    priceMain();
    coreMain();
    EXPECT_TRUE(callbackOrder == std::vector<std::string>({"price-work", "core-work", "price-main", "core-main"}));
    EXPECT_FALSE(service.jobInProgress(Job::Core));
    EXPECT_FALSE(service.jobInProgress(Job::Price));

    FakeRefreshWorker reconnectWorker;
    int reconnectMainRuns = 0;
    auto staleBefore = service.stats(Job::Transactions).staleCallbacks;
    auto oldTransactions = service.enqueue(Job::Transactions, reconnectWorker, [&]() -> dragonx::rpc::RPCWorker::MainCb {
        return [&]() { ++reconnectMainRuns; };
    }, 3);
    EXPECT_TRUE(oldTransactions.enqueued);
    auto oldTransactionsMain = reconnectWorker.runNext();
    service.resetJobs();
    auto freshTransactions = service.enqueue(Job::Transactions, reconnectWorker, [&]() -> dragonx::rpc::RPCWorker::MainCb {
        return [&]() { ++reconnectMainRuns; };
    }, 3);
    EXPECT_TRUE(freshTransactions.enqueued);
    auto freshTransactionsMain = reconnectWorker.runNext();
    freshTransactionsMain();
    oldTransactionsMain();
    EXPECT_EQ(reconnectMainRuns, 1);
    EXPECT_EQ(service.stats(Job::Transactions).staleCallbacks, staleBefore + 1);
    EXPECT_FALSE(service.jobInProgress(Job::Transactions));
}

void testNetworkRefreshSnapshotHelpers()
{
    using Refresh = dragonx::services::NetworkRefreshService;
    using nlohmann::json;

    auto shielded = Refresh::buildShieldedAddressInfo("zs-viewonly", json{{"ismine", false}}, true);
    EXPECT_EQ(shielded.address, std::string("zs-viewonly"));
    EXPECT_EQ(shielded.type, std::string("shielded"));
    EXPECT_FALSE(shielded.has_spending_key);

    auto legacyShielded = Refresh::buildShieldedAddressInfo("zs-legacy", json::object(), false);
    EXPECT_TRUE(legacyShielded.has_spending_key);

    auto transparentAddresses = Refresh::parseTransparentAddressList(json::array({"R-one", "R-two"}));
    EXPECT_EQ(transparentAddresses.size(), static_cast<size_t>(2));
    EXPECT_EQ(transparentAddresses[0].type, std::string("transparent"));

    std::vector<dragonx::AddressInfo> balanceAddresses = {shielded, legacyShielded};
    Refresh::applyShieldedBalancesFromUnspent(balanceAddresses, json::array({
        json{{"address", "zs-viewonly"}, {"amount", 1.25}},
        json{{"address", "zs-viewonly"}, {"amount", 0.75}},
        json{{"address", "zs-legacy"}, {"amount", 3.0}}
    }));
    EXPECT_NEAR(balanceAddresses[0].balance, 2.0, 0.00000001);
    EXPECT_NEAR(balanceAddresses[1].balance, 3.0, 0.00000001);

    std::vector<dragonx::TransactionInfo> transactions;
    std::set<std::string> knownTxids;
    Refresh::appendTransparentTransactions(transactions, knownTxids, json::array({
        json{{"txid", "transparent-a"}, {"category", "receive"}, {"amount", 1.5},
             {"timereceived", 100}, {"confirmations", 2}, {"address", "R-one"}},
        json{{"txid", "transparent-b"}, {"category", "send"}, {"amount", -0.25},
             {"time", 300}, {"confirmations", 12}}
    }));
    EXPECT_EQ(transactions.size(), static_cast<size_t>(2));
    EXPECT_EQ(knownTxids.count("transparent-a"), static_cast<size_t>(1));
    EXPECT_EQ(transactions[0].timestamp, static_cast<int64_t>(100));
    EXPECT_EQ(transactions[1].timestamp, static_cast<int64_t>(300));

    Refresh::appendShieldedReceivedTransactions(transactions, knownTxids, "zs-viewonly", json::array({
        json{{"txid", "transparent-a"}, {"amount", 9.0}, {"confirmations", 1}},
        json{{"txid", "shielded-change"}, {"change", true}, {"amount", 2.0}},
        json{{"txid", "shielded-receive"}, {"amount", 4.0}, {"confirmations", 5},
             {"time", 200}, {"memoStr", "hello"}}
    }));
    EXPECT_EQ(transactions.size(), static_cast<size_t>(3));
    EXPECT_EQ(knownTxids.count("shielded-receive"), static_cast<size_t>(1));
    EXPECT_EQ(transactions.back().address, std::string("zs-viewonly"));
    EXPECT_EQ(transactions.back().memo, std::string("hello"));

    Refresh::sortTransactionsNewestFirst(transactions);
    EXPECT_EQ(transactions.front().txid, std::string("transparent-b"));
    EXPECT_EQ(transactions.back().txid, std::string("transparent-a"));

    dragonx::WalletState state;
    state.z_addresses.push_back(shielded);
    dragonx::AddressInfo emptyShielded;
    emptyShielded.type = "shielded";
    state.z_addresses.push_back(emptyShielded);
    dragonx::TransactionInfo oldTransaction;
    oldTransaction.txid = "old-complete";
    oldTransaction.confirmations = 7;
    oldTransaction.timestamp = 1000;
    state.transactions.push_back(oldTransaction);

    Refresh::TransactionViewCache viewCache;
    viewCache["cached-view"].from_address = "zs-from";
    std::unordered_set<std::string> sendTxids{"pending-send"};
    auto snapshot = Refresh::buildTransactionRefreshSnapshot(state, viewCache, sendTxids);
    EXPECT_EQ(snapshot.shieldedAddresses.size(), static_cast<size_t>(1));
    EXPECT_EQ(snapshot.shieldedAddresses[0], std::string("zs-viewonly"));
    EXPECT_EQ(snapshot.fullyEnrichedTxids.count("cached-view"), static_cast<size_t>(1));
    EXPECT_EQ(snapshot.fullyEnrichedTxids.count("old-complete"), static_cast<size_t>(1));
    EXPECT_EQ(snapshot.viewTxCache.size(), static_cast<size_t>(1));
    EXPECT_EQ(snapshot.sendTxids.count("pending-send"), static_cast<size_t>(1));
}

void testNetworkRefreshRpcCollectors()
{
    using Refresh = dragonx::services::NetworkRefreshService;
    using nlohmann::json;

    MockRefreshRpc warmupRpc;
    warmupRpc.addResponse("getinfo", json{
        {"version", 120000},
        {"protocolversion", 170002},
        {"blocks", 12},
        {"longestchain", 15},
        {"notarized", 9}
    });
    auto warmup = Refresh::collectWarmupPollResult(warmupRpc);
    EXPECT_TRUE(warmupRpc.methodNames() == std::vector<std::string>({"getinfo"}));
    EXPECT_EQ(warmupRpc.calls[0].params, json::array());
    EXPECT_TRUE(warmup.ready);
    EXPECT_TRUE(warmup.info.ok);
    EXPECT_EQ(*warmup.info.blocks, 12);
    EXPECT_EQ(*warmup.info.longestChain, 15);
    EXPECT_EQ(warmup.errorMessage, std::string());

    MockRefreshRpc warmupFailureRpc;
    warmupFailureRpc.addFailure("getinfo", "Loading block index");
    auto warmupFailure = Refresh::collectWarmupPollResult(warmupFailureRpc);
    EXPECT_TRUE(warmupFailureRpc.methodNames() == std::vector<std::string>({"getinfo"}));
    EXPECT_EQ(warmupFailureRpc.calls[0].params, json::array());
    EXPECT_FALSE(warmupFailure.ready);
    EXPECT_FALSE(warmupFailure.info.ok);
    EXPECT_EQ(warmupFailure.errorMessage, std::string("Loading block index"));

    MockRefreshRpc connectionRpc;
    connectionRpc.addResponse("getinfo", json{
        {"version", 120001},
        {"protocolversion", 170003},
        {"p2pport", 21769},
        {"longestchain", 250},
        {"blocks", 240},
        {"notarized", 230}
    });
    connectionRpc.addResponse("getwalletinfo", json{{"unlocked_until", 0}});
    auto connection = Refresh::collectConnectionInitResult(connectionRpc);
    EXPECT_TRUE(connectionRpc.methodNames() == std::vector<std::string>({
        "getinfo", "getwalletinfo"
    }));
    EXPECT_EQ(connectionRpc.calls[0].params, json::array());
    EXPECT_EQ(connectionRpc.calls[1].params, json::array());
    EXPECT_TRUE(connection.info.ok);
    EXPECT_EQ(*connection.info.daemonVersion, 120001);
    EXPECT_TRUE(connection.encryption.ok);
    EXPECT_TRUE(connection.encryption.encrypted);
    EXPECT_EQ(connection.encryption.unlockedUntil, static_cast<int64_t>(0));

    MockRefreshRpc connectionPrefetchRpc;
    connectionPrefetchRpc.addFailure("getinfo", "daemon info unavailable");
    connectionPrefetchRpc.addResponse("getwalletinfo", json{{"unlocked_until", 99}});
    auto prefetch = Refresh::collectConnectionInitResult(connectionPrefetchRpc);
    EXPECT_TRUE(connectionPrefetchRpc.methodNames() == std::vector<std::string>({
        "getinfo", "getwalletinfo"
    }));
    EXPECT_FALSE(prefetch.info.ok);
    EXPECT_TRUE(prefetch.encryption.ok);
    EXPECT_TRUE(prefetch.encryption.encrypted);
    EXPECT_EQ(prefetch.encryption.unlockedUntil, static_cast<int64_t>(99));

    MockRefreshRpc connectionReuseRpc;
    connectionReuseRpc.addResponse("getwalletinfo", json{{"unlocked_until", 0}});
    auto reusedInfo = Refresh::parseConnectionInfoResult(json{{"version", 120002}, {"blocks", 241}});
    auto reusedConnection = Refresh::collectConnectionInitResult(connectionReuseRpc, reusedInfo);
    EXPECT_TRUE(connectionReuseRpc.methodNames() == std::vector<std::string>({"getwalletinfo"}));
    EXPECT_TRUE(reusedConnection.info.ok);
    EXPECT_EQ(*reusedConnection.info.daemonVersion, 120002);
    EXPECT_TRUE(reusedConnection.encryption.ok);

    MockRefreshRpc coreRpc;
    coreRpc.addResponse("z_gettotalbalance", json{
        {"private", "3.00000000"},
        {"transparent", "1.25000000"},
        {"total", "4.25000000"}
    });
    coreRpc.addResponse("getblockchaininfo", json{
        {"blocks", 150},
        {"headers", 155},
        {"bestblockhash", "core-best-150"},
        {"verificationprogress", 0.80},
        {"longestchain", 160},
        {"notarized", 145}
    });
    auto core = Refresh::collectCoreRefreshResult(coreRpc);
    EXPECT_TRUE(coreRpc.methodNames() == std::vector<std::string>({
        "z_gettotalbalance", "getblockchaininfo"
    }));
    EXPECT_EQ(coreRpc.calls[0].params, json::array());
    EXPECT_EQ(coreRpc.calls[1].params, json::array());
    EXPECT_TRUE(core.balanceOk);
    EXPECT_TRUE(core.blockchainOk);
    EXPECT_NEAR(*core.totalBalance, 4.25, 0.00000001);
    EXPECT_EQ(*core.blocks, 150);
    EXPECT_EQ(*core.bestBlockHash, std::string("core-best-150"));
    EXPECT_EQ(*core.longestChain, 160);

    MockRefreshRpc coreFallbackRpc;
    coreFallbackRpc.addFailure("z_gettotalbalance", "wallet warming up");
    coreFallbackRpc.addResponse("getblockchaininfo", json{{"blocks", 8}, {"headers", 9}});
    auto partialCore = Refresh::collectCoreRefreshResult(coreFallbackRpc);
    EXPECT_TRUE(coreFallbackRpc.methodNames() == std::vector<std::string>({
        "z_gettotalbalance", "getblockchaininfo"
    }));
    EXPECT_FALSE(partialCore.balanceOk);
    EXPECT_TRUE(partialCore.blockchainOk);
    EXPECT_EQ(*partialCore.blocks, 8);

    MockRefreshRpc peerRpc;
    peerRpc.addResponse("getpeerinfo", json::array({
        json{{"id", 42}, {"addr", "203.0.113.7:21769"}, {"tls_verified", true}}
    }));
    peerRpc.addResponse("listbanned", json::array({
        json{{"address", "198.51.100.9"}, {"banned_until", 4444}}
    }));
    auto peers = Refresh::collectPeerRefreshResult(peerRpc);
    EXPECT_TRUE(peerRpc.methodNames() == std::vector<std::string>({
        "getpeerinfo", "listbanned"
    }));
    EXPECT_EQ(peerRpc.calls[0].params, json::array());
    EXPECT_EQ(peerRpc.calls[1].params, json::array());
    EXPECT_EQ(peers.peers.size(), static_cast<size_t>(1));
    EXPECT_EQ(peers.peers[0].id, 42);
    EXPECT_TRUE(peers.peers[0].tls_verified);
    EXPECT_EQ(peers.bannedPeers.size(), static_cast<size_t>(1));
    EXPECT_EQ(peers.bannedPeers[0].address, std::string("198.51.100.9"));

    MockRefreshRpc peerPartialRpc;
    peerPartialRpc.addFailure("getpeerinfo", "peer table unavailable");
    peerPartialRpc.addResponse("listbanned", json::array({
        json{{"address", "203.0.113.8"}, {"banned_until", 5555}}
    }));
    auto partialPeers = Refresh::collectPeerRefreshResult(peerPartialRpc);
    EXPECT_TRUE(peerPartialRpc.methodNames() == std::vector<std::string>({
        "getpeerinfo", "listbanned"
    }));
    EXPECT_EQ(partialPeers.peers.size(), static_cast<size_t>(0));
    EXPECT_EQ(partialPeers.bannedPeers.size(), static_cast<size_t>(1));

    MockRefreshRpc miningSlowRpc;
    miningSlowRpc.addResponse("getlocalsolps", json(72.5));
    miningSlowRpc.addResponse("getmininginfo", json{
        {"generate", true},
        {"genproclimit", 6},
        {"blocks", 321},
        {"difficulty", 19.75},
        {"networkhashps", 1250.0},
        {"chain", "main"}
    });
    auto miningSlow = Refresh::collectMiningRefreshResult(miningSlowRpc, 128.0, true);
    EXPECT_TRUE(miningSlowRpc.methodNames() == std::vector<std::string>({
        "getlocalsolps", "getmininginfo"
    }));
    EXPECT_EQ(miningSlowRpc.calls[0].params, json::array());
    EXPECT_EQ(miningSlowRpc.calls[1].params, json::array());
    EXPECT_NEAR(*miningSlow.localHashrate, 72.5, 0.00000001);
    EXPECT_TRUE(miningSlow.miningOk);
    EXPECT_TRUE(*miningSlow.generate);
    EXPECT_EQ(*miningSlow.genproclimit, 6);
    EXPECT_NEAR(miningSlow.daemonMemoryMb, 128.0, 0.00000001);

    MockRefreshRpc miningFastRpc;
    miningFastRpc.addResponse("getlocalsolps", json(80.25));
    auto miningFast = Refresh::collectMiningRefreshResult(miningFastRpc, 64.0, false);
    EXPECT_TRUE(miningFastRpc.methodNames() == std::vector<std::string>({
        "getlocalsolps"
    }));
    EXPECT_NEAR(*miningFast.localHashrate, 80.25, 0.00000001);
    EXPECT_FALSE(miningFast.miningOk);
    EXPECT_NEAR(miningFast.daemonMemoryMb, 64.0, 0.00000001);

    MockRefreshRpc miningPartialRpc;
    miningPartialRpc.addFailure("getlocalsolps", "local solps unavailable");
    miningPartialRpc.addResponse("getmininginfo", json{{"generate", false}, {"blocks", 7}});
    auto miningPartial = Refresh::collectMiningRefreshResult(miningPartialRpc, 12.0, true);
    EXPECT_TRUE(miningPartialRpc.methodNames() == std::vector<std::string>({
        "getlocalsolps", "getmininginfo"
    }));
    EXPECT_FALSE(miningPartial.localHashrate.has_value());
    EXPECT_TRUE(miningPartial.miningOk);
    EXPECT_FALSE(*miningPartial.generate);

    MockRefreshRpc miningSlowOnlyRpc;
    miningSlowOnlyRpc.addResponse("getmininginfo", json{{"generate", false}, {"networkhashps", 222.0}});
    auto miningSlowOnly = Refresh::collectMiningRefreshResult(miningSlowOnlyRpc, 7.0, true, false);
    EXPECT_TRUE(miningSlowOnlyRpc.methodNames() == std::vector<std::string>({"getmininginfo"}));
    EXPECT_FALSE(miningSlowOnly.localHashrate.has_value());
    EXPECT_TRUE(miningSlowOnly.miningOk);
    EXPECT_NEAR(*miningSlowOnly.networkHashrate, 222.0, 0.00000001);

    MockRefreshRpc addressRpc;
    addressRpc.addResponse("z_listaddresses", json::array({"zs-one", "zs-two"}));
    addressRpc.addResponse("z_validateaddress", json{{"ismine", true}});
    addressRpc.addResponse("z_validateaddress", json{{"ismine", false}});
    addressRpc.addResponse("z_listunspent", json::array({
        json{{"address", "zs-one"}, {"amount", 1.0}},
        json{{"address", "zs-one"}, {"amount", 0.5}}
    }));
    addressRpc.addResponse("getaddressesbyaccount", json::array({"R-one"}));
    addressRpc.addResponse("listunspent", json::array({
        json{{"address", "R-one"}, {"amount", 2.25}}
    }));

    auto addresses = Refresh::collectAddressRefreshResult(addressRpc);
    EXPECT_TRUE(addressRpc.methodNames() == std::vector<std::string>({
        "z_listaddresses", "z_validateaddress", "z_validateaddress",
        "z_listunspent", "getaddressesbyaccount", "listunspent"
    }));
    EXPECT_EQ(addressRpc.calls[1].params, json::array({"zs-one"}));
    EXPECT_EQ(addressRpc.calls[2].params, json::array({"zs-two"}));
    EXPECT_EQ(addressRpc.calls[4].params, json::array({""}));
    EXPECT_EQ(addresses.shieldedAddresses.size(), static_cast<size_t>(2));
    EXPECT_TRUE(addresses.shieldedAddresses[0].has_spending_key);
    EXPECT_FALSE(addresses.shieldedAddresses[1].has_spending_key);
    EXPECT_NEAR(addresses.shieldedAddresses[0].balance, 1.5, 0.00000001);
    EXPECT_EQ(addresses.transparentAddresses.size(), static_cast<size_t>(1));
    EXPECT_NEAR(addresses.transparentAddresses[0].balance, 2.25, 0.00000001);

    MockRefreshRpc fallbackRpc;
    fallbackRpc.addResponse("z_listaddresses", json::array({"zs-fallback"}));
    fallbackRpc.addFailure("z_validateaddress", "legacy daemon");
    fallbackRpc.addFailure("z_listunspent", "method not found");
    fallbackRpc.addResponse("z_getbalance", json(4.75));
    fallbackRpc.addResponse("getaddressesbyaccount", json::array());
    fallbackRpc.addResponse("listunspent", json::array());
    auto fallbackAddresses = Refresh::collectAddressRefreshResult(fallbackRpc);
    EXPECT_TRUE(fallbackRpc.methodNames() == std::vector<std::string>({
        "z_listaddresses", "z_validateaddress", "z_listunspent",
        "z_getbalance", "getaddressesbyaccount", "listunspent"
    }));
    EXPECT_TRUE(fallbackAddresses.shieldedAddresses[0].has_spending_key);
    EXPECT_NEAR(fallbackAddresses.shieldedAddresses[0].balance, 4.75, 0.00000001);

    dragonx::WalletState cachedAddressState;
    cachedAddressState.z_addresses.push_back({"zs-cached", 0.0, "shielded", false});
    auto addressSnapshot = Refresh::buildAddressRefreshSnapshot(cachedAddressState);
    MockRefreshRpc cachedAddressRpc;
    cachedAddressRpc.addResponse("z_listaddresses", json::array({"zs-cached", "zs-new"}));
    cachedAddressRpc.addResponse("z_validateaddress", json{{"ismine", true}});
    cachedAddressRpc.addResponse("z_listunspent", json::array());
    cachedAddressRpc.addResponse("getaddressesbyaccount", json::array());
    cachedAddressRpc.addResponse("listunspent", json::array());
    auto cachedAddresses = Refresh::collectAddressRefreshResult(cachedAddressRpc, addressSnapshot);
    EXPECT_TRUE(cachedAddressRpc.methodNames() == std::vector<std::string>({
        "z_listaddresses", "z_validateaddress", "z_listunspent",
        "getaddressesbyaccount", "listunspent"
    }));
    EXPECT_FALSE(cachedAddresses.shieldedAddresses[0].has_spending_key);
    EXPECT_TRUE(cachedAddresses.shieldedAddresses[1].has_spending_key);

    Refresh::TransactionRefreshSnapshot snapshot;
    snapshot.shieldedAddresses = {"zs-one", "zs-two"};
    snapshot.sendTxids = {"cached-send", "pending-send"};
    snapshot.fullyEnrichedTxids = {"shielded-receive", "transparent-a"};
    Refresh::TransactionViewCacheEntry cachedEntry;
    cachedEntry.from_address = "zs-cache-from";
    cachedEntry.outgoing_outputs.push_back({"zs-cache-dest", 0.20, "cached memo"});
    snapshot.viewTxCache["cached-send"] = cachedEntry;

    MockRefreshRpc transactionRpc;
    transactionRpc.addResponse("listtransactions", json::array({
        json{{"txid", "transparent-a"}, {"category", "receive"}, {"amount", 1.0},
             {"time", 100}, {"confirmations", 3}, {"address", "R-one"}}
    }));
    transactionRpc.addResponse("z_listreceivedbyaddress", json::array({
        json{{"txid", "shielded-receive"}, {"amount", 2.0}, {"confirmations", 4},
             {"time", 50}, {"memoStr", "shielded memo"}}
    }));
    transactionRpc.addResponse("z_listreceivedbyaddress", json::array());
    transactionRpc.addResponse("z_viewtransaction", json{
        {"spends", json::array({json{{"address", "zs-from"}}})},
        {"outputs", json::array({
            json{{"outgoing", true}, {"address", "zs-dest"}, {"value", 0.40}, {"memoStr", "fresh memo"}}
        })}
    });
    transactionRpc.addResponse("gettransaction", json{{"time", 500}, {"confirmations", 1}});

    auto transactionResult = Refresh::collectTransactionRefreshResult(transactionRpc, snapshot, 321, 4);
    EXPECT_TRUE(transactionRpc.methodNames() == std::vector<std::string>({
        "listtransactions", "z_listreceivedbyaddress", "z_listreceivedbyaddress",
        "z_viewtransaction", "gettransaction"
    }));
    EXPECT_EQ(transactionRpc.calls[1].params, json::array({"zs-one", 0}));
    EXPECT_EQ(transactionRpc.calls[2].params, json::array({"zs-two", 0}));
    EXPECT_EQ(transactionRpc.calls[3].params, json::array({"pending-send"}));
    EXPECT_EQ(transactionRpc.calls[4].params, json::array({"pending-send"}));
    EXPECT_EQ(transactionResult.blockHeight, 321);
    EXPECT_EQ(transactionResult.newViewTxEntries.size(), static_cast<size_t>(1));
    EXPECT_EQ(transactionResult.newViewTxEntries.count("pending-send"), static_cast<size_t>(1));
    EXPECT_EQ(transactionResult.newViewTxEntries.at("pending-send").timestamp, static_cast<int64_t>(500));
    EXPECT_EQ(transactionResult.newViewTxEntries.at("pending-send").confirmations, 1);
    EXPECT_EQ(transactionResult.transactions.size(), static_cast<size_t>(4));
    EXPECT_EQ(transactionResult.transactions.front().txid, std::string("pending-send"));
    EXPECT_EQ(transactionResult.transactions.front().type, std::string("send"));
    EXPECT_NEAR(transactionResult.transactions.front().amount, -0.40, 0.00000001);
    EXPECT_EQ(transactionResult.transactions.front().timestamp, static_cast<int64_t>(500));
    EXPECT_EQ(transactionResult.transactions[1].txid, std::string("transparent-a"));

    Refresh::TransactionRefreshSnapshot cachedOnlySnapshot;
    auto cachedOnlyEntry = cachedEntry;
    cachedOnlyEntry.timestamp = 450;
    cachedOnlyEntry.confirmations = 6;
    cachedOnlySnapshot.viewTxCache["cached-only-send"] = cachedOnlyEntry;
    cachedOnlySnapshot.fullyEnrichedTxids = {"cached-only-send"};
    MockRefreshRpc cachedOnlyRpc;
    cachedOnlyRpc.addResponse("listtransactions", json::array());
    auto cachedOnlyResult = Refresh::collectTransactionRefreshResult(cachedOnlyRpc, cachedOnlySnapshot, 322, 4);
    EXPECT_TRUE(cachedOnlyRpc.methodNames() == std::vector<std::string>({"listtransactions"}));
    EXPECT_EQ(cachedOnlyResult.transactions.size(), static_cast<size_t>(1));
    EXPECT_EQ(cachedOnlyResult.transactions[0].txid, std::string("cached-only-send"));
    EXPECT_EQ(cachedOnlyResult.transactions[0].type, std::string("send"));
    EXPECT_NEAR(cachedOnlyResult.transactions[0].amount, -0.20, 0.00000001);
    EXPECT_EQ(cachedOnlyResult.transactions[0].timestamp, static_cast<int64_t>(450));
    EXPECT_EQ(cachedOnlyResult.transactions[0].confirmations, 6);

    Refresh::TransactionRefreshSnapshot retrySnapshot;
    retrySnapshot.sendTxids = {"retry-send"};
    retrySnapshot.viewTxCache["retry-send"] = Refresh::TransactionViewCacheEntry{};
    retrySnapshot.fullyEnrichedTxids = {"retry-send"};
    MockRefreshRpc retryRpc;
    retryRpc.addResponse("listtransactions", json::array());
    retryRpc.addResponse("z_viewtransaction", json{
        {"spends", json::array({json{{"address", "zs-retry-from"}}})},
        {"outputs", json::array({
            json{{"outgoing", true}, {"address", "zs-retry-dest"}, {"value", 0.55}}
        })}
    });
    retryRpc.addResponse("gettransaction", json{{"time", 650}, {"confirmations", 2}});
    auto retryResult = Refresh::collectTransactionRefreshResult(retryRpc, retrySnapshot, 323, 4);
    EXPECT_TRUE(retryRpc.methodNames() == std::vector<std::string>({
        "listtransactions", "z_viewtransaction", "gettransaction"
    }));
    EXPECT_EQ(retryResult.transactions.size(), static_cast<size_t>(1));
    EXPECT_EQ(retryResult.transactions[0].txid, std::string("retry-send"));
    EXPECT_NEAR(retryResult.transactions[0].amount, -0.55, 0.00000001);
    EXPECT_EQ(retryResult.newViewTxEntries.count("retry-send"), static_cast<size_t>(1));
    EXPECT_EQ(retryResult.newViewTxEntries.at("retry-send").timestamp, static_cast<int64_t>(650));
    EXPECT_EQ(retryResult.newViewTxEntries.at("retry-send").confirmations, 2);

    Refresh::TransactionRefreshSnapshot placeholderSnapshot;
    placeholderSnapshot.sendTxids = {"placeholder-send"};
    MockRefreshRpc placeholderRpc;
    placeholderRpc.addResponse("listtransactions", json::array());
    placeholderRpc.addResponse("z_viewtransaction", json{{"spends", json::array()}, {"outputs", json::array()}});
    placeholderRpc.addResponse("gettransaction", json{
        {"time", 700},
        {"confirmations", 0},
        {"amount", -0.33},
        {"details", json::array({
            json{{"category", "send"}, {"address", "zs-placeholder-dest"}, {"amount", -0.33}}
        })}
    });
    auto placeholderResult = Refresh::collectTransactionRefreshResult(placeholderRpc, placeholderSnapshot, 324, 4);
    EXPECT_TRUE(placeholderRpc.methodNames() == std::vector<std::string>({
        "listtransactions", "z_viewtransaction", "gettransaction"
    }));
    EXPECT_EQ(placeholderResult.transactions.size(), static_cast<size_t>(1));
    EXPECT_EQ(placeholderResult.transactions[0].txid, std::string("placeholder-send"));
    EXPECT_EQ(placeholderResult.transactions[0].type, std::string("send"));
    EXPECT_NEAR(placeholderResult.transactions[0].amount, -0.33, 0.00000001);
    EXPECT_EQ(placeholderResult.transactions[0].timestamp, static_cast<int64_t>(700));
    EXPECT_EQ(placeholderResult.transactions[0].confirmations, 0);
    EXPECT_EQ(placeholderResult.transactions[0].address, std::string("zs-placeholder-dest"));
    EXPECT_EQ(placeholderResult.newViewTxEntries.count("placeholder-send"), static_cast<size_t>(0));

    Refresh::TransactionRefreshSnapshot missingAddressesSnapshot;
    missingAddressesSnapshot.previousTransactions.push_back(dragonx::TransactionInfo{
        "shielded-fallback", "receive", 1.25, 150, 2, "zs-one", "", "memo"
    });
    MockRefreshRpc missingAddressesRpc;
    missingAddressesRpc.addResponse("listtransactions", json::array());
    auto missingAddressesResult = Refresh::collectTransactionRefreshResult(
        missingAddressesRpc, missingAddressesSnapshot, 325, 4);
    EXPECT_TRUE(missingAddressesRpc.methodNames() == std::vector<std::string>({"listtransactions"}));
    EXPECT_EQ(missingAddressesResult.transactions.size(), static_cast<size_t>(1));
    EXPECT_EQ(missingAddressesResult.transactions[0].txid, std::string("shielded-fallback"));
    EXPECT_EQ(missingAddressesResult.transactions[0].type, std::string("receive"));

    Refresh::TransactionRefreshSnapshot pendingOpidSnapshot;
    pendingOpidSnapshot.pendingOpids = {"opid-visible-send"};
    pendingOpidSnapshot.previousTransactions.push_back(dragonx::TransactionInfo{
        "opid-visible-send", "send", -4.25, 170, 0, "R-destination", "R-source", ""
    });
    MockRefreshRpc pendingOpidRpc;
    pendingOpidRpc.addResponse("listtransactions", json::array());
    auto pendingOpidResult = Refresh::collectTransactionRefreshResult(
        pendingOpidRpc, pendingOpidSnapshot, 326, 4);
    EXPECT_TRUE(pendingOpidRpc.methodNames() == std::vector<std::string>({"listtransactions"}));
    EXPECT_EQ(pendingOpidResult.transactions.size(), static_cast<size_t>(1));
    EXPECT_EQ(pendingOpidResult.transactions[0].txid, std::string("opid-visible-send"));
    EXPECT_EQ(pendingOpidResult.transactions[0].type, std::string("send"));
    EXPECT_NEAR(pendingOpidResult.transactions[0].amount, -4.25, 0.00000001);

    Refresh::TransactionRefreshSnapshot partialFailureSnapshot;
    partialFailureSnapshot.shieldedAddresses = {"zs-one"};
    partialFailureSnapshot.previousTransactions.push_back(dragonx::TransactionInfo{
        "old-receive", "receive", 2.50, 140, 8, "zs-one", "", "old memo"
    });
    MockRefreshRpc partialFailureRpc;
    partialFailureRpc.addResponse("listtransactions", json::array({
        json{{"txid", "transparent-b"}, {"category", "receive"}, {"amount", 0.75},
             {"time", 160}, {"confirmations", 2}, {"address", "R-two"}}
    }));
    partialFailureRpc.addFailure("z_listreceivedbyaddress", "temporary receive failure");
    auto partialFailureResult = Refresh::collectTransactionRefreshResult(
        partialFailureRpc, partialFailureSnapshot, 326, 4);
    EXPECT_EQ(partialFailureResult.transactions.size(), static_cast<size_t>(2));
    EXPECT_EQ(partialFailureResult.transactions[0].txid, std::string("transparent-b"));
    EXPECT_EQ(partialFailureResult.transactions[1].txid, std::string("old-receive"));

    Refresh::TransactionRefreshSnapshot pagedSnapshot;
    MockRefreshRpc pagedRpc;
    json firstPage = json::array();
    for (int i = 0; i < 1000; ++i) {
        firstPage.push_back(json{{"txid", "paged-" + std::to_string(i)}, {"category", "receive"},
                                  {"amount", 0.01}, {"time", i}, {"confirmations", 10},
                                  {"address", "R-page"}});
    }
    pagedRpc.addResponse("listtransactions", firstPage);
    pagedRpc.addResponse("listtransactions", json::array({
        json{{"txid", "paged-1000"}, {"category", "receive"}, {"amount", 0.02},
             {"time", 2000}, {"confirmations", 11}, {"address", "R-page"}}
    }));
    auto pagedResult = Refresh::collectTransactionRefreshResult(pagedRpc, pagedSnapshot, 327, 0);
    EXPECT_TRUE(pagedRpc.methodNames() == std::vector<std::string>({"listtransactions", "listtransactions"}));
    EXPECT_EQ(pagedRpc.calls[0].params, json::array({"", 1000, 0}));
    EXPECT_EQ(pagedRpc.calls[1].params, json::array({"", 1000, 1000}));
    EXPECT_EQ(pagedResult.transactions.size(), static_cast<size_t>(1001));
    EXPECT_EQ(pagedResult.transactions[0].txid, std::string("paged-1000"));

    Refresh::TransactionRefreshSnapshot recentSnapshot;
    dragonx::TransactionInfo previousShielded;
    previousShielded.txid = "shielded-old";
    previousShielded.type = "receive";
    previousShielded.address = "zs-one";
    previousShielded.amount = 3.0;
    previousShielded.timestamp = 10;
    dragonx::TransactionInfo previousTransparent;
    previousTransparent.txid = "recent-one";
    previousTransparent.type = "receive";
    previousTransparent.address = "R-one";
    previousTransparent.amount = 1.0;
    previousTransparent.timestamp = 20;
    recentSnapshot.previousTransactions = {previousShielded, previousTransparent};
    MockRefreshRpc recentRpc;
    recentRpc.addResponse("listtransactions", json::array({
        json{{"txid", "recent-one"}, {"category", "receive"}, {"address", "R-one"}, {"amount", 2.0}, {"time", 30}, {"confirmations", 0}},
        json{{"txid", "recent-two"}, {"category", "send"}, {"address", "R-two"}, {"amount", -0.5}, {"time", 40}, {"confirmations", 0}}
    }));
    auto recent = Refresh::collectRecentTransactionRefreshResult(recentRpc, recentSnapshot, 123);
    EXPECT_TRUE(recentRpc.methodNames() == std::vector<std::string>({"listtransactions"}));
    EXPECT_EQ(recentRpc.calls[0].params, json::array({"", 100, 0}));
    EXPECT_EQ(recent.blockHeight, 123);
    EXPECT_EQ(recent.transactions.size(), static_cast<size_t>(3));
    EXPECT_EQ(recent.transactions[0].txid, std::string("recent-two"));
    EXPECT_EQ(recent.transactions[1].txid, std::string("recent-one"));
    EXPECT_NEAR(recent.transactions[1].amount, 2.0, 0.00000001);
    EXPECT_EQ(recent.transactions[2].txid, std::string("shielded-old"));

    Refresh::TransactionRefreshSnapshot recentShieldedProbeSnapshot;
    recentShieldedProbeSnapshot.shieldedAddresses = {"zs-probe-a", "zs-probe-b"};
    recentShieldedProbeSnapshot.shieldedScanStartIndex = 1;
    recentShieldedProbeSnapshot.maxShieldedReceiveScans = 1;
    recentShieldedProbeSnapshot.shieldedScanHeights = {{"zs-probe-a", 600}, {"zs-probe-b", 600}};
    MockRefreshRpc recentShieldedProbeRpc;
    recentShieldedProbeRpc.addResponse("listtransactions", json::array());
    recentShieldedProbeRpc.addResponse("z_listreceivedbyaddress", json::array({
        json{{"txid", "same-tip-shielded"}, {"amount", 1.75}, {"confirmations", 0}, {"time", 170}}
    }));
    auto recentShieldedProbe = Refresh::collectRecentTransactionRefreshResult(
        recentShieldedProbeRpc, recentShieldedProbeSnapshot, 600);
    EXPECT_TRUE(recentShieldedProbeRpc.methodNames() == std::vector<std::string>({
        "listtransactions", "z_listreceivedbyaddress"
    }));
    EXPECT_EQ(recentShieldedProbeRpc.calls[1].params, json::array({"zs-probe-b", 0}));
    EXPECT_EQ(recentShieldedProbe.nextShieldedScanStartIndex, static_cast<size_t>(0));
    EXPECT_EQ(recentShieldedProbe.shieldedAddressesScanned, static_cast<size_t>(1));
    EXPECT_EQ(recentShieldedProbe.shieldedScanHeights.at("zs-probe-b"), 600);
    EXPECT_EQ(recentShieldedProbe.transactions.size(), static_cast<size_t>(1));
    EXPECT_EQ(recentShieldedProbe.transactions[0].txid, std::string("same-tip-shielded"));
    EXPECT_EQ(recentShieldedProbe.transactions[0].confirmations, 0);

    Refresh::TransactionRefreshSnapshot partialShieldedSnapshot;
    partialShieldedSnapshot.shieldedAddresses = {"zs-zero", "zs-one", "zs-two"};
    partialShieldedSnapshot.maxShieldedReceiveScans = 2;
    partialShieldedSnapshot.previousTransactions.push_back(dragonx::TransactionInfo{
        "old-zs-two", "receive", 1.0, 90, 10, "zs-two", "", "memo"
    });
    MockRefreshRpc partialShieldedRpc;
    partialShieldedRpc.addResponse("listtransactions", json::array());
    partialShieldedRpc.addResponse("z_listreceivedbyaddress", json::array({
        json{{"txid", "new-zs-zero"}, {"amount", 0.5}, {"confirmations", 1}, {"time", 100}}
    }));
    partialShieldedRpc.addResponse("z_listreceivedbyaddress", json::array());
    auto partialShielded = Refresh::collectTransactionRefreshResult(
        partialShieldedRpc, partialShieldedSnapshot, 400, 0);
    EXPECT_TRUE(partialShieldedRpc.methodNames() == std::vector<std::string>({
        "listtransactions", "z_listreceivedbyaddress", "z_listreceivedbyaddress"
    }));
    EXPECT_EQ(partialShieldedRpc.calls[1].params, json::array({"zs-zero", 0}));
    EXPECT_EQ(partialShieldedRpc.calls[2].params, json::array({"zs-one", 0}));
    EXPECT_FALSE(partialShielded.shieldedScanComplete);
    EXPECT_EQ(partialShielded.nextShieldedScanStartIndex, static_cast<size_t>(2));
    EXPECT_EQ(partialShielded.shieldedAddressesScanned, static_cast<size_t>(2));
    EXPECT_EQ(partialShielded.transactions.size(), static_cast<size_t>(2));
    EXPECT_EQ(partialShielded.transactions[0].txid, std::string("new-zs-zero"));
    EXPECT_EQ(partialShielded.transactions[1].txid, std::string("old-zs-two"));

    partialShieldedSnapshot.shieldedScanStartIndex = partialShielded.nextShieldedScanStartIndex;
    partialShieldedSnapshot.shieldedScanHeights = partialShielded.shieldedScanHeights;
    MockRefreshRpc finalShieldedRpc;
    finalShieldedRpc.addResponse("listtransactions", json::array());
    finalShieldedRpc.addResponse("z_listreceivedbyaddress", json::array());
    auto finalShielded = Refresh::collectTransactionRefreshResult(
        finalShieldedRpc, partialShieldedSnapshot, 400, 0);
    EXPECT_TRUE(finalShieldedRpc.methodNames() == std::vector<std::string>({
        "listtransactions", "z_listreceivedbyaddress"
    }));
    EXPECT_EQ(finalShieldedRpc.calls[1].params, json::array({"zs-two", 0}));
    EXPECT_TRUE(finalShielded.shieldedScanComplete);
    EXPECT_EQ(finalShielded.nextShieldedScanStartIndex, static_cast<size_t>(0));
    EXPECT_EQ(finalShielded.shieldedScanHeights.at("zs-zero"), 400);
    EXPECT_EQ(finalShielded.shieldedScanHeights.at("zs-one"), 400);
    EXPECT_EQ(finalShielded.shieldedScanHeights.at("zs-two"), 400);

    Refresh::TransactionRefreshSnapshot cachedShieldedSnapshot;
    cachedShieldedSnapshot.shieldedAddresses = {"zs-cached"};
    cachedShieldedSnapshot.shieldedScanHeights = {{"zs-cached", 500}};
    cachedShieldedSnapshot.previousTransactions.push_back(dragonx::TransactionInfo{
        "cached-shielded", "receive", 1.5, 120, 20, "zs-cached", "", "cached memo"
    });
    MockRefreshRpc cachedShieldedRpc;
    cachedShieldedRpc.addResponse("listtransactions", json::array());
    auto cachedShielded = Refresh::collectTransactionRefreshResult(
        cachedShieldedRpc, cachedShieldedSnapshot, 500, 0);
    EXPECT_TRUE(cachedShieldedRpc.methodNames() == std::vector<std::string>({"listtransactions"}));
    EXPECT_TRUE(cachedShielded.shieldedScanComplete);
    EXPECT_EQ(cachedShielded.shieldedAddressesScanned, static_cast<size_t>(0));
    EXPECT_EQ(cachedShielded.transactions.size(), static_cast<size_t>(1));
    EXPECT_EQ(cachedShielded.transactions[0].txid, std::string("cached-shielded"));

    Refresh::TransactionRefreshSnapshot staleProgressSnapshot;
    staleProgressSnapshot.shieldedAddresses = {"zs-current", "zs-stale", "zs-missing"};
    staleProgressSnapshot.shieldedScanHeights = {{"zs-current", 500}, {"zs-stale", 499}};
    staleProgressSnapshot.maxShieldedReceiveScans = 1;
    staleProgressSnapshot.previousTransactions.push_back(dragonx::TransactionInfo{
        "current-shielded", "receive", 0.75, 110, 12, "zs-current", "", ""
    });
    MockRefreshRpc staleProgressRpc;
    staleProgressRpc.addResponse("listtransactions", json::array());
    staleProgressRpc.addResponse("z_listreceivedbyaddress", json::array({
        json{{"txid", "stale-shielded"}, {"amount", 2.0}, {"confirmations", 1}, {"time", 130}}
    }));
    auto staleProgress = Refresh::collectTransactionRefreshResult(
        staleProgressRpc, staleProgressSnapshot, 500, 0);
    EXPECT_TRUE(staleProgressRpc.methodNames() == std::vector<std::string>({
        "listtransactions", "z_listreceivedbyaddress"
    }));
    EXPECT_EQ(staleProgressRpc.calls[1].params, json::array({"zs-stale", 0}));
    EXPECT_FALSE(staleProgress.shieldedScanComplete);
    EXPECT_EQ(staleProgress.nextShieldedScanStartIndex, static_cast<size_t>(2));
    EXPECT_EQ(staleProgress.shieldedAddressesScanned, static_cast<size_t>(1));
    EXPECT_EQ(staleProgress.shieldedScanHeights.at("zs-current"), 500);
    EXPECT_EQ(staleProgress.shieldedScanHeights.at("zs-stale"), 500);
    EXPECT_TRUE(staleProgress.shieldedScanHeights.find("zs-missing") == staleProgress.shieldedScanHeights.end());
    EXPECT_EQ(staleProgress.transactions.size(), static_cast<size_t>(2));
    EXPECT_EQ(staleProgress.transactions[0].txid, std::string("stale-shielded"));
    EXPECT_EQ(staleProgress.transactions[1].txid, std::string("current-shielded"));

    Refresh::TransactionRefreshSnapshot miningSnapshot;
    miningSnapshot.shieldedAddresses = {"zs-mine"};
    miningSnapshot.miningAddresses = {"R-mine", "zs-mine"};
    MockRefreshRpc miningTxRpc;
    miningTxRpc.addResponse("listtransactions", json::array({
        json{{"txid", "transparent-mined"}, {"category", "receive"}, {"amount", 3.0},
             {"time", 220}, {"confirmations", 101}, {"address", "R-mine"}}
    }));
    miningTxRpc.addResponse("z_listreceivedbyaddress", json::array({
        json{{"txid", "shielded-mined"}, {"amount", 4.0}, {"confirmations", 102},
             {"time", 210}, {"memoStr", "pool"}}
    }));
    auto miningTxResult = Refresh::collectTransactionRefreshResult(miningTxRpc, miningSnapshot, 328, 0);
    EXPECT_EQ(miningTxResult.transactions.size(), static_cast<size_t>(2));
    EXPECT_EQ(miningTxResult.transactions[0].type, std::string("mined"));
    EXPECT_EQ(miningTxResult.transactions[1].type, std::string("mined"));
}

void testNetworkRefreshResultModels()
{
    using Refresh = dragonx::services::NetworkRefreshService;
    using nlohmann::json;

    dragonx::WalletState state;

    auto core = Refresh::parseCoreRefreshResult(
        json{{"private", "1.25000000"}, {"transparent", "0.50000000"}, {"total", "1.75000000"}},
        true,
        json{{"blocks", 100}, {"headers", 105}, {"bestblockhash", "apply-best-100"}, {"verificationprogress", 0.75},
             {"longestchain", 110}, {"notarized", 90}},
        true);
    Refresh::applyCoreRefreshResult(state, core, 1234);
    EXPECT_NEAR(state.shielded_balance, 1.25, 0.00000001);
    EXPECT_NEAR(state.transparent_balance, 0.5, 0.00000001);
    EXPECT_NEAR(state.total_balance, 1.75, 0.00000001);
    EXPECT_EQ(state.sync.blocks, 100);
    EXPECT_EQ(state.sync.headers, 105);
    EXPECT_EQ(state.sync.best_blockhash, std::string("apply-best-100"));
    EXPECT_TRUE(state.sync.syncing);
    EXPECT_EQ(state.longestchain, 110);
    EXPECT_EQ(state.notarized, 90);
    EXPECT_EQ(state.last_balance_update, static_cast<int64_t>(1234));

    auto connectionInfo = Refresh::parseConnectionInfoResult(
        json{{"version", 120000}, {"protocolversion", 170002}, {"p2pport", 8233},
             {"longestchain", 0}, {"blocks", 120}, {"notarized", 118}});
    Refresh::applyConnectionInfoResult(state, connectionInfo);
    EXPECT_EQ(state.daemon_version, 120000);
    EXPECT_EQ(state.protocol_version, 170002);
    EXPECT_EQ(state.p2p_port, 8233);
    EXPECT_EQ(state.sync.blocks, 120);
    EXPECT_EQ(state.longestchain, 120);
    EXPECT_EQ(state.notarized, 118);

    auto encrypted = Refresh::parseWalletEncryptionResult(json{{"unlocked_until", 0}});
    Refresh::applyWalletEncryptionResult(state, encrypted);
    EXPECT_TRUE(state.encrypted);
    EXPECT_TRUE(state.locked);
    EXPECT_TRUE(state.encryption_state_known);
    auto unencrypted = Refresh::parseWalletEncryptionResult(json::object());
    Refresh::applyWalletEncryptionResult(state, unencrypted);
    EXPECT_FALSE(state.encrypted);
    EXPECT_FALSE(state.locked);
    EXPECT_EQ(state.unlocked_until, static_cast<int64_t>(0));

    auto mining = Refresh::parseMiningRefreshResult(
        json{{"generate", true}, {"genproclimit", 4}, {"blocks", 101},
             {"difficulty", 12.5}, {"networkhashps", 900.0}, {"chain", "main"}},
        true,
        json(45.0),
        true,
        64.0);
    Refresh::applyMiningRefreshResult(state, mining, 2345);
    EXPECT_TRUE(state.mining.generate);
    EXPECT_EQ(state.mining.genproclimit, 4);
    EXPECT_NEAR(state.mining.localHashrate, 45.0, 0.0001);
    EXPECT_EQ(state.mining.hashrate_history.size(), static_cast<size_t>(1));
    EXPECT_NEAR(state.mining.networkHashrate, 900.0, 0.0001);
    EXPECT_NEAR(state.mining.daemon_memory_mb, 64.0, 0.0001);
    EXPECT_EQ(state.last_mining_update, static_cast<int64_t>(2345));

    auto peers = Refresh::parsePeerRefreshResult(
        json::array({json{{"id", 7}, {"addr", "127.0.0.1:8233"}, {"tls_verified", true}}}),
        json::array({json{{"address", "192.0.2.1"}, {"banned_until", 3456}}}));
    Refresh::applyPeerRefreshResult(state, std::move(peers), 3456);
    EXPECT_EQ(state.peers.size(), static_cast<size_t>(1));
    EXPECT_EQ(state.peers[0].id, 7);
    EXPECT_TRUE(state.peers[0].tls_verified);
    EXPECT_EQ(state.bannedPeers.size(), static_cast<size_t>(1));
    EXPECT_EQ(state.bannedPeers[0].address, std::string("192.0.2.1"));
    EXPECT_EQ(state.last_peer_update, static_cast<int64_t>(3456));

    auto price = Refresh::parseCoinGeckoPriceResponse(
        R"({"dragonx-2":{"usd":0.5,"btc":0.00001,"usd_24h_change":2.5,"usd_24h_vol":1000,"usd_market_cap":50000}})",
        0);
    EXPECT_TRUE(price.has_value());
    if (price) {
        Refresh::markPriceRefreshStarted(state);
        EXPECT_TRUE(state.market.price_loading);
        Refresh::applyPriceRefreshResult(state, *price, std::chrono::steady_clock::now());
        EXPECT_FALSE(state.market.price_loading);
        EXPECT_EQ(state.market.price_error, std::string());
        EXPECT_NEAR(state.market.price_usd, 0.5, 0.00000001);
        EXPECT_NEAR(state.market.price_btc, 0.00001, 0.00000001);
        EXPECT_NEAR(state.market.change_24h, 2.5, 0.0001);
        EXPECT_EQ(state.market.price_history.size(), static_cast<size_t>(1));
    }

    Refresh::markPriceRefreshStarted(state);
    Refresh::applyPriceRefreshFailure(state, "timeout");
    EXPECT_FALSE(state.market.price_loading);
    EXPECT_EQ(state.market.price_error, std::string("timeout"));
    EXPECT_NEAR(state.market.price_usd, 0.5, 0.00000001);

    Refresh::PriceHttpResponse priceHttpOk;
    priceHttpOk.transportOk = true;
    priceHttpOk.httpStatus = 200;
    priceHttpOk.body = R"({"dragonx-2":{"usd":0.75,"btc":0.00002,"usd_24h_change":1.5,"usd_24h_vol":2000,"usd_market_cap":75000}})";
    auto priceHttp = Refresh::parsePriceHttpResponse(priceHttpOk, 0);
    EXPECT_TRUE(priceHttp.price.has_value());
    if (priceHttp.price) {
        EXPECT_NEAR(priceHttp.price->market.price_usd, 0.75, 0.00000001);
        EXPECT_NEAR(priceHttp.price->market.price_btc, 0.00002, 0.00000001);
    }
    EXPECT_EQ(priceHttp.errorMessage, std::string());

    Refresh::PriceHttpResponse priceHttpStatus;
    priceHttpStatus.transportOk = true;
    priceHttpStatus.httpStatus = 429;
    auto priceHttpStatusResult = Refresh::parsePriceHttpResponse(priceHttpStatus, 0);
    EXPECT_FALSE(priceHttpStatusResult.price.has_value());
    EXPECT_EQ(priceHttpStatusResult.errorMessage, std::string("Price fetch failed: OK (HTTP 429)"));

    Refresh::PriceHttpResponse priceHttpTransport;
    priceHttpTransport.transportOk = false;
    priceHttpTransport.httpStatus = 0;
    priceHttpTransport.transportError = "timeout";
    auto priceHttpTransportResult = Refresh::parsePriceHttpResponse(priceHttpTransport, 0);
    EXPECT_FALSE(priceHttpTransportResult.price.has_value());
    EXPECT_EQ(priceHttpTransportResult.errorMessage, std::string("Price fetch failed: timeout (HTTP 0)"));

    Refresh::PriceHttpResponse priceHttpParse;
    priceHttpParse.transportOk = true;
    priceHttpParse.httpStatus = 200;
    priceHttpParse.body = R"({"other-coin":{"usd":1.0}})";
    auto priceHttpParseResult = Refresh::parsePriceHttpResponse(priceHttpParse, 0);
    EXPECT_FALSE(priceHttpParseResult.price.has_value());
    EXPECT_EQ(priceHttpParseResult.errorMessage, std::string("Price fetch returned an unrecognized response"));

    Refresh::AddressRefreshResult addresses;
    dragonx::AddressInfo zAddr;
    zAddr.address = "zs-refresh";
    zAddr.type = "shielded";
    zAddr.balance = 2.0;
    zAddr.has_spending_key = false;
    dragonx::AddressInfo tAddr;
    tAddr.address = "R-refresh";
    tAddr.type = "transparent";
    tAddr.balance = 3.0;
    addresses.shieldedAddresses.push_back(zAddr);
    addresses.transparentAddresses.push_back(tAddr);
    Refresh::applyAddressRefreshResult(state, std::move(addresses));
    EXPECT_EQ(state.z_addresses.size(), static_cast<size_t>(1));
    EXPECT_EQ(state.t_addresses.size(), static_cast<size_t>(1));
    EXPECT_EQ(state.z_addresses[0].address, std::string("zs-refresh"));
    EXPECT_FALSE(state.z_addresses[0].has_spending_key);

    auto viewEntry = Refresh::parseViewTransactionCacheEntry(json{
        {"spends", json::array({json{{"address", "zs-from"}}})},
        {"outputs", json::array({
            json{{"outgoing", true}, {"address", "zs-destination"}, {"value", 0.75}, {"memoStr", "memo"}},
            json{{"outgoing", false}, {"address", "zs-change"}, {"value", 0.25}}
        })}
    });
    EXPECT_EQ(viewEntry.from_address, std::string("zs-from"));
    EXPECT_EQ(viewEntry.outgoing_outputs.size(), static_cast<size_t>(1));
    EXPECT_EQ(viewEntry.outgoing_outputs[0].address, std::string("zs-destination"));

    std::vector<dragonx::TransactionInfo> enrichedTxs;
    dragonx::TransactionInfo baseTx;
    baseTx.txid = "shielded-send";
    baseTx.type = "receive";
    baseTx.confirmations = 7;
    baseTx.timestamp = 111;
    enrichedTxs.push_back(baseTx);
    Refresh::appendViewTransactionOutputs(enrichedTxs, "shielded-send", viewEntry);
    Refresh::appendViewTransactionOutputs(enrichedTxs, "shielded-send", viewEntry);
    EXPECT_EQ(enrichedTxs.size(), static_cast<size_t>(2));
    EXPECT_EQ(enrichedTxs[1].type, std::string("send"));
    EXPECT_EQ(enrichedTxs[1].from_address, std::string("zs-from"));
    EXPECT_NEAR(enrichedTxs[1].amount, -0.75, 0.00000001);
    EXPECT_EQ(enrichedTxs[1].confirmations, 7);
    EXPECT_EQ(enrichedTxs[1].timestamp, static_cast<int64_t>(111));

    Refresh::TransactionRefreshResult transactionResult;
    transactionResult.blockHeight = 222;
    dragonx::TransactionInfo confirmedTx;
    confirmedTx.txid = "confirmed";
    confirmedTx.type = "receive";
    confirmedTx.confirmations = 12;
    confirmedTx.timestamp = 1000;
    dragonx::TransactionInfo pendingTx;
    pendingTx.txid = "pending";
    pendingTx.type = "receive";
    pendingTx.confirmations = 2;
    pendingTx.timestamp = 1001;
    transactionResult.transactions = {pendingTx, confirmedTx};
    transactionResult.newViewTxEntries["shielded-send"] = viewEntry;
    transactionResult.newViewTxEntries["pending-empty"] = Refresh::TransactionViewCacheEntry{};

    Refresh::TransactionViewCache viewCache;
    std::unordered_set<std::string> sendTxids{"shielded-send", "pending", "pending-empty"};
    std::vector<dragonx::TransactionInfo> confirmedCache;
    std::unordered_set<std::string> confirmedIds;
    int confirmedBlock = -1;
    int lastTxBlock = -1;
    Refresh::TransactionCacheUpdate cacheUpdate{
        viewCache,
        sendTxids,
        confirmedCache,
        confirmedIds,
        confirmedBlock,
        lastTxBlock
    };
    Refresh::applyTransactionRefreshResult(state, cacheUpdate, std::move(transactionResult), 4567);
    EXPECT_EQ(state.transactions.size(), static_cast<size_t>(2));
    EXPECT_EQ(state.last_tx_update, static_cast<int64_t>(4567));
    EXPECT_EQ(lastTxBlock, 222);
    EXPECT_EQ(viewCache.size(), static_cast<size_t>(2));
    EXPECT_EQ(sendTxids.count("shielded-send"), static_cast<size_t>(0));
    EXPECT_EQ(sendTxids.count("pending"), static_cast<size_t>(1));
    EXPECT_EQ(sendTxids.count("pending-empty"), static_cast<size_t>(1));
    EXPECT_EQ(confirmedCache.size(), static_cast<size_t>(1));
    EXPECT_EQ(confirmedCache[0].txid, std::string("confirmed"));
    EXPECT_EQ(confirmedIds.count("confirmed"), static_cast<size_t>(1));
    EXPECT_EQ(confirmedBlock, 222);
}

void testOperationStatusPollParsing()
{
    using Refresh = dragonx::services::NetworkRefreshService;
    using nlohmann::json;

    auto parsed = Refresh::parseOperationStatusPoll(json::array({
        json{{"id", "op-success"}, {"status", "success"}, {"result", json{{"txid", "tx-success"}}}},
        json{{"id", "op-failed"}, {"status", "failed"}, {"error", json{{"message", "bad memo"}}}},
        json{{"id", "op-running"}, {"status", "executing"}}
    }), {"op-success", "op-failed", "op-running", "op-stale"});

    EXPECT_TRUE(parsed.anySuccess);
    EXPECT_EQ(parsed.doneOpids.size(), static_cast<size_t>(2));
    EXPECT_EQ(parsed.doneOpids[0], std::string("op-success"));
    EXPECT_EQ(parsed.doneOpids[1], std::string("op-failed"));
    EXPECT_EQ(parsed.successTxids.size(), static_cast<size_t>(1));
    EXPECT_EQ(parsed.successTxids[0], std::string("tx-success"));
    EXPECT_EQ(parsed.successTxidsByOpid.size(), static_cast<size_t>(1));
    EXPECT_EQ(parsed.successTxidsByOpid.at("op-success"), std::string("tx-success"));
    EXPECT_EQ(parsed.failureMessages.size(), static_cast<size_t>(1));
    EXPECT_EQ(parsed.failureMessages[0], std::string("bad memo"));
    EXPECT_EQ(parsed.staleOpids.size(), static_cast<size_t>(1));
    EXPECT_EQ(parsed.staleOpids[0], std::string("op-stale"));

    auto malformed = Refresh::parseOperationStatusPoll(json{{"status", "success"}}, {"op-keep"});
    EXPECT_FALSE(malformed.anySuccess);
    EXPECT_TRUE(malformed.doneOpids.empty());
    EXPECT_TRUE(malformed.staleOpids.empty());
}

void testWalletSecurityController()
{
    using dragonx::services::WalletSecurityController;
    using PinError = WalletSecurityController::PinValidationError;

    WalletSecurityController controller;
    EXPECT_FALSE(controller.hasDeferredEncryption());
    controller.beginDeferredEncryption("secret-passphrase", "1234");
    EXPECT_TRUE(controller.hasDeferredEncryption());

    auto snapshot = controller.deferredEncryption();
    EXPECT_EQ(snapshot.passphrase, std::string("secret-passphrase"));
    EXPECT_EQ(snapshot.pin, std::string("1234"));

    EXPECT_TRUE(controller.shouldAttemptDeferredConnect(0.0));
    EXPECT_FALSE(controller.shouldAttemptDeferredConnect(1.0));
    EXPECT_TRUE(controller.shouldAttemptDeferredConnect(3.1));

    controller.clearDeferredEncryption();
    EXPECT_FALSE(controller.hasDeferredEncryption());
    EXPECT_FALSE(controller.shouldAttemptDeferredConnect(10.0));

    auto valid = WalletSecurityController::validatePinSetup("1234", "1234");
    EXPECT_TRUE(valid.ok);
    auto emptyAllowed = WalletSecurityController::validatePinSetup("", "", true);
    EXPECT_TRUE(emptyAllowed.ok);
    auto mismatch = WalletSecurityController::validatePinSetup("1234", "4321");
    EXPECT_EQ(mismatch.error, PinError::Mismatch);
    auto shortPin = WalletSecurityController::validatePinSetup("123", "123");
    EXPECT_EQ(shortPin.error, PinError::TooShort);
    auto nondigit = WalletSecurityController::validatePinSetup("12a4", "12a4");
    EXPECT_EQ(nondigit.error, PinError::NonDigit);

    EXPECT_EQ(WalletSecurityController::classifyAddress("zs123"), WalletSecurityController::KeyKind::Shielded);
    EXPECT_EQ(WalletSecurityController::classifyAddress("R123"), WalletSecurityController::KeyKind::Transparent);
    EXPECT_EQ(WalletSecurityController::classifyPrivateKey("secret-key"), WalletSecurityController::KeyKind::Shielded);
    EXPECT_EQ(WalletSecurityController::classifyPrivateKey("Kx123"), WalletSecurityController::KeyKind::Transparent);
    EXPECT_EQ(std::string(WalletSecurityController::importSuccessMessage(WalletSecurityController::KeyKind::Shielded)),
              std::string("Z-address key imported successfully. Wallet is rescanning."));
    EXPECT_EQ(WalletSecurityController::decryptExportFileName(42), std::string("obsidiandecryptexport42"));

    MockWalletSecurityRpc rpc;
    MockWalletSecurityVault vault;
    auto encrypted = controller.runDeferredEncryption({"passphrase", "9876"}, rpc, &vault);
    EXPECT_TRUE(encrypted.encrypted);
    EXPECT_TRUE(encrypted.pinProvided);
    EXPECT_TRUE(encrypted.pinStored);
    EXPECT_TRUE(encrypted.restartRequired);
    EXPECT_EQ(rpc.encryptCalls, 1);
    EXPECT_EQ(rpc.lastPassphrase, std::string("passphrase"));
    EXPECT_EQ(vault.storeCalls, 1);
    EXPECT_EQ(vault.lastPin, std::string("9876"));
    EXPECT_EQ(vault.lastPassphrase, std::string("passphrase"));

    MockWalletSecurityRpc failingRpc;
    MockWalletSecurityVault unusedVault;
    failingRpc.encryptResult = false;
    failingRpc.encryptError = "rpc unavailable";
    auto failed = controller.runDeferredEncryption({"bad-passphrase", "1111"}, failingRpc, &unusedVault);
    EXPECT_FALSE(failed.encrypted);
    EXPECT_TRUE(failed.pinProvided);
    EXPECT_FALSE(failed.pinStored);
    EXPECT_FALSE(failed.restartRequired);
    EXPECT_EQ(failed.error, std::string("rpc unavailable"));
    EXPECT_EQ(unusedVault.storeCalls, 0);
}

void testWalletSecurityWorkflow()
{
    using dragonx::services::WalletSecurityWorkflow;
    using DecryptPhase = WalletSecurityWorkflow::DecryptPhase;
    using DecryptStep = WalletSecurityWorkflow::DecryptStep;

    WalletSecurityWorkflow workflow;
    auto start = std::chrono::steady_clock::now();
    EXPECT_EQ(workflow.phase(), DecryptPhase::PassphraseEntry);
    EXPECT_TRUE(workflow.canClose());

    workflow.start(start);
    EXPECT_EQ(workflow.phase(), DecryptPhase::Working);
    EXPECT_EQ(workflow.step(), DecryptStep::Unlock);
    EXPECT_FALSE(workflow.canClose());
    EXPECT_TRUE(workflow.inProgress());

    workflow.advanceTo(DecryptStep::BackupWallet,
                       WalletSecurityWorkflow::stepStatus(DecryptStep::BackupWallet),
                       start + std::chrono::seconds(2));
    EXPECT_EQ(workflow.step(), DecryptStep::BackupWallet);
    EXPECT_EQ(workflow.status(), std::string("Backing up encrypted wallet..."));
    EXPECT_TRUE(WalletSecurityWorkflow::stepIsComplete(workflow.step(), DecryptStep::StopDaemon));
    EXPECT_FALSE(WalletSecurityWorkflow::stepIsComplete(workflow.step(), DecryptStep::RestartDaemon));

    workflow.closeDialogForImport();
    EXPECT_FALSE(workflow.inProgress());
    EXPECT_TRUE(workflow.importActive());
    workflow.finishImport();
    EXPECT_FALSE(workflow.importActive());

    workflow.fail("Daemon failed to restart");
    EXPECT_EQ(workflow.phase(), DecryptPhase::Error);
    EXPECT_TRUE(workflow.canClose());
    EXPECT_EQ(workflow.status(), std::string("Daemon failed to restart"));

    workflow.failEntry("Incorrect passphrase");
    EXPECT_EQ(workflow.phase(), DecryptPhase::PassphraseEntry);
    EXPECT_FALSE(workflow.inProgress());
    EXPECT_EQ(workflow.status(), std::string("Incorrect passphrase"));

    auto plan = WalletSecurityWorkflow::planWalletFiles("/tmp/dragonx/", 1234);
    EXPECT_EQ(plan.exportFile, std::string("obsidiandecryptexport1234"));
    EXPECT_EQ(plan.exportPath, std::string("/tmp/dragonx/obsidiandecryptexport1234"));
    EXPECT_EQ(plan.walletPath, std::string("/tmp/dragonx/wallet.dat"));
    EXPECT_EQ(plan.backupPath, std::string("/tmp/dragonx/wallet.dat.encrypted.bak"));
}

void testWalletSecurityWorkflowExecutor()
{
    using Executor = dragonx::services::WalletSecurityWorkflowExecutor;

    MockWorkflowRpc rpc;
    auto unlock = Executor::unlockWallet("passphrase", rpc);
    EXPECT_TRUE(unlock.ok);
    EXPECT_EQ(rpc.unlockCalls, 1);
    EXPECT_EQ(rpc.lastPassphrase, std::string("passphrase"));

    rpc.unlockResult = false;
    rpc.unlockError = "bad passphrase";
    auto failedUnlock = Executor::unlockWallet("bad", rpc);
    EXPECT_FALSE(failedUnlock.ok);
    EXPECT_TRUE(failedUnlock.passphraseRejected);
    EXPECT_EQ(failedUnlock.error, std::string("bad passphrase"));

    MockWorkflowFiles files;
    auto exported = Executor::exportWallet(rpc, files, 77);
    EXPECT_TRUE(exported.ok);
    EXPECT_EQ(exported.filePlan.exportFile, std::string("obsidiandecryptexport77"));
    EXPECT_EQ(exported.filePlan.exportPath, std::string("/tmp/dragonx/obsidiandecryptexport77"));
    EXPECT_EQ(rpc.lastExportFile, std::string("obsidiandecryptexport77"));

    rpc.exportResult = false;
    rpc.exportError = "disk full";
    auto failedExport = Executor::exportWallet(rpc, files, 78);
    EXPECT_FALSE(failedExport.ok);
    EXPECT_EQ(failedExport.error, std::string("Export failed: disk full"));

    rpc.stopResult = false;
    auto stop = Executor::stopDaemon(rpc);
    EXPECT_TRUE(stop.ok);
    EXPECT_EQ(rpc.stopCalls, 1);

    auto backup = Executor::backupEncryptedWallet(files, exported.filePlan);
    EXPECT_TRUE(backup.ok);
    EXPECT_EQ(files.backupCalls, 1);
    files.backupResult = false;
    files.backupError = "permission denied";
    auto failedBackup = Executor::backupEncryptedWallet(files, exported.filePlan);
    EXPECT_FALSE(failedBackup.ok);
    EXPECT_EQ(failedBackup.error, std::string("Failed to rename wallet.dat: permission denied"));

    MockWorkflowDaemon daemon;
    rpc.probeCalls = 0;
    rpc.probeSuccessOnCall = 3;
    auto restart = Executor::restartDaemonAndWait(daemon, rpc, 200, 100, 5);
    EXPECT_TRUE(restart.ok);
    EXPECT_EQ(daemon.stopCalls, 1);
    EXPECT_EQ(daemon.startCalls, 1);
    EXPECT_EQ(rpc.probeCalls, 3);

    MockWorkflowDaemon cancelledDaemon;
    cancelledDaemon.cancelAfterSleeps = 1;
    rpc.probeCalls = 0;
    auto cancelled = Executor::restartDaemonAndWait(cancelledDaemon, rpc, 200, 100, 5);
    EXPECT_FALSE(cancelled.ok);
    EXPECT_TRUE(cancelled.error.empty());

    MockWorkflowDaemon failedDaemon;
    rpc.probeCalls = 0;
    rpc.probeSuccessOnCall = 99;
    auto failedRestart = Executor::restartDaemonAndWait(failedDaemon, rpc, 0, 0, 2);
    EXPECT_FALSE(failedRestart.ok);
    EXPECT_EQ(failedRestart.error, std::string("Daemon failed to restart"));

    MockWorkflowImporter importer;
    auto imported = Executor::importWallet(importer, exported.filePlan.exportPath);
    EXPECT_TRUE(imported.ok);
    EXPECT_EQ(importer.importCalls, 1);
    EXPECT_EQ(importer.lastExportPath, exported.filePlan.exportPath);
    importer.result = false;
    importer.importError = "rescan failed";
    auto failedImport = Executor::importWallet(importer, exported.filePlan.exportPath);
    EXPECT_FALSE(failedImport.ok);
    EXPECT_EQ(failedImport.error, std::string("Key import failed: rescan failed"));

    int cleanupCalls = 0;
    Executor::cleanupVaultAndPin([&]() { ++cleanupCalls; });
    EXPECT_EQ(cleanupCalls, 1);
}

void testDaemonShutdownPolicy()
{
    using dragonx::daemon::DaemonController;
    using ShutdownAction = DaemonController::ShutdownAction;

    auto noDaemon = DaemonController::evaluateShutdownPolicy(false, false, false, false);
    EXPECT_EQ(noDaemon.action, ShutdownAction::DisconnectOnly);

    auto keepRunning = DaemonController::evaluateShutdownPolicy(true, false, true, false);
    EXPECT_EQ(keepRunning.action, ShutdownAction::DisconnectOnly);
    EXPECT_EQ(std::string(keepRunning.logReason), std::string("keep_daemon_running enabled"));

    auto externalPreserved = DaemonController::evaluateShutdownPolicy(true, true, false, false);
    EXPECT_EQ(externalPreserved.action, ShutdownAction::DisconnectOnly);
    EXPECT_EQ(std::string(externalPreserved.logReason), std::string("external daemon (not ours to stop)"));

    auto externalStopAllowed = DaemonController::evaluateShutdownPolicy(true, true, false, true);
    EXPECT_EQ(externalStopAllowed.action, ShutdownAction::StopDaemon);

    auto ownedDaemon = DaemonController::evaluateShutdownPolicy(true, false, false, false);
    EXPECT_EQ(ownedDaemon.action, ShutdownAction::StopDaemon);

    auto restart = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::ManualRestart, true, true, true, false);
    EXPECT_TRUE(restart.allowed);
    EXPECT_TRUE(restart.wasRunning);
    EXPECT_TRUE(restart.resetCrashCount);
    EXPECT_TRUE(restart.disconnectRpc);
    EXPECT_EQ(restart.restartDelayMs, 500);
    EXPECT_EQ(std::string(restart.taskName), std::string("daemon-restart"));

    auto blockedRestart = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::ManualRestart, true, true, true, true);
    EXPECT_FALSE(blockedRestart.allowed);

    auto externalRescan = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::Rescan, false, false, false, false);
    EXPECT_FALSE(externalRescan.allowed);
    EXPECT_EQ(std::string(externalRescan.warning),
              std::string("Rescan requires embedded daemon. Restart your daemon with -rescan manually."));

    auto deleteData = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::DeleteBlockchainData, true, true, false, false);
    EXPECT_TRUE(deleteData.allowed);
    EXPECT_FALSE(deleteData.wasRunning);
    EXPECT_EQ(deleteData.restartDelayMs, 3000);

    auto bootstrap = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::BootstrapStop, false, false, true, false);
    EXPECT_TRUE(bootstrap.allowed);
    EXPECT_TRUE(bootstrap.wasRunning);
    EXPECT_TRUE(bootstrap.disconnectRpc);
}

void testDaemonLifecycleExecution()
{
    using dragonx::daemon::DaemonController;

    auto restart = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::ManualRestart, true, true, true, false);
    MockDaemonLifecycleRuntime restartRuntime;
    MockDaemonLifecycleTask restartTask;
    auto restartResult = DaemonController::executeLifecycleOperation(restart, restartRuntime, restartTask);
    EXPECT_TRUE(restartResult.completed);
    EXPECT_TRUE(restartResult.stopped);
    EXPECT_TRUE(restartResult.started);
    EXPECT_EQ(restartTask.sleeps, 5);
    EXPECT_TRUE(restartRuntime.calls == std::vector<std::string>({"stop", "start"}));

    auto rescan = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::Rescan, true, true, true, false);
    MockDaemonLifecycleRuntime rescanRuntime;
    MockDaemonLifecycleTask rescanTask;
    auto rescanResult = DaemonController::executeLifecycleOperation(rescan, rescanRuntime, rescanTask);
    EXPECT_TRUE(rescanResult.completed);
    EXPECT_EQ(rescanTask.sleeps, 30);
    EXPECT_TRUE(rescanRuntime.calls == std::vector<std::string>({"stop", "reset-output", "start"}));

    auto deleteData = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::DeleteBlockchainData, true, true, false, false);
    MockDaemonLifecycleRuntime deleteRuntime;
    MockDaemonLifecycleTask deleteTask;
    auto deleteResult = DaemonController::executeLifecycleOperation(deleteData, deleteRuntime, deleteTask);
    EXPECT_TRUE(deleteResult.completed);
    EXPECT_EQ(deleteResult.deletedItems, 7);
    EXPECT_TRUE(deleteRuntime.calls == std::vector<std::string>({"stop", "delete", "reset-output", "start"}));

    auto bootstrap = DaemonController::evaluateLifecycleOperation(
        DaemonController::LifecycleOperation::BootstrapStop, true, true, true, false);
    MockDaemonLifecycleRuntime bootstrapRuntime;
    MockDaemonLifecycleTask bootstrapTask;
    auto bootstrapResult = DaemonController::executeLifecycleOperation(bootstrap, bootstrapRuntime, bootstrapTask);
    EXPECT_TRUE(bootstrapResult.completed);
    EXPECT_TRUE(bootstrapResult.stopped);
    EXPECT_TRUE(bootstrapRuntime.calls == std::vector<std::string>({"rpc-stop-disconnect"}));
    EXPECT_EQ(bootstrapRuntime.rpcStopContext, std::string("Bootstrap daemon stop"));
    EXPECT_EQ(bootstrapRuntime.disconnectReason, std::string("Bootstrap"));

    MockDaemonLifecycleRuntime cancelRuntime;
    MockDaemonLifecycleTask cancelTask;
    cancelTask.cancelAfterSleeps = 1;
    auto cancelResult = DaemonController::executeLifecycleOperation(rescan, cancelRuntime, cancelTask);
    EXPECT_TRUE(cancelResult.cancelled);
    EXPECT_FALSE(cancelResult.started);
    EXPECT_TRUE(cancelRuntime.calls == std::vector<std::string>({"stop"}));

    MockDaemonLifecycleRuntime shutdownRuntime;
    MockDaemonLifecycleTask shutdownTask;
    shutdownTask.isShuttingDown = true;
    auto shutdownResult = DaemonController::executeLifecycleOperation(restart, shutdownRuntime, shutdownTask);
    EXPECT_TRUE(shutdownResult.cancelled);
    EXPECT_TRUE(shutdownResult.stopped);
    EXPECT_FALSE(shutdownResult.started);
    EXPECT_TRUE(shutdownRuntime.calls == std::vector<std::string>({"stop"}));
}

void testDaemonLifecycleAdapters()
{
    using dragonx::daemon::AsyncLifecycleTaskContext;
    using dragonx::daemon::BlockchainDataCleaner;
    using dragonx::daemon::ImmediateLifecycleTaskContext;
    using dragonx::util::AsyncTaskManager;

    auto cancelled = std::make_shared<std::atomic<bool>>(false);
    std::atomic<bool> shuttingDown{false};
    AsyncTaskManager::Token token(cancelled);
    AsyncLifecycleTaskContext asyncContext(token, shuttingDown);
    EXPECT_FALSE(asyncContext.cancelled());
    EXPECT_FALSE(asyncContext.shuttingDown());
    cancelled->store(true);
    EXPECT_TRUE(asyncContext.cancelled());
    shuttingDown.store(true);
    EXPECT_TRUE(asyncContext.shuttingDown());

    ImmediateLifecycleTaskContext immediateContext;
    EXPECT_FALSE(immediateContext.cancelled());
    EXPECT_FALSE(immediateContext.shuttingDown());
    immediateContext.sleepForMs(50);

    fs::path dataDir = makeTempDir();
    fs::create_directories(dataDir / "blocks" / "nested");
    fs::create_directories(dataDir / "chainstate");
    fs::create_directories(dataDir / "database");
    fs::create_directories(dataDir / "notarizations");
    std::ofstream(dataDir / "blocks" / "nested" / "blk00000.dat") << "block";
    std::ofstream(dataDir / "chainstate" / "state.dat") << "state";
    std::ofstream(dataDir / "database" / "db.dat") << "db";
    std::ofstream(dataDir / "notarizations" / "notary.dat") << "notary";
    std::ofstream(dataDir / "peers.dat") << "peers";
    std::ofstream(dataDir / "fee_estimates.dat") << "fees";
    std::ofstream(dataDir / "wallet.dat") << "wallet";

    int removed = BlockchainDataCleaner::removeBlockchainData(dataDir);
    EXPECT_TRUE(removed >= 6);
    EXPECT_FALSE(fs::exists(dataDir / "blocks"));
    EXPECT_FALSE(fs::exists(dataDir / "chainstate"));
    EXPECT_FALSE(fs::exists(dataDir / "database"));
    EXPECT_FALSE(fs::exists(dataDir / "notarizations"));
    EXPECT_FALSE(fs::exists(dataDir / "peers.dat"));
    EXPECT_FALSE(fs::exists(dataDir / "fee_estimates.dat"));
    EXPECT_TRUE(fs::exists(dataDir / "wallet.dat"));
    fs::remove_all(dataDir);
}

void testRendererHelpers()
{
    EXPECT_NEAR(dragonx::ui::ComputeConsoleInputHeight(20.0f, 4.0f, 8.0f, 2.0f, 1.0f),
                43.0, 0.0001);
    EXPECT_NEAR(dragonx::ui::ComputeConsoleOutputHeight(200.0f, 50.0f, 120.0f, 0.75f),
                150.0, 0.0001);
    EXPECT_NEAR(dragonx::ui::ComputeConsoleOutputHeight(120.0f, 80.0f, 70.0f, 0.5f),
                70.0, 0.0001);
    EXPECT_NEAR(dragonx::ui::ClampConsoleWrapWidth(40.0f, 8.0f), 50.0, 0.0001);
    EXPECT_NEAR(dragonx::ui::ClampConsoleWrapWidth(200.0f, 12.0f), 176.0, 0.0001);

    EXPECT_EQ(dragonx::ui::ClampMiningThreads(0, 8), 1);
    EXPECT_EQ(dragonx::ui::ClampMiningThreads(12, 8), 8);
    EXPECT_EQ(dragonx::ui::ClampMiningThreads(4, 8), 4);
    EXPECT_TRUE(dragonx::ui::IsPoolMiningActive(true, true, false));
    EXPECT_FALSE(dragonx::ui::IsPoolMiningActive(true, false, true));
    EXPECT_TRUE(dragonx::ui::IsPoolMiningActive(false, false, true));

    dragonx::ui::ConsoleOutputFilter filter{"error", false, false, false, 10, 20, 30};
    EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("RPC Error", 20, filter));
    EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("daemon line", 10, filter));
    filter.text.clear();
    EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("[rpc] History -> listtransactions", 30, filter));
    filter.rpcTraceEnabled = true;
    EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("[rpc] History -> listtransactions", 30, filter));
    filter.daemonMessagesEnabled = true;
    filter.errorsOnly = true;
    filter.text.clear();
    EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("error line", 20, filter));
    EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("info line", 30, filter));

    std::vector<dragonx::AddressInfo> poolAddresses;
    poolAddresses.push_back({"R-transparent", 0.0, "transparent", true});
    poolAddresses.push_back({"zs-default-worker", 0.0, "shielded", true});
    EXPECT_TRUE(dragonx::ui::shouldDefaultPoolWorker("", false));
    EXPECT_TRUE(dragonx::ui::shouldDefaultPoolWorker("x", false));
    EXPECT_FALSE(dragonx::ui::shouldDefaultPoolWorker("zs-manual", false));
    EXPECT_FALSE(dragonx::ui::shouldDefaultPoolWorker("", true));
    EXPECT_EQ(dragonx::ui::defaultPoolWorkerAddress(poolAddresses), std::string("zs-default-worker"));
    EXPECT_TRUE(dragonx::ui::miningValueAlreadySaved({"pool-a", "pool-b"}, "pool-b"));
    EXPECT_FALSE(dragonx::ui::miningValueAlreadySaved({"pool-a"}, ""));
    EXPECT_EQ(std::string(dragonx::ui::defaultPoolUrl()), std::string("pool.dragonx.is:3433"));

    dragonx::TransactionInfo tx;
    tx.type = "send";
    tx.amount = -1.25;
    tx.address = "zsabcdefghijklmnopqrstuvwxyz";
    auto txDisplay = dragonx::ui::buildRecentTxDisplay(tx, 12);
    EXPECT_EQ(txDisplay.typeText, std::string("Sent"));
    EXPECT_EQ(txDisplay.addressText, std::string("zsab...wxyz"));
    EXPECT_EQ(txDisplay.amountText, std::string("-1.2500 DRGX"));
    EXPECT_EQ(txDisplay.timeText, std::string(""));
}


void testConsoleInputModel()
{
    std::vector<std::string> history;
    dragonx::ui::AppendConsoleHistory(history, "getinfo", 3);
    dragonx::ui::AppendConsoleHistory(history, "getinfo", 3);
    dragonx::ui::AppendConsoleHistory(history, "getpeerinfo", 3);
    dragonx::ui::AppendConsoleHistory(history, "getblockcount", 3);
    dragonx::ui::AppendConsoleHistory(history, "help", 3);
    EXPECT_EQ(history.size(), static_cast<size_t>(3));
    EXPECT_EQ(history.front(), std::string("getpeerinfo"));

    int index = dragonx::ui::NavigateConsoleHistoryIndex(-1, history.size(), true);
    EXPECT_EQ(index, 2);
    EXPECT_EQ(dragonx::ui::ConsoleHistoryEntry(history, index), std::string("help"));
    index = dragonx::ui::NavigateConsoleHistoryIndex(index, history.size(), true);
    EXPECT_EQ(index, 1);
    index = dragonx::ui::NavigateConsoleHistoryIndex(index, history.size(), false);
    EXPECT_EQ(index, 2);
    index = dragonx::ui::NavigateConsoleHistoryIndex(index, history.size(), false);
    EXPECT_EQ(index, -1);

    auto single = dragonx::ui::CompleteConsoleCommand("stop");
    EXPECT_EQ(single.matches.size(), static_cast<size_t>(1));
    EXPECT_EQ(single.commonPrefix, std::string("stop"));

    auto multiple = dragonx::ui::CompleteConsoleCommand("getblock");
    EXPECT_TRUE(multiple.matches.size() > 1);
    EXPECT_EQ(multiple.commonPrefix, std::string("getblock"));
    auto lines = dragonx::ui::FormatConsoleCompletionLines(multiple.matches, 40);
    EXPECT_FALSE(lines.empty());

    auto args = dragonx::ui::ParseConsoleCommandArgs("z_sendmany \"from addr\" [{\"address\":\"zs\",\"amount\":1.25}] true");
    EXPECT_EQ(args.size(), static_cast<size_t>(4));
    EXPECT_EQ(args[1], std::string("from addr"));
    auto call = dragonx::ui::BuildConsoleRpcCall("setgenerate true 4");
    EXPECT_TRUE(call.valid);
    EXPECT_EQ(call.method, std::string("setgenerate"));
    EXPECT_EQ(call.params.size(), static_cast<size_t>(2));
    EXPECT_TRUE(call.params[0].get<bool>());
    EXPECT_EQ(call.params[1].get<long long>(), 4LL);

    auto resultLines = dragonx::ui::FormatConsoleRpcResultLines("{\n  \"balance\": 12,\n  \"ok\": true\n}", false);
    EXPECT_EQ(resultLines.size(), static_cast<size_t>(4));
    EXPECT_EQ(resultLines[0].role, dragonx::ui::ConsoleResultLineRole::JsonBrace);
    EXPECT_EQ(resultLines[1].role, dragonx::ui::ConsoleResultLineRole::JsonKey);
    auto nullLines = dragonx::ui::FormatConsoleRpcResultLines("null", false);
    EXPECT_EQ(nullLines[0].text, std::string("(no result)"));
    auto errorLines = dragonx::ui::FormatConsoleRpcResultLines("bad rpc", true);
    EXPECT_EQ(errorLines[0].role, dragonx::ui::ConsoleResultLineRole::Error);
    EXPECT_EQ(errorLines[0].text, std::string("Error: bad rpc"));
}

void testMiningBenchmarkModel()
{
    dragonx::ui::ThreadBenchmark benchmark;
    EXPECT_FALSE(benchmark.active());
    benchmark.buildCandidates(8);
    EXPECT_EQ(benchmark.candidates.size(), static_cast<size_t>(5));
    EXPECT_EQ(benchmark.candidates.front(), 4);
    EXPECT_EQ(benchmark.candidates.back(), 8);
    EXPECT_NEAR(benchmark.avgWarmupSecs(), 195.0, 0.0001);

    benchmark.phase = dragonx::ui::ThreadBenchmark::Phase::WarmingUp;
    EXPECT_TRUE(benchmark.active());
    benchmark.current_index = 2;
    benchmark.total_warmup_secs = 210.0f;
    EXPECT_NEAR(benchmark.avgWarmupSecs(), 105.0, 0.0001);
    EXPECT_NEAR(benchmark.perTestSecs(), 135.0, 0.0001);
    benchmark.phase_timer = 15.0f;
    EXPECT_TRUE(benchmark.progress() > 0.0f);

    benchmark.resetStabilityTracking();
    EXPECT_EQ(benchmark.window_samples, 0);
    EXPECT_EQ(benchmark.consecutive_stable, 0);

    benchmark.phase = dragonx::ui::ThreadBenchmark::Phase::Starting;
    benchmark.candidates = {2};
    benchmark.current_index = 0;
    auto update = dragonx::ui::AdvanceThreadBenchmark(benchmark, 0.1f, 0.0);
    EXPECT_TRUE(update.stopPoolMining);
    EXPECT_TRUE(update.startPoolMining);
    EXPECT_EQ(update.startThreads, 2);
    EXPECT_TRUE(benchmark.phase == dragonx::ui::ThreadBenchmark::Phase::WarmingUp);

    benchmark.phase_timer = dragonx::ui::ThreadBenchmark::MAX_WARMUP_SECS - 0.5f;
    update = dragonx::ui::AdvanceThreadBenchmark(benchmark, 1.0f, 100.0);
    EXPECT_TRUE(benchmark.phase == dragonx::ui::ThreadBenchmark::Phase::Measuring);
    benchmark.phase_timer = dragonx::ui::ThreadBenchmark::MEASURE_SECS - 0.5f;
    update = dragonx::ui::AdvanceThreadBenchmark(benchmark, 1.0f, 250.0);
    EXPECT_TRUE(benchmark.phase == dragonx::ui::ThreadBenchmark::Phase::Advancing);
    EXPECT_EQ(benchmark.optimal_threads, 2);
    benchmark.was_pool_running = true;
    update = dragonx::ui::AdvanceThreadBenchmark(benchmark, 0.1f, 0.0);
    EXPECT_TRUE(update.stopPoolMining);
    EXPECT_TRUE(update.saveOptimalThreads);
    EXPECT_TRUE(update.startPoolMining);
    EXPECT_EQ(update.optimalThreads, 2);

    benchmark.reset();
    EXPECT_FALSE(benchmark.active());
    EXPECT_TRUE(benchmark.candidates.empty());
}

void testBalanceAddressListModel()
{
    std::vector<dragonx::AddressInfo> addresses;
    dragonx::AddressInfo zHigh;
    zHigh.address = "zs-high";
    zHigh.label = "Vault";
    zHigh.balance = 10.0;
    zHigh.type = "shielded";
    addresses.push_back(zHigh);

    dragonx::AddressInfo tZero;
    tZero.address = "R-zero";
    tZero.label = "Empty";
    tZero.balance = 0.0;
    tZero.type = "transparent";
    addresses.push_back(tZero);

    dragonx::AddressInfo tFav;
    tFav.address = "R-favorite";
    tFav.label = "Favorite";
    tFav.balance = 0.0;
    tFav.type = "transparent";
    addresses.push_back(tFav);

    std::vector<dragonx::ui::AddressListInput> inputs = {
        {&addresses[0], true, false, false, false, "Main Vault", "", -1},
        {&addresses[1], false, false, false, false, "Empty", "", -1},
        {&addresses[2], false, false, true, false, "Favorite", "", -1}
    };

    auto rows = dragonx::ui::BuildAddressListRows(inputs, "", true, false);
    EXPECT_EQ(rows.size(), static_cast<size_t>(2));
    EXPECT_EQ(rows[0].info->address, std::string("R-favorite"));
    EXPECT_EQ(rows[1].info->address, std::string("zs-high"));

    inputs[2].sortOrder = 0;
    rows = dragonx::ui::BuildAddressListRows(inputs, "", true, false);
    EXPECT_EQ(rows[0].info->address, std::string("R-favorite"));

    rows = dragonx::ui::BuildAddressListRows(inputs, "vault", false, false);
    EXPECT_EQ(rows.size(), static_cast<size_t>(1));
    EXPECT_EQ(rows[0].info->address, std::string("zs-high"));

    auto layout = dragonx::ui::ComputeAddressRowLayout(10.0f, 20.0f, 300.0f, 50.0f,
                                                       12.0f, 16.0f, 6.0f, 4.0f, 2.0f);
    EXPECT_NEAR(layout.contentStartX, 22.0, 0.0001);
    EXPECT_NEAR(layout.contentStartY, 26.0, 0.0001);
    EXPECT_NEAR(layout.buttonSize, 38.0, 0.0001);
    EXPECT_NEAR(layout.favoriteButton.x, 270.0, 0.0001);
    EXPECT_NEAR(layout.visibilityButton.x, 228.0, 0.0001);
    EXPECT_NEAR(layout.contentRight, 224.0, 0.0001);
    EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(2.0, 3.5), std::string("$7.00"));
    EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(0.001, 2.0), std::string("$0.002000"));
    EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(0.0, 2.0), std::string(""));
}

void testExplorerBlockCache()
{
    using dragonx::ui::ExplorerBlockCache;
    using dragonx::ui::ExplorerBlockSummary;
    using nlohmann::json;

    fs::path dir = makeTempDir();
    fs::path databasePath = dir / "explorer_blocks.sqlite";
    fs::path legacyPath = dir / "explorer_blocks_cache.json";

    json legacy = {
        {"version", 1},
        {"tip_height", 10},
        {"tip_hash", "hash-10"},
        {"blocks", json::array({
            json{{"height", 10}, {"hash", "hash-10"}, {"tx_count", 3}, {"size", 1000}, {"time", 5000}, {"difficulty", 1.25}},
            json{{"height", 9}, {"hash", "hash-9"}, {"tx_count", 2}, {"size", 900}, {"time", 4900}, {"difficulty", 1.20}}
        })}
    };
    {
        std::ofstream file(legacyPath);
        file << legacy.dump(2);
    }

    ExplorerBlockCache cache(databasePath.string(), legacyPath.string());
    EXPECT_TRUE(cache.ensureOpen());
    EXPECT_EQ(cache.cachedBlockCount(), 2);

    auto range = cache.loadRange(9, 10);
    EXPECT_EQ(range.size(), static_cast<size_t>(2));
    EXPECT_EQ(range[10].hash, std::string("hash-10"));
    EXPECT_EQ(range[9].tx_count, 2);

    auto sameTipValidation = cache.prepareValidation(10, "hash-10");
    EXPECT_FALSE(sameTipValidation.needed);

    auto advancedValidation = cache.prepareValidation(12, "hash-12");
    EXPECT_TRUE(advancedValidation.needed);
    EXPECT_EQ(advancedValidation.height, 10);
    EXPECT_EQ(advancedValidation.expectedHash, std::string("hash-10"));
    cache.applySavedTipValidation(advancedValidation, "hash-10", 12, "hash-12");

    ExplorerBlockSummary block12;
    block12.height = 12;
    block12.hash = "hash-12";
    block12.tx_count = 5;
    block12.size = 1200;
    block12.time = 5200;
    block12.difficulty = 1.40;
    EXPECT_TRUE(cache.storeBlock(block12));
    EXPECT_EQ(cache.loadRange(12, 12)[12].tx_count, 5);

    {
        ExplorerBlockCache reopened(databasePath.string(), legacyPath.string());
        EXPECT_TRUE(reopened.ensureOpen());
        EXPECT_EQ(reopened.cachedBlockCount(), 3);
        auto noRepeatValidation = reopened.prepareValidation(12, "hash-12");
        EXPECT_FALSE(noRepeatValidation.needed);
    }

    cache.prepareValidation(12, "different-tip");
    EXPECT_EQ(cache.cachedBlockCount(), 0);

    {
        ExplorerBlockCache reopened(databasePath.string(), legacyPath.string());
        EXPECT_TRUE(reopened.ensureOpen());
        EXPECT_EQ(reopened.cachedBlockCount(), 0);
    }

    fs::remove_all(dir);
}

void testTransactionHistoryCache()
{
    using dragonx::TransactionInfo;
    using dragonx::data::TransactionHistoryCache;

    std::string identityA = TransactionHistoryCache::walletIdentityFromAddresses(
        {"zs-beta", "zs-alpha"}, {"R-one"});
    std::string identityB = TransactionHistoryCache::walletIdentityFromAddresses(
        {"zs-alpha", "zs-beta"}, {"R-one"});
    std::string identityWithSameText = TransactionHistoryCache::walletIdentityFromAddresses(
        {"same-address-text"}, {"same-address-text"});
    EXPECT_EQ(identityA, identityB);
    EXPECT_TRUE(identityA.find("protocol=") == std::string::npos);
    EXPECT_TRUE(identityA.find("p2p_port=") == std::string::npos);
    EXPECT_TRUE(identityA.find("z:zs-alpha") != std::string::npos);
    EXPECT_TRUE(identityA.find("t:R-one") != std::string::npos);
    EXPECT_TRUE(identityWithSameText.find("z:same-address-text") != std::string::npos);
    EXPECT_TRUE(identityWithSameText.find("t:same-address-text") != std::string::npos);

    fs::path dir = makeTempDir();
    fs::path databasePath = dir / "transaction_history.sqlite";
    std::string walletIdentity = "mainnet|R-alpha|zs-beta";
    std::string passphrase = "correct horse battery staple";

    std::vector<TransactionInfo> transactions;
    transactions.push_back(TransactionInfo{
        "tx-sensitive-send", "send", -1.25, 1700000100, 12,
        "zs-destination-sensitive", "R-source-sensitive", "private memo"
    });
    transactions.push_back(TransactionInfo{
        "tx-mined", "mined", 3.5, 1700000000, 104,
        "R-mining-address", "", ""
    });
    std::unordered_map<std::string, int> shieldedScanHeights{
        {"zs-beta", 120},
        {"zs-archive", 118}
    };

    {
        TransactionHistoryCache cache(databasePath.string());
        EXPECT_TRUE(cache.ensureOpen());
        EXPECT_TRUE(cache.unlockWithPassphrase(walletIdentity, passphrase));
        EXPECT_TRUE(cache.replace(walletIdentity, 120, "tip-120", transactions, 1700000200, shieldedScanHeights));
        EXPECT_EQ(cache.snapshotCount(), 1);
    }

    {
        std::ifstream database(databasePath, std::ios::binary);
        std::string bytes((std::istreambuf_iterator<char>(database)), std::istreambuf_iterator<char>());
        EXPECT_TRUE(bytes.find("tx-sensitive-send") == std::string::npos);
        EXPECT_TRUE(bytes.find("zs-destination-sensitive") == std::string::npos);
        EXPECT_TRUE(bytes.find("zs-archive") == std::string::npos);
        EXPECT_TRUE(bytes.find("private memo") == std::string::npos);
    }

    {
        TransactionHistoryCache wrongKey(databasePath.string());
        EXPECT_TRUE(wrongKey.unlockWithPassphrase(walletIdentity, "wrong passphrase"));
        auto wrongLoad = wrongKey.load(walletIdentity, 120, "tip-120");
        EXPECT_FALSE(wrongLoad.loaded);
    }

    {
        TransactionHistoryCache reopened(databasePath.string());
        EXPECT_TRUE(reopened.unlockWithPassphrase(walletIdentity, passphrase));
        auto loaded = reopened.load(walletIdentity, 120, "tip-120");
        EXPECT_TRUE(loaded.loaded);
        EXPECT_FALSE(loaded.invalidated);
        EXPECT_EQ(loaded.tipHeight, 120);
        EXPECT_EQ(loaded.tipHash, std::string("tip-120"));
        EXPECT_EQ(loaded.updatedAt, static_cast<std::time_t>(1700000200));
        EXPECT_EQ(loaded.transactions.size(), static_cast<size_t>(2));
        EXPECT_EQ(loaded.transactions[0].txid, std::string("tx-sensitive-send"));
        EXPECT_EQ(loaded.transactions[0].memo, std::string("private memo"));
        EXPECT_EQ(loaded.transactions[1].type, std::string("mined"));
        EXPECT_EQ(loaded.shieldedScanHeights.size(), static_cast<size_t>(2));
        EXPECT_EQ(loaded.shieldedScanHeights.at("zs-beta"), 120);
        EXPECT_EQ(loaded.shieldedScanHeights.at("zs-archive"), 118);
    }

    {
        TransactionHistoryCache cache(databasePath.string());
        EXPECT_TRUE(cache.unlockWithPassphrase(walletIdentity, passphrase));
        EXPECT_TRUE(cache.replace(walletIdentity, 120, "tip-120", transactions, 1700000250));
        auto loaded = cache.load(walletIdentity, 120, "tip-120");
        EXPECT_TRUE(loaded.loaded);
        EXPECT_EQ(loaded.shieldedScanHeights.size(), static_cast<size_t>(0));
    }

    {
        TransactionHistoryCache staleTip(databasePath.string());
        EXPECT_TRUE(staleTip.unlockWithPassphrase(walletIdentity, passphrase));
        auto invalidated = staleTip.load(walletIdentity, 119, "tip-119");
        EXPECT_FALSE(invalidated.loaded);
        EXPECT_TRUE(invalidated.invalidated);
        EXPECT_EQ(staleTip.snapshotCount(), 0);
    }

    {
        TransactionHistoryCache cache(databasePath.string());
        EXPECT_TRUE(cache.unlockWithPassphrase(walletIdentity, passphrase));
        EXPECT_TRUE(cache.replace(walletIdentity, 120, "tip-120", transactions, 1700000300));
        auto invalidated = cache.load(walletIdentity, 120, "different-tip");
        EXPECT_FALSE(invalidated.loaded);
        EXPECT_TRUE(invalidated.invalidated);
        EXPECT_EQ(cache.snapshotCount(), 0);
    }

    fs::remove_all(dir);
}

void testTransactionHistoryCacheRefreshApply()
{
    using Refresh = dragonx::services::NetworkRefreshService;
    using dragonx::TransactionInfo;
    using dragonx::data::TransactionHistoryCache;

    fs::path dir = makeTempDir();
    fs::path databasePath = dir / "transaction_history_refresh.sqlite";
    std::string walletIdentity = "mainnet|R-refresh-cache|zs-refresh-cache";
    std::string passphrase = "refresh cache passphrase";

    dragonx::WalletState state;
    state.sync.blocks = 333;
    state.sync.best_blockhash = "tip-333";

    Refresh::TransactionRefreshResult refreshResult;
    refreshResult.blockHeight = 333;
    TransactionInfo refreshedTx;
    refreshedTx.txid = "rpc-refreshed-send";
    refreshedTx.type = "send";
    refreshedTx.amount = -2.75;
    refreshedTx.timestamp = 1700000400;
    refreshedTx.confirmations = 14;
    refreshedTx.address = "zs-rpc-destination";
    refreshedTx.from_address = "R-refresh-cache";
    refreshedTx.memo = "rpc refreshed memo";
    refreshResult.transactions = {refreshedTx};

    Refresh::TransactionViewCache viewCache;
    std::unordered_set<std::string> sendTxids;
    std::vector<TransactionInfo> confirmedCache;
    std::unordered_set<std::string> confirmedIds;
    int confirmedBlock = -1;
    int lastTxBlock = -1;
    Refresh::TransactionCacheUpdate cacheUpdate{
        viewCache,
        sendTxids,
        confirmedCache,
        confirmedIds,
        confirmedBlock,
        lastTxBlock
    };
    Refresh::applyTransactionRefreshResult(state, cacheUpdate, std::move(refreshResult), 1700000500);

    TransactionHistoryCache cache(databasePath.string());
    EXPECT_TRUE(cache.unlockWithPassphrase(walletIdentity, passphrase));
    EXPECT_TRUE(cache.replace(walletIdentity,
                              state.sync.blocks,
                              state.sync.best_blockhash,
                              state.transactions,
                              state.last_tx_update));

    TransactionHistoryCache reopened(databasePath.string());
    EXPECT_TRUE(reopened.unlockWithPassphrase(walletIdentity, passphrase));
    auto loaded = reopened.load(walletIdentity, 333, "tip-333");
    EXPECT_TRUE(loaded.loaded);
    EXPECT_EQ(loaded.transactions.size(), static_cast<size_t>(1));
    EXPECT_EQ(loaded.transactions[0].txid, std::string("rpc-refreshed-send"));
    EXPECT_EQ(loaded.transactions[0].memo, std::string("rpc refreshed memo"));
    EXPECT_EQ(loaded.updatedAt, static_cast<std::time_t>(1700000500));
    EXPECT_EQ(lastTxBlock, 333);
    EXPECT_EQ(confirmedIds.count("rpc-refreshed-send"), static_cast<size_t>(1));

    fs::remove_all(dir);
}




































































































































































































void testLiteBridgeOwnedStringCopiesBeforeFreeOnSuccess()
{
    resetLiteBridgeRuntimeTrackedFree();
    dragonx::wallet::LiteBridgeOwnedString ownedString(
        makeLiteBridgeRuntimeOwnedCString("phase2-ok"),
        &fakeLiteBridgeRuntimeTrackedFreeString);

    auto result = ownedString.intoResult();
    EXPECT_TRUE(result.ok);
    EXPECT_EQ(result.value, std::string("phase2-ok"));
    EXPECT_TRUE(result.error.empty());
    EXPECT_TRUE(ownedString.rawPointerReceived());
    EXPECT_TRUE(ownedString.copiedBeforeFree());
    EXPECT_TRUE(ownedString.freed());
    EXPECT_FALSE(ownedString.rawPointerEscaped());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues.size(), static_cast<std::size_t>(1));
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues[0], std::string("phase2-ok"));

    auto secondResult = ownedString.intoResult();
    EXPECT_FALSE(secondResult.ok);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 1);
}

void testLiteBridgeOwnedStringClassifiesNullWithoutFree()
{
    resetLiteBridgeRuntimeTrackedFree();
    dragonx::wallet::LiteBridgeOwnedString ownedString(
        nullptr,
        &fakeLiteBridgeRuntimeTrackedFreeString);

    auto result = ownedString.intoResult();
    EXPECT_FALSE(result.ok);
    EXPECT_TRUE(result.value.empty());
    EXPECT_TRUE(result.error.find("null string") != std::string::npos);
    EXPECT_FALSE(ownedString.rawPointerReceived());
    EXPECT_FALSE(ownedString.copiedBeforeFree());
    EXPECT_FALSE(ownedString.freed());
    EXPECT_FALSE(ownedString.rawPointerEscaped());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 0);
}

void testLiteBridgeOwnedStringClassifiesErrorAndFreesOnce()
{
    resetLiteBridgeRuntimeTrackedFree();
    dragonx::wallet::LiteBridgeOwnedString ownedString(
        makeLiteBridgeRuntimeOwnedCString("Error: phase2 denied"),
        &fakeLiteBridgeRuntimeTrackedFreeString);

    auto result = ownedString.intoResult();
    EXPECT_FALSE(result.ok);
    EXPECT_TRUE(result.value.empty());
    EXPECT_EQ(result.error, std::string("Error: phase2 denied"));
    EXPECT_TRUE(ownedString.copiedBeforeFree());
    EXPECT_TRUE(ownedString.freed());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues.size(), static_cast<std::size_t>(1));
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues[0], std::string("Error: phase2 denied"));
}

void testLiteBridgeOwnedStringMovePreventsDoubleFree()
{
    resetLiteBridgeRuntimeTrackedFree();
    {
        dragonx::wallet::LiteBridgeOwnedString original(
            makeLiteBridgeRuntimeOwnedCString("phase2-move"),
            &fakeLiteBridgeRuntimeTrackedFreeString);
        dragonx::wallet::LiteBridgeOwnedString moved(std::move(original));

        EXPECT_FALSE(original.rawPointerReceived());
        EXPECT_TRUE(moved.rawPointerReceived());
        EXPECT_FALSE(moved.rawPointerEscaped());
    }

    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues.size(), static_cast<std::size_t>(1));
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues[0], std::string("phase2-move"));
}

void testLiteClientBridgeUsesRuntimeOwnedStringCleanup()
{
    resetLiteBridgeRuntimeTrackedFree();
    g_liteBridgeRuntimeFakeCallCount = 0;
    auto bridgeApi = makeCompleteFakeLiteBridgeRuntimeApi();
    bridgeApi.execute = &fakeLiteBridgeRuntimeOwnedExecute;
    bridgeApi.freeString = &fakeLiteBridgeRuntimeTrackedFreeString;

    auto bridge = dragonx::wallet::LiteClientBridge::fromApi(bridgeApi);
    auto result = bridge.execute("phase2", "{}");

    EXPECT_TRUE(result.ok);
    EXPECT_EQ(result.value, std::string("bridge-runtime-ok"));
    EXPECT_TRUE(result.error.empty());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues.size(), static_cast<std::size_t>(1));
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues[0], std::string("bridge-runtime-ok"));
    EXPECT_EQ(g_liteBridgeRuntimeFakeCallCount, 1);
}






void testLiteBridgeRuntimeUnavailableDoesNotCallShutdown()
{
    resetLiteBridgeRuntimeTrackedFree();
    auto runtime = LiteBridgeRuntime::fromBindingResult({});

    EXPECT_FALSE(runtime.available());
    EXPECT_EQ(runtime.status(), LiteBridgeRuntimeStatus::Unavailable);
    runtime.shutdown();
    EXPECT_EQ(runtime.status(), LiteBridgeRuntimeStatus::Shutdown);
    EXPECT_TRUE(runtime.shutdownCalled());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 0);
    EXPECT_TRUE(runtime.dynamicLibraryUnloadDeferred());
}








































































































































































































































































































































































































































































































































































































void testGeneratedResourceBehavior()
{
    const auto* themes = dragonx::resources::getEmbeddedThemes();
    EXPECT_TRUE(themes != nullptr);
    EXPECT_TRUE(dragonx::resources::getEmbeddedResource("__missing_resource__") == nullptr);
    if (!dragonx::resources::hasEmbeddedResources()) {
        EXPECT_TRUE(themes[0].data == nullptr);
        EXPECT_EQ(dragonx::resources::extractBundledThemes("/tmp/obsidian-dragon-empty-themes"), 0);
    }
}


// ---- restored live lite_bridge_runtime tests ----
std::vector<std::string> requiredLiteBackendAbiSymbolNames()
{
    std::vector<std::string> names;
    for (const auto& symbol : dragonx::wallet::liteBackendArtifactContractRequiredSymbols()) {
        names.push_back(symbol.abiName);
    }
    return names;
}

LiteBackendArtifactContractInput makeReadyLiteBackendArtifactContractInput(const fs::path& artifactPath)
{
    LiteBackendArtifactContractInput input;
    input.contractOwnerReady = true;
    input.readOnlyGateReady = true;
    input.projectRoot = artifactPath.parent_path().string();
    input.artifactPath = artifactPath.string();
    input.platform = dragonx::wallet::currentLiteBackendArtifactPlatform();
    input.artifactKind = dragonx::wallet::LiteBackendArtifactKind::StaticLibrary;
    input.linkMode = LiteBackendArtifactContractLinkMode::ImportedLibrary;
    input.abiVersion = dragonx::wallet::liteBackendArtifactContractSupportedAbiVersion();
    input.artifactSha256 = "phase1-artifact-sha256";
    input.provenance = makeReadyLiteBackendArtifactProvenance();
    input.sdxlCompatible = true;
    input.symbolInventoryOwnerReady = true;
    input.exportedSymbols = requiredLiteBackendAbiSymbolNames();
    return input;
}

LiteBackendArtifactContractResult makeReadyLiteBridgeRuntimeArtifactContractResult()
{
    const auto tempDir = makeTempDir();
    const auto artifactPath = tempDir / "libsilentdragonxlite.a";
    writeTestFile(artifactPath, "phase2 fake archive");

    auto contractInput = makeReadyLiteBackendArtifactContractInput(artifactPath);
    auto contractResult = dragonx::wallet::evaluateLiteBackendArtifactContract(contractInput);
    fs::remove_all(tempDir);
    return contractResult;
}

LiteBridgeRuntimeBindingInput makeReadyFakeLiteBridgeRuntimeBindingInput(bool activationRequested = true)
{
    LiteBridgeRuntimeBindingInput input;
    input.artifactContract = makeReadyLiteBridgeRuntimeArtifactContractResult();
    input.config = dragonx::wallet::liteBridgeRuntimeConfigFromContractResult(
        input.artifactContract, activationRequested);
    input.useProvidedSymbolTable = true;
    input.symbolTable = dragonx::wallet::liteBridgeRuntimeSymbolTableFromApi(
        makeCompleteFakeLiteBridgeRuntimeApi(), "fake-imported-linked");
    return input;
}

LiteBridgeRuntime makeReadyTrackedLiteBridgeRuntime()
{
    auto input = makeReadyFakeLiteBridgeRuntimeBindingInput();
    auto bridgeApi = makeCompleteFakeLiteBridgeRuntimeApi();
    bridgeApi.initializeNew = &fakeLiteBridgeRuntimeOwnedInitializeNew;
    bridgeApi.initializeNewFromPhrase = &fakeLiteBridgeRuntimeOwnedInitializeNewFromPhrase;
    bridgeApi.initializeExisting = &fakeLiteBridgeRuntimeOwnedInitializeExisting;
    bridgeApi.execute = &fakeLiteBridgeRuntimeOwnedExecute;
    bridgeApi.freeString = &fakeLiteBridgeRuntimeTrackedFreeString;
    bridgeApi.shutdown = &fakeLiteBridgeRuntimeTrackedShutdown;
    input.symbolTable = dragonx::wallet::liteBridgeRuntimeSymbolTableFromApi(
        bridgeApi, "fake-imported-linked");
    auto bindingResult = dragonx::wallet::evaluateLiteBridgeRuntimeBinding(input);
    return LiteBridgeRuntime::fromBindingResult(bindingResult);
}

LiteBridgeRuntime makeReadyFakeDynamicLoaderLiteBridgeRuntime(
    const std::string& source = "fake-dynamic-library",
    const std::string& handleLabel = "phase6-handle")
{
    auto result = dragonx::wallet::evaluateLiteBridgeRuntimeFakeDynamicLoader(
        makeReadyFakeLiteBridgeRuntimeDynamicLoaderInput(source, handleLabel));
    return LiteBridgeRuntime::fromFakeDynamicLoaderResult(result);
}

void testLiteBridgeRuntimeShutdownIsIdempotent()
{
    resetLiteBridgeRuntimeTrackedFree();
    auto runtime = makeReadyTrackedLiteBridgeRuntime();

    EXPECT_TRUE(runtime.available());
    EXPECT_EQ(runtime.status(), LiteBridgeRuntimeStatus::Ready);
    EXPECT_TRUE(runtime.dynamicLibraryUnloadDeferred());

    runtime.shutdown();
    EXPECT_EQ(runtime.status(), LiteBridgeRuntimeStatus::Shutdown);
    EXPECT_TRUE(runtime.shutdownCalled());
    EXPECT_FALSE(runtime.shutdownPending());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);

    runtime.shutdown();
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(1));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[0], std::string("shutdown"));
}

void testLiteBridgeRuntimeDestructorCallsShutdownOnce()
{
    resetLiteBridgeRuntimeTrackedFree();
    {
        auto runtime = makeReadyTrackedLiteBridgeRuntime();
        EXPECT_TRUE(runtime.available());
        EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 0);
    }

    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(1));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[0], std::string("shutdown"));
}

void testLiteBridgeRuntimeShutdownWaitsForOwnedStringRelease()
{
    resetLiteBridgeRuntimeTrackedFree();
    auto runtime = makeReadyTrackedLiteBridgeRuntime();
    auto ownedString = runtime.takeOwnedString(makeLiteBridgeRuntimeOwnedCString("phase3-owned"));

    EXPECT_EQ(runtime.outstandingOwnedStringCount(), static_cast<std::size_t>(1));
    runtime.shutdown();
    EXPECT_EQ(runtime.status(), LiteBridgeRuntimeStatus::ShuttingDown);
    EXPECT_TRUE(runtime.shutdownPending());
    EXPECT_FALSE(runtime.shutdownCalled());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 0);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 0);

    auto result = ownedString.intoResult();
    EXPECT_TRUE(result.ok);
    EXPECT_EQ(result.value, std::string("phase3-owned"));
    EXPECT_EQ(runtime.outstandingOwnedStringCount(), static_cast<std::size_t>(0));
    EXPECT_EQ(runtime.status(), LiteBridgeRuntimeStatus::Shutdown);
    EXPECT_TRUE(runtime.shutdownCalled());
    EXPECT_FALSE(runtime.shutdownPending());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(2));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[0], std::string("free:phase3-owned"));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[1], std::string("shutdown"));
}

void testLiteBridgeRuntimeDestructorShutdownWaitsForExternalOwnedString()
{
    resetLiteBridgeRuntimeTrackedFree();
    dragonx::wallet::LiteBridgeOwnedString heldString;
    {
        auto runtime = makeReadyTrackedLiteBridgeRuntime();
        heldString = runtime.takeOwnedString(makeLiteBridgeRuntimeOwnedCString("phase3-destructor"));
        EXPECT_EQ(runtime.outstandingOwnedStringCount(), static_cast<std::size_t>(1));
    }

    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 0);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 0);
    EXPECT_TRUE(g_liteBridgeRuntimeTeardownEvents.empty());

    auto result = heldString.intoResult();
    EXPECT_TRUE(result.ok);
    EXPECT_EQ(result.value, std::string("phase3-destructor"));
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(2));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[0], std::string("free:phase3-destructor"));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[1], std::string("shutdown"));
}

void testLiteBridgeRuntimeMoveAssignmentShutsDownReplacedRuntime()
{
    resetLiteBridgeRuntimeTrackedFree();
    {
        auto firstRuntime = makeReadyTrackedLiteBridgeRuntime();
        auto secondRuntime = makeReadyTrackedLiteBridgeRuntime();

        secondRuntime = std::move(firstRuntime);
        EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
        EXPECT_EQ(secondRuntime.status(), LiteBridgeRuntimeStatus::Ready);
        EXPECT_TRUE(secondRuntime.available());
    }

    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 2);
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(2));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[0], std::string("shutdown"));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[1], std::string("shutdown"));
}

void testLiteBridgeRuntimeDryDispatchBoolWrappersAreFakeOnly()
{
    resetLiteBridgeRuntimeTrackedFree();
    g_liteBridgeRuntimeFakeCallCount = 0;
    auto runtime = makeReadyTrackedLiteBridgeRuntime();

    auto walletExists = runtime.dryDispatchWalletExists("DRGX");
    EXPECT_TRUE(walletExists.ok);
    EXPECT_TRUE(walletExists.boolValue);
    EXPECT_TRUE(walletExists.fakeOnlyDispatch);
    EXPECT_TRUE(walletExists.fakeCallAttempted);
    EXPECT_TRUE(walletExists.noRealBridgeCalls);
    EXPECT_TRUE(walletExists.noNetwork);
    EXPECT_TRUE(walletExists.noWalletLifecycle);
    EXPECT_TRUE(walletExists.noWalletStateMutation);
    EXPECT_EQ(walletExists.operation, dragonx::wallet::LiteBridgeRuntimeDryDispatchOperation::WalletExists);

    auto serverOnline = runtime.dryDispatchCheckServerOnline("https://lite.example.invalid");
    EXPECT_TRUE(serverOnline.ok);
    EXPECT_TRUE(serverOnline.boolValue);
    EXPECT_TRUE(serverOnline.fakeOnlyDispatch);
    EXPECT_TRUE(serverOnline.fakeCallAttempted);
    EXPECT_TRUE(serverOnline.noRealBridgeCalls);
    EXPECT_TRUE(serverOnline.noNetwork);
    EXPECT_TRUE(serverOnline.noWalletLifecycle);
    EXPECT_EQ(serverOnline.operation, dragonx::wallet::LiteBridgeRuntimeDryDispatchOperation::CheckServerOnline);
    EXPECT_EQ(g_liteBridgeRuntimeFakeCallCount, 2);
}

void testLiteBridgeRuntimeDryDispatchExecuteOwnsReturnedString()
{
    resetLiteBridgeRuntimeTrackedFree();
    g_liteBridgeRuntimeFakeCallCount = 0;
    auto runtime = makeReadyTrackedLiteBridgeRuntime();

    auto result = runtime.dryDispatchExecute("balance", "{}");
    EXPECT_TRUE(result.ok);
    EXPECT_TRUE(result.fakeCallAttempted);
    EXPECT_TRUE(result.noRealBridgeCalls);
    EXPECT_TRUE(result.noWalletLifecycle);
    EXPECT_TRUE(result.noSendImportExportExecution);
    EXPECT_EQ(result.stringResult.value, std::string("bridge-runtime-ok"));
    EXPECT_EQ(runtime.outstandingOwnedStringCount(), static_cast<std::size_t>(0));
    EXPECT_EQ(g_liteBridgeRuntimeFakeCallCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreedValues[0], std::string("bridge-runtime-ok"));

    result = runtime.dryDispatchExecute("", "{}");
    EXPECT_FALSE(result.ok);
    EXPECT_FALSE(result.fakeCallAttempted);
    EXPECT_TRUE(result.error.find("empty") != std::string::npos);
    EXPECT_EQ(g_liteBridgeRuntimeFakeCallCount, 1);
}

void testLiteBridgeRuntimeDryDispatchLifecycleWrappersRemainFakeOnly()
{
    resetLiteBridgeRuntimeTrackedFree();
    g_liteBridgeRuntimeFakeCallCount = 0;
    auto runtime = makeReadyTrackedLiteBridgeRuntime();

    auto newResult = runtime.dryDispatchInitializeNew(false, "https://lite.example.invalid");
    auto existingResult = runtime.dryDispatchInitializeExisting(false, "https://lite.example.invalid");
    auto phraseResult = runtime.dryDispatchInitializeNewFromPhrase(
        false, "https://lite.example.invalid", "seed words redacted", 42, 0, false);

    EXPECT_TRUE(newResult.ok);
    EXPECT_TRUE(existingResult.ok);
    EXPECT_TRUE(phraseResult.ok);
    EXPECT_EQ(newResult.stringResult.value, std::string("initialize-new-ok"));
    EXPECT_EQ(existingResult.stringResult.value, std::string("initialize-existing-ok"));
    EXPECT_EQ(phraseResult.stringResult.value, std::string("initialize-phrase-ok"));
    EXPECT_TRUE(newResult.noWalletLifecycle);
    EXPECT_TRUE(existingResult.noWalletLifecycle);
    EXPECT_TRUE(phraseResult.noWalletLifecycle);
    EXPECT_TRUE(newResult.noWalletPersistence);
    EXPECT_TRUE(existingResult.noWalletPersistence);
    EXPECT_TRUE(phraseResult.noWalletPersistence);
    EXPECT_TRUE(newResult.noWalletStateMutation);
    EXPECT_TRUE(existingResult.noWalletStateMutation);
    EXPECT_TRUE(phraseResult.noWalletStateMutation);
    EXPECT_EQ(g_liteBridgeRuntimeFakeCallCount, 3);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 3);
}

void testLiteBridgeRuntimeDryDispatchRejectsShutdownRuntime()
{
    resetLiteBridgeRuntimeTrackedFree();
    g_liteBridgeRuntimeFakeCallCount = 0;
    auto runtime = makeReadyTrackedLiteBridgeRuntime();
    runtime.shutdown();

    auto result = runtime.dryDispatchExecute("balance", "{}");
    EXPECT_FALSE(result.ok);
    EXPECT_FALSE(result.fakeCallAttempted);
    EXPECT_EQ(result.status, LiteBridgeRuntimeStatus::Shutdown);
    EXPECT_TRUE(result.error.find("not ready") != std::string::npos);
    EXPECT_EQ(g_liteBridgeRuntimeFakeCallCount, 0);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedFreeCount, 0);
}

void testLiteBridgeRuntimeDryDispatchShutdownUsesTeardownOwner()
{
    resetLiteBridgeRuntimeTrackedFree();
    auto runtime = makeReadyTrackedLiteBridgeRuntime();

    auto result = runtime.dryDispatchShutdown();
    EXPECT_TRUE(result.ok);
    EXPECT_TRUE(result.fakeCallAttempted);
    EXPECT_EQ(result.status, LiteBridgeRuntimeStatus::Shutdown);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);

    result = runtime.dryDispatchShutdown();
    EXPECT_TRUE(result.ok);
    EXPECT_FALSE(result.fakeCallAttempted);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
}

void testLiteBridgeRuntimeDryDispatchShutdownWaitsForOwnedString()
{
    resetLiteBridgeRuntimeTrackedFree();
    auto runtime = makeReadyTrackedLiteBridgeRuntime();
    auto ownedString = runtime.takeOwnedString(makeLiteBridgeRuntimeOwnedCString("phase4-dry"));

    auto result = runtime.dryDispatchShutdown();
    EXPECT_TRUE(result.ok);
    EXPECT_FALSE(result.fakeCallAttempted);
    EXPECT_EQ(result.status, LiteBridgeRuntimeStatus::ShuttingDown);
    EXPECT_TRUE(runtime.shutdownPending());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 0);

    auto stringResult = ownedString.intoResult();
    EXPECT_TRUE(stringResult.ok);
    EXPECT_EQ(stringResult.value, std::string("phase4-dry"));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(2));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[0], std::string("free:phase4-dry"));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[1], std::string("shutdown"));
}

void testLiteBridgeRuntimeFakeDynamicLoaderUnloadWaitsForOwnedStringRelease()
{
    resetLiteBridgeRuntimeTrackedFree();
    auto runtime = makeReadyFakeDynamicLoaderLiteBridgeRuntime(
        "fake-dynamic-library", "phase6-deferred");
    auto ownedString = runtime.takeOwnedString(makeLiteBridgeRuntimeOwnedCString("phase6-owned"));

    EXPECT_TRUE(runtime.dynamicLibraryHandlePresent());
    EXPECT_EQ(runtime.outstandingOwnedStringCount(), static_cast<std::size_t>(1));
    runtime.shutdown();
    EXPECT_EQ(runtime.status(), LiteBridgeRuntimeStatus::ShuttingDown);
    EXPECT_TRUE(runtime.shutdownPending());
    EXPECT_FALSE(runtime.shutdownCalled());
    EXPECT_TRUE(runtime.dynamicLibraryHandlePresent());
    EXPECT_TRUE(runtime.dynamicLibraryUnloadDeferred());
    EXPECT_FALSE(runtime.dynamicLibraryUnloadCalled());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 0);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedUnloadCount, 0);

    auto result = ownedString.intoResult();
    EXPECT_TRUE(result.ok);
    EXPECT_EQ(result.value, std::string("phase6-owned"));
    EXPECT_EQ(runtime.status(), LiteBridgeRuntimeStatus::Shutdown);
    EXPECT_FALSE(runtime.dynamicLibraryHandlePresent());
    EXPECT_TRUE(runtime.dynamicLibraryUnloadCalled());
    EXPECT_FALSE(runtime.dynamicLibraryUnloadDeferred());
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedUnloadCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(3));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[0], std::string("free:phase6-owned"));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[1], std::string("shutdown"));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[2], std::string("unload:phase6-deferred"));
}

void testLiteBridgeRuntimeFakeDynamicLoaderMoveAssignmentUnloadsReplacedHandle()
{
    resetLiteBridgeRuntimeTrackedFree();
    {
        auto firstRuntime = makeReadyFakeDynamicLoaderLiteBridgeRuntime(
            "fake-dynamic-library", "phase6-first");
        auto secondRuntime = makeReadyFakeDynamicLoaderLiteBridgeRuntime(
            "fake-dynamic-library", "phase6-second");

        secondRuntime = std::move(firstRuntime);
        EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
        EXPECT_EQ(g_liteBridgeRuntimeTrackedUnloadCount, 1);
        EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(2));
        EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[0], std::string("shutdown"));
        EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[1], std::string("unload:phase6-second"));
        EXPECT_TRUE(secondRuntime.available());
        EXPECT_TRUE(secondRuntime.dynamicLibraryHandlePresent());
    }

    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 2);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedUnloadCount, 2);
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents.size(), static_cast<std::size_t>(4));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[2], std::string("shutdown"));
    EXPECT_EQ(g_liteBridgeRuntimeTeardownEvents[3], std::string("unload:phase6-first"));
}

void testLiteBridgeRuntimeFakeDynamicLoaderNonFakeSourceBlocksDryDispatch()
{
    resetLiteBridgeRuntimeTrackedFree();
    g_liteBridgeRuntimeFakeCallCount = 0;
    auto runtime = makeReadyFakeDynamicLoaderLiteBridgeRuntime(
        "dynamic-library", "phase6-nonfake");

    EXPECT_TRUE(runtime.available());
    EXPECT_FALSE(runtime.fakeDispatchAllowed());
    auto result = runtime.dryDispatchWalletExists("DRGX");
    EXPECT_FALSE(result.ok);
    EXPECT_FALSE(result.fakeCallAttempted);
    EXPECT_TRUE(result.error.find("fake") != std::string::npos);
    EXPECT_EQ(g_liteBridgeRuntimeFakeCallCount, 0);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedUnloadCount, 0);

    runtime.shutdown();
    EXPECT_EQ(g_liteBridgeRuntimeTrackedShutdownCount, 1);
    EXPECT_EQ(g_liteBridgeRuntimeTrackedUnloadCount, 1);
}

// M0: deterministic injectable fake backend driving the real LiteClientBridge
// (ungated fromApi path). This is the harness M1 service tests reuse.
void testLiteBackendInjectableFakeBridge()
{
    using dragonx::wallet::LiteClientBridge;

    // available() + bool round-trips
    {
        dragonx::test::resetLiteFakeCounters();
        auto bridge = LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi());
        EXPECT_TRUE(bridge.available());
        EXPECT_TRUE(bridge.walletExists("DRGX"));
        EXPECT_TRUE(bridge.checkServerOnline("https://lite.example"));
    }

    // execute() round-trips the canned value and frees the owned string exactly once
    {
        dragonx::test::resetLiteFakeCounters();
        auto bridge = LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi());
        const auto result = bridge.execute("ping", "");  // unknown command -> default fake response
        EXPECT_TRUE(result.ok);
        EXPECT_EQ(result.value, std::string("{\"version\":\"sdxl-fake\"}"));
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, 1L);
        EXPECT_EQ(dragonx::test::g_liteFakeFreed, 1L);
    }

    // lifecycle calls round-trip
    {
        dragonx::test::resetLiteFakeCounters();
        auto bridge = LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi());
        EXPECT_TRUE(bridge.initializeNew(false, "https://lite.example").ok);
        EXPECT_TRUE(bridge.initializeExisting(false, "https://lite.example").ok);
        EXPECT_TRUE(bridge.initializeNewFromPhrase(false, "https://lite.example", "seed words", 0, 0, false).ok);
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, dragonx::test::g_liteFakeFreed);
    }

    // an "Error:"-prefixed backend response maps to ok=false with the message propagated
    {
        dragonx::test::resetLiteFakeCounters();
        auto bridge = LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi());
        const auto result = bridge.execute("boom", "");
        EXPECT_FALSE(result.ok);
        EXPECT_TRUE(result.error.rfind("Error:", 0) == 0);
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, dragonx::test::g_liteFakeFreed);
    }

    // empty command is rejected before reaching the backend (no allocation)
    {
        dragonx::test::resetLiteFakeCounters();
        auto bridge = LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi());
        EXPECT_FALSE(bridge.execute("", "").ok);
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, 0L);
    }

    // an unavailable bridge fails closed (never fakes success)
    {
        dragonx::test::resetLiteFakeCounters();
        auto bridge = LiteClientBridge::unavailable("lite backend is not linked");
        EXPECT_FALSE(bridge.available());
        EXPECT_FALSE(bridge.execute("info", "").ok);
        EXPECT_FALSE(bridge.walletExists("DRGX"));
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, 0L);
    }

    // shutdown is invoked (via destructor) and no owned string leaks or double-frees
    {
        dragonx::test::resetLiteFakeCounters();
        {
            auto bridge = LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi());
            (void)bridge.execute("info", "");
        }
        EXPECT_TRUE(dragonx::test::g_liteFakeShutdownCalled);
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, dragonx::test::g_liteFakeFreed);
    }
}

// M1: the App-owned LiteWalletController drives real create/open/restore through an
// (injected fake) bridge, reports wallet-ready, persists, wipes secrets, and fails closed.
void testLiteWalletControllerLifecycle()
{
    using namespace dragonx::wallet;
    const auto liteCaps = makeWalletCapabilities(WalletBuildKind::Lite, /*embeddedDaemon*/ false, /*liteBackendLinked*/ true);
    const LiteConnectionSettings conn = defaultLiteConnectionSettings();

    // create -> wallet ready, walletOpen() true, persist callback fires once.
    // (No alloc==freed leak assert here: a ready wallet launches the detached sync thread +
    //  refresh worker, which are still in flight at end-of-scope and legitimately hold owned
    //  strings. The leak/double-free invariant is checked in the thread-free bridge test.)
    {
        dragonx::test::resetLiteFakeCounters();
        int persistCount = 0;
        LiteWalletController controller(liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
        controller.setPersistCallback([&persistCount]() { ++persistCount; });
        EXPECT_FALSE(controller.walletOpen());

        LiteWalletCreateRequest req;
        req.passphrase = "hunter2";
        const auto result = controller.createWallet(req);
        EXPECT_TRUE(result.ok);
        EXPECT_TRUE(result.walletReady);
        EXPECT_TRUE(result.operation == LiteWalletLifecycleOperation::CreateNew);
        EXPECT_TRUE(controller.walletOpen());
        EXPECT_EQ(persistCount, 1);
    }

    // open existing -> ready. Regression guard: the real backend's open path
    // (litelib_initialize_existing) returns the bare string "OK", which is NOT valid JSON, so
    // walletReady must key off a non-empty success response, not JSON validity.
    {
        dragonx::test::resetLiteFakeCounters();
        dragonx::test::g_liteFakeWalletExists = true;
        LiteWalletController controller(liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
        LiteWalletOpenRequest req;
        req.passphrase = "hunter2";
        const auto result = controller.openWallet(req);
        EXPECT_TRUE(result.ok);
        EXPECT_TRUE(result.walletReady);
        EXPECT_TRUE(result.operation == LiteWalletLifecycleOperation::OpenExisting);
        EXPECT_TRUE(controller.walletOpen());
    }

    // restore-from-seed round-trips to ready
    {
        dragonx::test::resetLiteFakeCounters();
        LiteWalletController controller(liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
        LiteWalletRestoreRequest req;
        req.seedPhrase = "abandon abandon abandon ... art";
        const auto result = controller.restoreWallet(req);
        EXPECT_TRUE(result.walletReady);
        EXPECT_TRUE(result.operation == LiteWalletLifecycleOperation::RestoreFromSeed);
        EXPECT_TRUE(controller.walletOpen());
    }

    // empty restore seed is rejected before the backend is touched
    {
        dragonx::test::resetLiteFakeCounters();
        LiteWalletController controller(liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
        const auto result = controller.restoreWallet(LiteWalletRestoreRequest{});
        EXPECT_FALSE(result.ok);
        EXPECT_FALSE(result.walletReady);
        EXPECT_FALSE(controller.walletOpen());
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, 0L);
    }

    // bridge calls disabled -> blocked, backend never reached
    {
        dragonx::test::resetLiteFakeCounters();
        LiteWalletController controller(liteCaps, conn,
                                       LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()),
                                       LiteWalletControllerOptions{/*allowBridgeCalls*/ false});
        const auto result = controller.createWallet(LiteWalletCreateRequest{});
        EXPECT_FALSE(result.walletReady);
        EXPECT_FALSE(controller.walletOpen());
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, 0L);
    }

    // full-node capabilities -> unsupported, fails closed
    {
        dragonx::test::resetLiteFakeCounters();
        const auto fullNodeCaps = makeWalletCapabilities(WalletBuildKind::FullNode, true, false);
        LiteWalletController controller(fullNodeCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
        const auto result = controller.createWallet(LiteWalletCreateRequest{});
        EXPECT_FALSE(result.walletReady);
        EXPECT_EQ(dragonx::test::g_liteFakeAlloc, 0L);
    }

    // secret-wipe helper zeroes and clears
    {
        std::string secret = "super-secret-seed-phrase";
        secureWipeLiteSecret(secret);
        EXPECT_TRUE(secret.empty());
    }
}

// Migration: a saved lite chain_name outside {main,test,regtest} (e.g. the legacy
// "DRAGONX" ticker) is rewritten to "main" on load, since the backend hard-panics
// on unknown chains. Valid values are preserved.
void testLiteChainNameMigration()
{
    const fs::path dir = fs::temp_directory_path() / "dragonx_lite_chain_migration_test";
    fs::create_directories(dir);

    {
        const fs::path p = dir / "legacy.json";
        writeTestFile(p, "{\"lite_wallet\":{\"chain_name\":\"DRAGONX\"}}");
        dragonx::config::Settings settings;
        EXPECT_TRUE(settings.load(p.string()));
        EXPECT_EQ(settings.getLiteChainName(), std::string("main"));
    }
    {
        const fs::path p = dir / "valid.json";
        writeTestFile(p, "{\"lite_wallet\":{\"chain_name\":\"test\"}}");
        dragonx::config::Settings settings;
        EXPECT_TRUE(settings.load(p.string()));
        EXPECT_EQ(settings.getLiteChainName(), std::string("test"));
    }

    std::error_code ec;
    fs::remove_all(dir, ec);
}

// M2: a parsed lite refresh bundle maps through to the app's WalletState (the last hop
// the Balance/Receive/Transactions tabs read), with zatoshi->DRGX conversion, z/t address
// split, transaction typing, confirmations, and sync progress.
void testLiteRefreshModelAppliesToWalletState()
{
    using namespace dragonx::wallet;

    LiteWalletRefreshBundle bundle;
    bundle.complete = true;
    bundle.successfulCommandCount = 4;

    bundle.hasBalance = true;
    bundle.balance.shieldedBalance = 200000000;     // 2 DRGX
    bundle.balance.transparentBalance = 100000000;  // 1 DRGX
    bundle.balance.unconfirmedBalance = 50000000;   // 0.5 DRGX

    bundle.hasAddresses = true;
    bundle.addresses.zAddresses = {"zs1shielded"};
    bundle.addresses.tAddresses = {"t1transparent"};

    bundle.hasTransactions = true;
    LiteTransactionRecord rec;
    rec.txid = "deadbeef";
    rec.datetime = 1700000000;
    rec.blockHeight = 990;
    rec.unconfirmed = false;
    rec.direction = LiteTransactionDirection::Receive;
    rec.address = "zs1shielded";
    rec.amount = 150000000;  // 1.5 DRGX
    bundle.transactions.transactions.push_back(rec);

    bundle.hasSyncStatus = true;
    bundle.syncStatus.syncedBlocks = 1000;
    bundle.syncStatus.totalBlocks = 1000;
    bundle.syncStatus.progress = 1.0;
    bundle.syncStatus.complete = true;

    const auto mapped = mapLiteWalletRefreshBundle(bundle);
    EXPECT_TRUE(mapped.ok);

    dragonx::WalletState state;
    applyLiteRefreshModelToWalletState(mapped.model, state);

    EXPECT_NEAR(state.privateBalance, 2.0, 1e-9);
    EXPECT_NEAR(state.transparentBalance, 1.0, 1e-9);
    EXPECT_NEAR(state.totalBalance, 3.0, 1e-9);
    EXPECT_NEAR(state.unconfirmedBalance, 0.5, 1e-9);

    EXPECT_EQ(static_cast<int>(state.addresses.size()), 2);
    EXPECT_EQ(static_cast<int>(state.z_addresses.size()), 1);
    EXPECT_EQ(static_cast<int>(state.t_addresses.size()), 1);
    EXPECT_EQ(state.z_addresses[0].type, std::string("shielded"));
    EXPECT_EQ(state.t_addresses[0].type, std::string("transparent"));

    EXPECT_EQ(static_cast<int>(state.transactions.size()), 1);
    EXPECT_EQ(state.transactions[0].type, std::string("receive"));
    EXPECT_NEAR(state.transactions[0].amount, 1.5, 1e-9);
    EXPECT_EQ(state.transactions[0].confirmations, 11);  // chain 1000 - block 990 + 1

    EXPECT_EQ(state.sync.blocks, 1000);
    EXPECT_EQ(state.sync.headers, 1000);
    EXPECT_FALSE(state.sync.syncing);
}

// M2b: the controller, after a wallet is ready, auto-starts sync and refreshWalletState()
// pulls balance/addresses/transactions/syncstatus through the shared bridge into WalletState.
void testLiteWalletControllerRefreshPopulatesState()
{
    using namespace dragonx::wallet;
    const auto caps = makeWalletCapabilities(WalletBuildKind::Lite, /*embeddedDaemon*/ false, /*liteBackendLinked*/ true);
    const auto conn = defaultLiteConnectionSettings();

    LiteWalletController controller(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
    EXPECT_TRUE(controller.createWallet(LiteWalletCreateRequest{}).walletReady);
    EXPECT_TRUE(controller.walletOpen());
    EXPECT_TRUE(controller.syncStarted());  // auto-started when the wallet became ready

    // Sync runs on a detached thread; the full refresh (balance/addresses) only runs once it
    // completes. Wait for it (instant with the fake) so the refresh is deterministic.
    for (int i = 0; i < 500 && !controller.syncComplete(); ++i)
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    EXPECT_TRUE(controller.syncComplete());

    dragonx::WalletState state;
    EXPECT_TRUE(controller.refreshWalletState(state));
    EXPECT_NEAR(state.privateBalance, 2.0, 1e-9);
    EXPECT_NEAR(state.transparentBalance, 1.0, 1e-9);
    EXPECT_EQ(static_cast<int>(state.z_addresses.size()), 1);
    EXPECT_EQ(static_cast<int>(state.t_addresses.size()), 1);
    EXPECT_EQ(static_cast<int>(state.transactions.size()), 1);
    EXPECT_EQ(state.transactions[0].type, std::string("receive"));
    EXPECT_NEAR(state.transactions[0].amount, 1.5, 1e-9);
    EXPECT_EQ(state.sync.headers, 1000);

    // No wallet open -> refresh is a safe no-op
    LiteWalletController fresh(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
    dragonx::WalletState empty;
    EXPECT_FALSE(fresh.refreshWalletState(empty));
}

// syncstatus parser must accept both real backend shapes: idle {"syncing":"false"} and
// in-progress {"syncing":"true","synced_blocks":N,"total_blocks":M} ("syncing" is a string).
void testLiteSyncStatusParserRealShapes()
{
    using namespace dragonx::wallet;

    // Idle: no block counts, syncing is the string "false".
    {
        const auto p = parseLiteSyncStatusResponse(std::string("{\"syncing\":\"false\"}"));
        EXPECT_TRUE(p.ok);
        EXPECT_EQ(static_cast<long long>(p.syncStatus.syncedBlocks), 0LL);
        EXPECT_TRUE(p.syncStatus.complete);
    }
    // In-progress: block counts present.
    {
        const auto p = parseLiteSyncStatusResponse(
            std::string("{\"syncing\":\"true\",\"synced_blocks\":900,\"total_blocks\":1000}"));
        EXPECT_TRUE(p.ok);
        EXPECT_EQ(static_cast<long long>(p.syncStatus.syncedBlocks), 900LL);
        EXPECT_EQ(static_cast<long long>(p.syncStatus.totalBlocks), 1000LL);
        EXPECT_FALSE(p.syncStatus.complete);
        EXPECT_NEAR(p.syncStatus.progress, 0.9, 1e-9);
    }
    // While syncing, missing block counts is still an error.
    {
        const auto p = parseLiteSyncStatusResponse(std::string("{\"syncing\":\"true\"}"));
        EXPECT_FALSE(p.ok);
    }
}

// Per-address balances: unspent notes/utxos are summed per address (spent ones excluded)
// into the WalletState address list, instead of aggregate-only zeros.
void testLitePerAddressBalances()
{
    using namespace dragonx::wallet;

    LiteWalletRefreshBundle bundle;
    bundle.complete = true;
    bundle.hasAddresses = true;
    bundle.addresses.zAddresses = {"zs1aaa"};
    bundle.addresses.tAddresses = {"t1bbb"};
    bundle.hasNotes = true;

    LiteSpendableOutput note;       note.address = "zs1aaa"; note.value = 150000000; note.createdInTxid = "n1";
    LiteSpendableOutput utxo;       utxo.address = "t1bbb";  utxo.value = 50000000;  utxo.createdInTxid = "u1";
    LiteSpendableOutput spentNote;  spentNote.address = "zs1aaa"; spentNote.value = 999; spentNote.spent = true; spentNote.createdInTxid = "n2";
    bundle.notes.unspentNotes.push_back(note);
    bundle.notes.unspentNotes.push_back(spentNote);  // excluded: spent
    bundle.notes.utxos.push_back(utxo);

    const auto mapped = mapLiteWalletRefreshBundle(bundle);
    EXPECT_TRUE(mapped.ok);

    dragonx::WalletState state;
    applyLiteRefreshModelToWalletState(mapped.model, state);
    EXPECT_EQ(static_cast<int>(state.z_addresses.size()), 1);
    EXPECT_EQ(static_cast<int>(state.t_addresses.size()), 1);
    EXPECT_NEAR(state.z_addresses[0].balance, 1.5, 1e-9);  // 150000000; the spent note is excluded
    EXPECT_NEAR(state.t_addresses[0].balance, 0.5, 1e-9);  // 50000000
}

// M3: new-address generation via the controller (backend "new" zs/R) returns the address.
void testLiteWalletControllerNewAddress()
{
    using namespace dragonx::wallet;
    const auto caps = makeWalletCapabilities(WalletBuildKind::Lite, /*embeddedDaemon*/ false, /*liteBackendLinked*/ true);
    const auto conn = defaultLiteConnectionSettings();

    LiteWalletController controller(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
    EXPECT_TRUE(controller.createWallet(LiteWalletCreateRequest{}).walletReady);

    const auto z = controller.newAddress(/*shielded*/ true);
    EXPECT_TRUE(z.ok);
    EXPECT_TRUE(z.address.rfind("zs1", 0) == 0);

    const auto t = controller.newAddress(/*shielded*/ false);
    EXPECT_TRUE(t.ok);
    EXPECT_TRUE(t.address.rfind("R1", 0) == 0);

    // No wallet open -> error, no address.
    LiteWalletController idle(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
    const auto none = idle.newAddress(true);
    EXPECT_FALSE(none.ok);
}

// Gateway hardening: one command's parse failure must not abort the whole refresh — the
// other commands still populate the bundle (graceful degradation against real-shape drift).
void testLiteWalletGatewayRefreshSkipsFailedCommand()
{
    using namespace dragonx::wallet;
    const auto caps = makeWalletCapabilities(WalletBuildKind::Lite, /*embeddedDaemon*/ false, /*liteBackendLinked*/ true);
    const auto conn = defaultLiteConnectionSettings();

    auto bridge = LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi());
    LiteWalletGateway gateway(caps, conn, &bridge, LiteWalletGatewayOptions{/*allowBridgeCalls*/ true});

    dragonx::test::g_liteFakeBadBalance.store(true);  // make only the balance command fail to parse
    const auto result = gateway.refresh(LiteWalletRefreshRequest{});
    dragonx::test::g_liteFakeBadBalance.store(false);

    EXPECT_TRUE(result.ok);                       // partial success, not a total failure
    EXPECT_FALSE(result.bundle.complete);         // not all commands succeeded
    EXPECT_FALSE(result.bundle.hasBalance);       // the failed command is skipped
    EXPECT_TRUE(result.bundle.hasAddresses);      // the others still populate
    EXPECT_TRUE(result.bundle.hasTransactions);
    EXPECT_TRUE(result.bundle.hasInfo);
}

// M2b-3: opening a wallet auto-starts the background worker, which produces a refresh model
// the main thread can pick up via takeRefreshedModel() and apply to WalletState.
void testLiteWalletControllerWorkerProducesModel()
{
    using namespace dragonx::wallet;
    const auto caps = makeWalletCapabilities(WalletBuildKind::Lite, /*embeddedDaemon*/ false, /*liteBackendLinked*/ true);
    const auto conn = defaultLiteConnectionSettings();

    LiteWalletController controller(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
    EXPECT_TRUE(controller.createWallet(LiteWalletCreateRequest{}).walletReady);  // auto-starts the worker

    // The worker publishes progress-only models while syncing, then full models once synced.
    // Poll until a full (balance-bearing) model arrives (sync is instant with the fake).
    LiteWalletAppRefreshModel model;
    bool gotFull = false;
    for (int i = 0; i < 500 && !gotFull; ++i) {
        LiteWalletAppRefreshModel m;
        if (controller.takeRefreshedModel(m) && m.hasBalance) {
            model = m;
            gotFull = true;
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    EXPECT_TRUE(gotFull);
    EXPECT_TRUE(model.hasBalance);
    EXPECT_TRUE(model.hasAddresses);

    dragonx::WalletState state;
    applyLiteRefreshModelToWalletState(model, state);
    EXPECT_NEAR(state.privateBalance, 2.0, 1e-9);

    // Idle controller (no wallet -> no worker) has nothing pending.
    LiteWalletController idle(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
    LiteWalletAppRefreshModel none;
    EXPECT_FALSE(idle.takeRefreshedModel(none));
}

// M2b-3 hardening: the backend `sync` is a blocking, uninterruptible full scan. Destroying the
// controller while a sync is in flight must NOT hang (the sync thread is detached, not joined).
void testLiteWalletControllerShutdownDoesNotHangDuringSync()
{
    using namespace dragonx::wallet;
    const auto caps = makeWalletCapabilities(WalletBuildKind::Lite, /*embeddedDaemon*/ false, /*liteBackendLinked*/ true);
    const auto conn = defaultLiteConnectionSettings();

    dragonx::test::g_liteFakeSyncBlock.store(true);  // make the backend "sync" block indefinitely
    const auto start = std::chrono::steady_clock::now();
    {
        LiteWalletController controller(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
        controller.createWallet(LiteWalletCreateRequest{});  // launches the (now-blocked) sync thread
        EXPECT_TRUE(controller.syncStarted());
        EXPECT_FALSE(controller.syncComplete());
        // controller destructs here with the sync thread still blocked -> must return promptly.
    }
    const auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::steady_clock::now() - start).count();
    EXPECT_TRUE(elapsedMs < 1500);  // did not wait for the (blocked) sync to finish

    dragonx::test::g_liteFakeSyncBlock.store(false);  // release the detached sync thread
    std::this_thread::sleep_for(std::chrono::milliseconds(50));  // let it unwind cleanly
}

} // namespace

int main()
{
    testConnectionConfig();
    testPaymentUri();
    testAmountFormatting();
    testSpendableFiltering();
    testRefreshScheduler();
    testNetworkRefreshService();
    testNetworkRefreshSnapshotHelpers();
    testNetworkRefreshRpcCollectors();
    testNetworkRefreshResultModels();
    testOperationStatusPollParsing();
    testWalletSecurityController();
    testWalletSecurityWorkflow();
    testWalletSecurityWorkflowExecutor();
    testDaemonShutdownPolicy();
    testDaemonLifecycleExecution();
    testDaemonLifecycleAdapters();
    testRendererHelpers();
    testConsoleInputModel();
    testMiningBenchmarkModel();
    testBalanceAddressListModel();
    testExplorerBlockCache();
    testTransactionHistoryCache();
    testTransactionHistoryCacheRefreshApply();
    testLiteBridgeOwnedStringCopiesBeforeFreeOnSuccess();
    testLiteBridgeOwnedStringClassifiesNullWithoutFree();
    testLiteBridgeOwnedStringClassifiesErrorAndFreesOnce();
    testLiteBridgeOwnedStringMovePreventsDoubleFree();
    testLiteClientBridgeUsesRuntimeOwnedStringCleanup();
    testLiteBackendInjectableFakeBridge();
    testLiteWalletControllerLifecycle();
    testLiteChainNameMigration();
    testLiteRefreshModelAppliesToWalletState();
    testLitePerAddressBalances();
    testLiteWalletControllerNewAddress();
    testLiteSyncStatusParserRealShapes();
    testLiteWalletControllerRefreshPopulatesState();
    testLiteWalletGatewayRefreshSkipsFailedCommand();
    testLiteWalletControllerWorkerProducesModel();
    testLiteWalletControllerShutdownDoesNotHangDuringSync();
    testLiteBridgeRuntimeShutdownIsIdempotent();
    testLiteBridgeRuntimeDestructorCallsShutdownOnce();
    testLiteBridgeRuntimeShutdownWaitsForOwnedStringRelease();
    testLiteBridgeRuntimeDestructorShutdownWaitsForExternalOwnedString();
    testLiteBridgeRuntimeMoveAssignmentShutsDownReplacedRuntime();
    testLiteBridgeRuntimeDryDispatchBoolWrappersAreFakeOnly();
    testLiteBridgeRuntimeDryDispatchExecuteOwnsReturnedString();
    testLiteBridgeRuntimeDryDispatchLifecycleWrappersRemainFakeOnly();
    testLiteBridgeRuntimeDryDispatchRejectsShutdownRuntime();
    testLiteBridgeRuntimeDryDispatchShutdownUsesTeardownOwner();
    testLiteBridgeRuntimeDryDispatchShutdownWaitsForOwnedString();
    testLiteBridgeRuntimeFakeDynamicLoaderUnloadWaitsForOwnedStringRelease();
    testLiteBridgeRuntimeFakeDynamicLoaderMoveAssignmentUnloadsReplacedHandle();
    testLiteBridgeRuntimeFakeDynamicLoaderNonFakeSourceBlocksDryDispatch();
    testLiteBridgeRuntimeUnavailableDoesNotCallShutdown();
    testGeneratedResourceBehavior();

    if (g_failures != 0) {
        std::cerr << g_failures << " test assertion(s) failed\n";
        return 1;
    }
    std::cout << "Focused service tests passed\n";
    return 0;
}
