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

#pragma once

#include <memory>
#include <string>
#include <functional>
#include <thread>
#include <atomic>
#include <chrono>
#include <unordered_map>
#include <unordered_set>
#include "data/transaction_history_cache.h"
#include "data/wallet_state.h"
#include "rpc/connection.h"
#include "services/network_refresh_service.h"
#include "services/wallet_security_controller.h"
#include "services/wallet_security_workflow.h"
#include "util/async_task_manager.h"
#include "wallet/wallet_capabilities.h"
#include "ui/sidebar.h"
#include "ui/windows/console_tab.h"
#include "imgui.h"

// Forward declarations
namespace dragonx {
    namespace rpc { 
        class RPCClient;
        class RPCWorker;
    }
    namespace config { class Settings; }
    namespace daemon { class DaemonController; class EmbeddedDaemon; class XmrigManager; }
    namespace util { class Bootstrap; class SecureVault; }
    namespace wallet { class LiteWalletController; }
}

namespace dragonx {

/**
 * @brief First-run wizard states
 */
enum class WizardPhase {
    None,               // No wizard — normal operation
    BootstrapOffer,     // Step 1: offer bootstrap download
    BootstrapInProgress,// downloading / extracting
    BootstrapFailed,    // error with retry/skip option
    Appearance,         // Step 2: visual effects / performance options
    EncryptOffer,       // Step 3: offer wallet encryption
    EncryptInProgress,  // encrypting + daemon restarting
    PinSetup,           // Step 4: optional PIN setup (after encryption)
    Done                // wizard complete, launch normally
};

/**
 * @brief Encrypt-wallet dialog phases (settings page flow)
 */
enum class EncryptDialogPhase {
    PassphraseEntry,    // Enter & confirm passphrase
    Encrypting,         // In-progress animation
    PinSetup,           // Offer PIN after successful encryption
    Done                // Finished — close dialog
};

/**
 * @brief Main application class
 * 
 * Manages application state, RPC connection, and coordinates UI rendering.
 */
class App {
public:
    App();
    ~App();

    // Non-copyable
    App(const App&) = delete;
    App& operator=(const App&) = delete;

    /**
     * @brief Initialize the application
     * @return true if initialization succeeded
     */
    bool init();

    /**
     * @brief Update application state (called every frame)
     */
    void update();

    /**
     * @brief Pre-frame tasks that must run BEFORE ImGui::NewFrame().
     *
     * Font atlas rebuilds (hot-reload, user font scale changes) must
     * happen before NewFrame() because NewFrame() caches font pointers.
     * Rebuilding mid-frame causes dangling pointer crashes.
     */
    void preFrame();

    /**
     * @brief Render the application UI (called every frame)
     */
    void render();

    /**
     * @brief Clean shutdown
     */
    void shutdown();

    /**
     * @brief Check if app should exit (render loop can stop)
     * Enforces minimum 1-second display of shutdown screen so user sees feedback.
     */
    bool shouldQuit() const {
        if (!quit_requested_ || !shutdown_complete_) return false;
        auto elapsed = std::chrono::steady_clock::now() - shutdown_start_time_;
        return elapsed >= std::chrono::seconds(1);
    }

    /**
     * @brief Request application exit — begins async shutdown
     */
    void requestQuit();

    /**
     * @brief Begin graceful shutdown (called on window close)
     * Starts daemon stop on a background thread while UI keeps rendering.
     */
    void beginShutdown();

    /**
     * @brief Whether we are in the shutdown phase
     */
    bool isShuttingDown() const { return shutting_down_; }
    wallet::WalletCapabilities walletCapabilities() const { return wallet::currentWalletCapabilities(); }
    bool isLiteBuild() const { return wallet::isLiteBuild(walletCapabilities()); }
    bool supportsEmbeddedDaemon() const { return wallet::supportsEmbeddedDaemon(walletCapabilities()); }
    bool supportsFullNodeLifecycleActions() const { return wallet::supportsFullNodeLifecycleActions(walletCapabilities()); }
    bool supportsSoloMining() const { return wallet::supportsSoloMining(walletCapabilities()); }
    bool supportsPoolMining() const { return wallet::supportsPoolMining(walletCapabilities()); }
    bool supportsLiteBackend() const { return wallet::supportsLiteBackend(walletCapabilities()); }

    /**
     * @brief Render the shutdown overlay (called instead of normal UI during shutdown)
     */
    void renderShutdownScreen();

    /**
     * @brief Render loading overlay in content area while daemon is starting/syncing
     * @param contentH Height of the content area child window
     */
    void renderLoadingOverlay(float contentH);

    // Accessors for subsystems
    rpc::RPCClient* rpc() { return rpc_.get(); }
    rpc::RPCWorker* worker() { return worker_.get(); }
    config::Settings* settings() { return settings_.get(); }
    // Lite wallet controller (non-null only in lite builds with a linked backend).
    wallet::LiteWalletController* liteWallet() { return lite_wallet_.get(); }
    // Reason the lite wallet failed to auto-open this session (empty if none / opened OK).
    const std::string& liteOpenError() const { return lite_open_error_; }
    // Show the lite send-time unlock modal (called when a spend is attempted on a locked wallet).
    void requestLiteUnlock() { lite_unlock_prompt_ = true; }
    // (Re)build the lite controller from current settings so a changed lite-server selection
    // takes effect. No-op on non-lite/unlinked builds; preserves a live wallet (see app.cpp).
    void rebuildLiteWallet(bool force = false);
    WalletState& state() { return state_; }
    const WalletState& state() const { return state_; }
    const WalletState& getWalletState() const { return state_; }

    // Connection state (convenience wrappers)
    bool isConnected() const { return state_.connected; }
    int getBlockHeight() const { return state_.sync.blocks; }
    const std::string& getConnectionStatus() const { return connection_status_; }
    const std::string& getDaemonStatus() const { return daemon_status_; }

    // Balance info (convenience wrappers)
    double getShieldedBalance() const { return state_.shielded_balance; }
    double getTransparentBalance() const { return state_.transparent_balance; }
    double getTotalBalance() const { return state_.total_balance; }
    double getUnconfirmedBalance() const { return state_.unconfirmed_balance; }

    // Addresses
    const std::vector<AddressInfo>& getZAddresses() const { return state_.z_addresses; }
    const std::vector<AddressInfo>& getTAddresses() const { return state_.t_addresses; }

    // Transactions
    const std::vector<TransactionInfo>& getTransactions() const { return state_.transactions; }

    // Mining
    const MiningInfo& getMiningInfo() const { return state_.mining; }
    void startMining(int threads);
    void stopMining();
    bool isMiningToggleInProgress() const { return mining_toggle_in_progress_.load(std::memory_order_relaxed); }

    // Pool mining (xmrig)
    void startPoolMining(int threads);
    void stopPoolMining();
    int getXmrigRequestedThreads() const {
        return xmrig_manager_ ? xmrig_manager_->getRequestedThreads() : 0;
    }
    // True while the pool miner process is live — used to refuse replacing the binary under it.
    bool isPoolMinerRunning() const {
        return xmrig_manager_ && xmrig_manager_->isRunning();
    }

    // Mine-when-idle state query
    bool isIdleMiningActive() const { return idle_mining_active_; }

    // Peers
    const std::vector<PeerInfo>& getPeers() const { return state_.peers; }
    const std::vector<BannedPeer>& getBannedPeers() const { return state_.bannedPeers; }
    bool isPeerRefreshInProgress() const {
        return network_refresh_.jobInProgress(services::NetworkRefreshService::Job::Peers);
    }
    void banPeer(const std::string& ip, int duration_seconds = 86400);
    void unbanPeer(const std::string& ip);
    void clearBans();

    // Address operations
    void createNewZAddress(std::function<void(const std::string&)> callback = nullptr);
    void createNewTAddress(std::function<void(const std::string&)> callback = nullptr);

    // Hide/unhide addresses from the address list (persisted in settings)
    void hideAddress(const std::string& addr);
    void unhideAddress(const std::string& addr);
    bool isAddressHidden(const std::string& addr) const;
    int getHiddenAddressCount() const;

    void favoriteAddress(const std::string& addr);
    void unfavoriteAddress(const std::string& addr);
    bool isAddressFavorite(const std::string& addr) const;

    // Address metadata (labels, icons, custom ordering)
    void setAddressLabel(const std::string& addr, const std::string& label);
    void setAddressIcon(const std::string& addr, const std::string& icon);
    std::string getAddressLabel(const std::string& addr) const;
    std::string getAddressIcon(const std::string& addr) const;
    int getAddressSortOrder(const std::string& addr) const;
    void setAddressSortOrder(const std::string& addr, int order);
    int getNextSortOrder() const;
    void swapAddressOrder(const std::string& a, const std::string& b);
    bool isMiningAddress(const std::string& addr) const;
    void setMiningAddress(const std::string& addr, bool mining);
    void invalidateAddressValidationCache();

    // Key export/import
    void exportPrivateKey(const std::string& address, std::function<void(const std::string&)> callback);
    void exportAllKeys(std::function<void(const std::string&)> callback);
    void importPrivateKey(const std::string& key, std::function<void(bool, const std::string&)> callback);
    
    // Wallet backup
    void backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback);

    // Transaction operations
    void sendTransaction(const std::string& from, const std::string& to,
                        double amount, double fee, const std::string& memo,
                        std::function<void(bool success, const std::string& result)> callback);

    // Register a daemon async operation id (z_shieldcoinbase / z_mergetoaddress /
    // auto-shield) with the shared opid poller so its eventual success/failure is
    // surfaced and balances/transactions refresh on completion. z_sendmany uses the
    // richer pending-send path internally; this is for operations with no optimistic
    // transaction row of their own.
    void trackOperation(const std::string& opid);

    // Force refresh
    void refreshNow();
    void refreshMiningInfo();
    void refreshPeerInfo();
    void refreshMarketData();
    
    /// @brief Per-category refresh intervals, adjusted by active tab
    using RefreshIntervals = services::NetworkRefreshService::Intervals;

    /// @brief Get recommended refresh intervals for a given page
    static RefreshIntervals getIntervalsForPage(ui::NavPage page);

    // UI navigation
    void setCurrentPage(ui::NavPage page);
    ui::NavPage getCurrentPage() const { return current_page_; }
    
    // Dialog triggers (used by settings page to open modal dialogs)
    void showImportKeyDialog() { show_import_key_ = true; }
    void showExportKeyDialog() { show_export_key_ = true; }
    void showBackupDialog() { show_backup_ = true; }
    void showAboutDialog() { show_about_ = true; }
    
    // Legacy tab compat — maps int to NavPage
    void setCurrentTab(int tab);
    int getCurrentTab() const { return static_cast<int>(current_page_); }
    
    // Payment URI handling
    void handlePaymentURI(const std::string& uri);
    bool hasPendingPayment() const { return pending_payment_valid_; }
    void clearPendingPayment() { pending_payment_valid_ = false; }
    std::string getPendingToAddress() const { return pending_to_address_; }
    double getPendingAmount() const { return pending_amount_; }
    std::string getPendingMemo() const { return pending_memo_; }
    std::string getPendingLabel() const { return pending_label_; }
    
    // Embedded daemon control
    bool startEmbeddedDaemon();
    void stopEmbeddedDaemon();
    bool isEmbeddedDaemonRunning() const;
    bool isUsingEmbeddedDaemon() const { return supportsEmbeddedDaemon() && use_embedded_daemon_; }
    void setUseEmbeddedDaemon(bool use) { use_embedded_daemon_ = use && supportsEmbeddedDaemon(); }
    void rescanBlockchain();  // restart daemon with -rescan flag
    void deleteBlockchainData();  // stop daemon, delete chain data, restart fresh
    bool stopDaemonForBootstrap(); // stop daemon + disconnect for bootstrap, returns true if was running
    bool isBootstrapDownloading() const { return bootstrap_downloading_; }
    void setBootstrapDownloading(bool v) { bootstrap_downloading_ = v; }

    // Get daemon memory usage in MB (uses embedded daemon handle if available,
    // falls back to platform-level process scan for external daemons)
    double getDaemonMemoryUsageMB() const;

    // Diagnostic string describing daemon memory detection path (for debugging)
    const std::string& getDaemonMemDiag() const { return daemon_mem_diag_; }

    // Background gradient overlay texture
    void setGradientTexture(ImTextureID tex) { gradient_tex_ = tex; }
    ImTextureID getGradientTexture() const { return gradient_tex_; }

    // Logo texture accessor (wallet branding icon)
    ImTextureID getLogoTexture() const { return logo_tex_; }
    int getLogoWidth() const { return logo_w_; }
    int getLogoHeight() const { return logo_h_; }

    // Coin logo texture accessor (DragonX currency icon for balance tab)
    ImTextureID getCoinLogoTexture() const { return coin_logo_tex_; }

    /**
     * @brief Reload theme images (background gradient + logo) from new paths
     * @param bgPath Path to background image override (empty = use default)
     * @param logoPath Path to logo image override (empty = use default)
     */
    void reloadThemeImages(const std::string& bgPath, const std::string& logoPath);

    // Wizard / first-run
    WizardPhase getWizardPhase() const { return wizard_phase_; }
    bool isFirstRun() const;

    /**
     * @brief Stop daemon and re-run the setup wizard
     * Called from Settings. Daemon restarts automatically when wizard completes.
     */
    void restartWizard();

    /**
     * @brief Restart the embedded daemon (e.g. after changing debug categories)
     * Shows "Restarting daemon..." in the loading overlay while the daemon cycles.
     */
    void restartDaemon();

    // Wallet encryption helpers
    void encryptWalletWithPassphrase(const std::string& passphrase);
    void unlockWallet(const std::string& passphrase, int timeout);
    void lockWallet();
    void changePassphrase(const std::string& oldPass, const std::string& newPass);

    // Dialog triggers for encryption (from settings page)
    void showEncryptDialog() {
        show_encrypt_dialog_ = true;
        encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry;
        encrypt_status_.clear();
        memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
        memset(encrypt_confirm_buf_, 0, sizeof(encrypt_confirm_buf_));
        memset(enc_dlg_pin_buf_, 0, sizeof(enc_dlg_pin_buf_));
        memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
        enc_dlg_saved_passphrase_.clear();
        enc_dlg_pin_status_.clear();
    }
    void showChangePassphraseDialog() { show_change_passphrase_ = true; }
    void showDecryptDialog() {
        show_decrypt_dialog_ = true;
        wallet_security_workflow_.reset();
        memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
    }

    // Dialog triggers for PIN (from settings page)
    void showPinSetupDialog() { show_pin_setup_ = true; pin_status_.clear(); }
    void showPinChangeDialog() { show_pin_change_ = true; pin_status_.clear(); }
    void showPinRemoveDialog() { show_pin_remove_ = true; pin_status_.clear(); }
    bool hasPinVault() const;

    /// @brief Check if RPC worker has queued results waiting to be processed
    bool hasPendingRPCResults() const;
    bool hasTransactionSendProgress() const { return send_progress_active_ || send_submissions_in_flight_ > 0 || !pending_opids_.empty(); }
    std::string transactionSendProgressText() const;
    std::string transactionRefreshProgressText() const;

    // Copy a SECRET (seed phrase, private key) to the clipboard and arm an auto-clear: after a
    // short delay the clipboard is wiped IF it still holds this secret (so we don't clobber
    // something the user copied afterwards). Only a hash of the secret is retained, never the
    // plaintext. Call pumpSecretClipboardClear() each frame to action the clear.
    void copySecretToClipboard(const std::string& secret);
    void pumpSecretClipboardClear();
    bool isTransactionRefreshInProgress() const {
        return network_refresh_.jobInProgress(services::NetworkRefreshService::Job::Transactions);
    }

private:
    friend class AppDaemonLifecycleRuntime;
    friend class AppDaemonLifecycleTaskContext;

    bool sendStopCommandSafely(rpc::RPCClient& client, const char* context);
    void maybeFinishTransactionSendProgress();
    void upsertPendingSendTransaction(const std::string& opid,
                                      const std::string& from,
                                      const std::string& to,
                                      double amount,
                                      const std::string& memo);
    void markPendingSendTransactionSucceeded(const std::string& opid,
                                             const std::string& txid);
    void removePendingSendTransactions(const std::vector<std::string>& opids,
                                       bool restoreBalances);
    // Deliver a deferred z_sendmany result to its waiting UI callback once the opid
    // reaches a terminal status. Returns true if a callback was registered (and fired).
    bool invokeSendResultCallback(const std::string& opid, bool ok,
                                  const std::string& result);
    void applyPendingSendBalanceDeltas(bool includeAggregateBalances);
    std::string transactionHistoryCacheWalletIdentity() const;
    bool ensureTransactionHistoryCacheUnlockedFor(const std::string& walletIdentity);
    void unlockTransactionHistoryCacheWithPassphrase(const std::string& passphrase);
    void loadTransactionHistoryCacheIfAvailable();
    void storeTransactionHistoryCacheIfAvailable();
    void wipePendingTransactionHistoryCachePassphrase();
    void resetTransactionHistoryCacheSession();
    void pruneShieldedHistoryScanProgress();
    void invalidateShieldedHistoryScanProgress(bool persistCache);

    // Subsystems
    std::unique_ptr<rpc::RPCClient> rpc_;
    std::unique_ptr<rpc::RPCWorker> worker_;

    // Fast-lane: dedicated RPC connection + worker for 1-second mining polls.
    // Runs on its own thread with its own curl handle so it never blocks behind
    // the main refresh batch.
    std::unique_ptr<rpc::RPCClient> fast_rpc_;
    std::unique_ptr<rpc::RPCWorker> fast_worker_;

    // Saved connection credentials (needed to open the fast-lane connection)
    rpc::ConnectionConfig saved_config_;

    std::unique_ptr<config::Settings> settings_;
    std::unique_ptr<wallet::LiteWalletController> lite_wallet_;  // lite builds w/ linked backend
    // Pending send_tab callback for an in-flight lite send (delivered in update() once the
    // controller's async broadcast result arrives). Only one lite send runs at a time.
    std::function<void(bool, const std::string&)> lite_send_callback_;
    // One-shot guard: auto-open an existing lite wallet on the first update() tick (kept off
    // init() so a slow initialize_existing network call doesn't freeze startup before the window).
    bool lite_autoopen_done_ = false;
    double lite_open_last_attempt_ = 0.0;  // ImGui time of the last async open attempt (retry timer)
    // Reason an existing lite wallet failed to auto-open (e.g. server unreachable). Surfaced in
    // the UI so a stuck "disconnected" state isn't silent; cleared once a wallet opens.
    std::string lite_open_error_;
    // Lite first-run welcome prompt: dismissed for the session once the user picks an action.
    bool lite_firstrun_dismissed_ = false;
    // Lite send-time unlock: set to show the unlock modal when a spend is attempted while locked.
    bool lite_unlock_prompt_ = false;
    // One-shot: prompt to unlock on startup once we learn the auto-opened wallet is encrypted+locked.
    bool lite_startup_lock_checked_ = false;
    std::unique_ptr<daemon::DaemonController> daemon_controller_;
    std::unique_ptr<daemon::XmrigManager> xmrig_manager_;
    util::AsyncTaskManager async_tasks_;
    bool pending_antivirus_dialog_ = false;  // Show Windows Defender help dialog

    // Wallet state
    WalletState state_;

    // Shutdown state
    std::atomic<bool> shutting_down_{false};
    std::atomic<bool> shutdown_complete_{false};
    bool address_list_dirty_ = false;   // P8: dedup rebuildAddressList
    std::string shutdown_status_;
    std::thread shutdown_thread_;
    float shutdown_timer_ = 0.0f;
    bool force_quit_confirm_ = false;
    std::chrono::steady_clock::time_point shutdown_start_time_;

    // Daemon restart (e.g. after changing debug log categories)
    std::atomic<bool> daemon_restarting_{false};

    // Encryption state check timeout
    float encryption_check_timer_ = 0.0f;

    // UI State
    bool quit_requested_ = false;
    bool show_demo_window_ = false;
    bool show_settings_ = false;
    bool show_about_ = false;
    bool show_import_key_ = false;
    bool show_export_key_ = false;
    bool show_backup_ = false;
    bool show_address_book_ = false;
    
    // Embedded daemon state
    bool use_embedded_daemon_ = wallet::supportsEmbeddedDaemon(wallet::currentWalletCapabilities());
    std::string daemon_status_;
    mutable std::string daemon_mem_diag_;  // diagnostic info for daemon memory detection
    size_t daemon_output_offset_ = 0;      // for incremental output parsing (rescan detection)
    
    // Export/Import state
    std::string export_result_;
    char import_key_input_[512] = {0};
    std::string export_address_;
    std::string import_status_;
    bool import_success_ = false;
    std::string backup_status_;
    bool backup_success_ = false;

    // Connection
    std::string connection_status_ = "Disconnected";
    bool connection_in_progress_ = false;
    bool remote_rpc_plaintext_warning_shown_ = false;
    // Startup daemon-launch diagnostics: bound the "RPC port busy, no config" wait before warning,
    // and show the embedded-daemon start failure (binary/params/spawn) only once. Reset on connect.
    int daemon_wait_attempts_ = 0;
    bool daemon_start_error_shown_ = false;
    int daemon_last_seen_crashes_ = 0;  // surface each new embedded-daemon crash reason once
    bool refresh_policy_syncing_ = false;  // whether the sync-throttle refresh profile is active
    // Auto-clear for secrets copied to the clipboard. Only a hash of the copied secret is kept.
    std::uint64_t clipboard_secret_hash_ = 0;
    double clipboard_clear_deadline_ = 0.0;
    float loading_timer_ = 0.0f;  // spinner animation for loading overlay

    // Current page (sidebar navigation)
    ui::NavPage current_page_ = ui::NavPage::Overview;
    ui::NavPage prev_page_    = ui::NavPage::Overview;
    float page_alpha_         = 1.0f;  // 0→1 fade on page switch
    bool  sidebar_collapsed_  = false; // true = icon-only mode
    bool  sidebar_user_toggled_ = false; // user manually toggled — suppress auto-collapse
    float sidebar_width_anim_ = 0.0f;  // animated width (0 = uninitialized)
    float prev_dpi_scale_     = 0.0f;  // detect DPI changes to snap sidebar width

    // Background gradient overlay
    ImTextureID gradient_tex_ = 0;

    // Logo texture (reload on skin change / dark↔light switch)
    ImTextureID logo_tex_ = 0;
    int logo_w_ = 0;
    int logo_h_ = 0;
    bool logo_loaded_ = false;
    bool logo_is_dark_variant_ = true; // tracks which variant is currently loaded

    // Coin logo texture (DragonX currency icon, separate from wallet branding)
    ImTextureID coin_logo_tex_ = 0;
    int coin_logo_w_ = 0;
    int coin_logo_h_ = 0;
    bool coin_logo_loaded_ = false;

    // Console tab
    ui::ConsoleTab console_tab_;
    
    // Pending payment from URI
    bool pending_payment_valid_ = false;
    std::string pending_to_address_;
    double pending_amount_ = 0.0;
    std::string pending_memo_;
    std::string pending_label_;

    // Per-category refresh timers, policy, and worker queue guards.
    services::NetworkRefreshService network_refresh_;
    int mining_slow_counter_ = 0;  // counts fast ticks; fires slow refresh every N

    // Mining toggle guard (prevents concurrent setgenerate calls)
    std::atomic<bool> mining_toggle_in_progress_{false};

    // Auto-shield guard (prevents concurrent auto-shield operations)
    std::atomic<bool> auto_shield_pending_{false};

    // P4: Incremental transaction cache
    int last_tx_block_height_ = -1;  // block height at last full tx fetch
    static constexpr int MAX_VIEWTX_PER_CYCLE = 25; // cap z_viewtransaction calls per refresh
    std::size_t shielded_history_scan_cursor_ = 0;
    bool shielded_history_scan_pending_ = false;
    // False until the first full shielded-history scan finishes. Drives the History tab's
    // "Loading older history…" progress so the user knows transactions are still streaming in
    // after the first batch appears; goes quiet for the routine per-block re-scans afterward.
    bool initial_history_scan_complete_ = false;
    std::unordered_map<std::string, int> shielded_history_scan_heights_;

    // P4b: z_viewtransaction result cache — avoids re-calling the RPC for
    // txids we've already enriched.  Keyed by txid.
    using ViewTxCacheEntry = services::NetworkRefreshService::TransactionViewCacheEntry;
    services::NetworkRefreshService::TransactionViewCache viewtx_cache_;

    // P4c: Confirmed transaction cache — deeply-confirmed txns (>= 10 confs)
    // are accumulated here and reused across refresh cycles.  Only
    // recent/unconfirmed txns are re-fetched from the daemon each time.
    std::vector<TransactionInfo> confirmed_tx_cache_;
    std::unordered_set<std::string> confirmed_tx_ids_;  // fast lookup
    int confirmed_cache_block_ = -1;  // block height when cache was last built

    // Dirty flags for demand-driven refresh
    bool addresses_dirty_ = true;           // true → refreshAddresses() will run
    bool address_validation_cache_dirty_ = true;
    bool transactions_dirty_ = false;       // true → force tx refresh regardless of block height
    bool encryption_state_prefetched_ = false; // suppress duplicate getwalletinfo on connect
    bool rescan_status_poll_in_progress_ = false;
    // True once we've actually observed the rescan running (daemon restarted into -rescan warmup).
    // Gates the "rescan complete" detection so a getrescaninfo poll that hits the still-running
    // pre-restart daemon (which reports rescanning=false) can't fire a false "complete" instantly.
    bool rescan_confirmed_active_ = false;
    bool opid_poll_in_progress_ = false;
    // Consecutive Core-refresh cycles where BOTH core RPCs failed → likely a dead
    // connection. After kCoreFailuresBeforeDisconnect, tear down and reconnect.
    int consecutive_core_failures_ = 0;

    // Pending z_sendmany operation tracking
    bool send_progress_active_ = false;
    int send_submissions_in_flight_ = 0;
    std::vector<std::string> pending_opids_;  // opids to poll for completion
    struct PendingSendInfo {
        std::string from;
        std::string to;
        std::string memo;
        double amount = 0.0;
        std::int64_t timestamp = 0;
    };
    std::unordered_map<std::string, PendingSendInfo> pending_send_info_;
    // z_sendmany UI callbacks held until the opid reaches a terminal status, so the
    // user isn't told "sent successfully" before the tx is actually built/broadcast.
    std::unordered_map<std::string, std::function<void(bool, const std::string&)>>
        pending_send_callbacks_;
    // Txids from completed z_sendmany operations.
    // Ensures shielded sends are discoverable by z_viewtransaction
    // even when they don't appear in listtransactions or
    // z_listreceivedbyaddress.
    std::unordered_set<std::string> send_txids_;

    // First-run wizard state
    WizardPhase wizard_phase_ = WizardPhase::None;
    std::unique_ptr<util::Bootstrap> bootstrap_;
    bool bootstrap_downloading_ = false;  // true while settings bootstrap dialog is active
    std::string wizard_pending_passphrase_;  // held until daemon connects
    std::string wizard_saved_passphrase_;    // held until PinSetup completes/skipped

    // Wallet security flow state shared by wizard/settings encryption paths.
    services::WalletSecurityController wallet_security_;
    services::WalletSecurityWorkflow wallet_security_workflow_;

    // Wizard: stopping an external daemon before bootstrap
    bool wizard_stopping_external_ = false;
    std::string wizard_stop_status_;

    // PIN vault
    std::unique_ptr<util::SecureVault> vault_;
    data::TransactionHistoryCache transaction_history_cache_;
    std::string pending_transaction_history_cache_passphrase_;
    bool transaction_history_cache_loaded_ = false;

    // Lock screen state
    bool lock_screen_was_visible_ = false; // tracks lock→unlock transitions for auto-focus
    bool lock_use_pin_ = true;  // true = PIN input, false = passphrase input
    char lock_pin_buf_[16] = {};
    char lock_passphrase_buf_[256] = {};
    std::string lock_error_msg_;
    float lock_error_timer_ = 0.0f;
    int lock_attempts_ = 0;
    float lock_lockout_timer_ = 0.0f;
    bool lock_unlock_in_progress_ = false;

    // Encrypt wallet dialog state
    bool show_encrypt_dialog_ = false;
    bool show_change_passphrase_ = false;
    EncryptDialogPhase encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry;
    char encrypt_pass_buf_[256] = {};
    char encrypt_confirm_buf_[256] = {};
    char change_old_pass_buf_[256] = {};
    char change_new_pass_buf_[256] = {};
    char change_confirm_buf_[256] = {};
    std::string encrypt_status_;
    bool encrypt_in_progress_ = false;
    std::string enc_dlg_saved_passphrase_;   // held for PIN setup after encrypt
    char enc_dlg_pin_buf_[16] = {};
    char enc_dlg_pin_confirm_buf_[16] = {};
    std::string enc_dlg_pin_status_;

    // PIN setup dialog state (settings page)
    bool show_pin_setup_ = false;
    bool show_pin_change_ = false;
    bool show_pin_remove_ = false;
    char pin_buf_[16] = {};
    char pin_confirm_buf_[16] = {};
    char pin_old_buf_[16] = {};
    char pin_passphrase_buf_[256] = {};
    std::string pin_status_;
    bool pin_in_progress_ = false;

    // Decrypt wallet dialog state
    bool show_decrypt_dialog_ = false;
    char decrypt_pass_buf_[256] = {};

    // Wizard PIN setup state
    char wizard_pin_buf_[16] = {};
    char wizard_pin_confirm_buf_[16] = {};
    std::string wizard_pin_status_;

    // Auto-lock on idle
    std::chrono::steady_clock::time_point last_interaction_ = std::chrono::steady_clock::now();

    // Mine-when-idle: auto-start/stop mining based on system idle state
    bool idle_mining_active_ = false;  // true when mining was auto-started by idle detection
    bool idle_scaled_to_idle_ = false; // true when threads have been scaled up for idle

    // Private methods - rendering
    void renderStatusBar();
    void renderAboutDialog();
    void renderLiteFirstRunPrompt();  // lite-only welcome modal when no wallet exists yet
    void renderLiteUnlockPrompt();    // lite-only send-time unlock modal
    void renderImportKeyDialog();
    void renderExportKeyDialog();
    void renderBackupDialog();
    void renderFirstRunWizard();
    void renderLockScreen();
    void renderEncryptWalletDialog();
    void renderDecryptWalletDialog();
    void renderPinDialogs();
    void renderAntivirusHelpDialog();
    void processDeferredEncryption();

    // Private methods - connection
    void tryConnect();
    void onConnected();
    void onDisconnected(const std::string& reason);
    // Set the "node is initializing" UI state (status line + overlay description) from the
    // embedded/external daemon's launch state and its own console output (current phase + block
    // height), so a connect probe that times out while the daemon loads shows WHAT it's doing.
    // `reachableButBusy` is true when the probe connected but got no RPC reply (a timeout),
    // false when the daemon is merely launching (not bound yet). Returns the status title.
    std::string applyDaemonInitStatus(bool reachableButBusy);
    // Tear down a connection that died mid-session (daemon crash / restart / dropped
    // socket) so update()'s reconnect branch re-enters tryConnect(). Unlike onDisconnected
    // alone, this also rpc_->disconnect()s so rpc_->isConnected() actually flips to false.
    void handleLostConnection(const std::string& reason);
    void applyDefaultBanlist();

    // Private methods - data refresh
    void refreshData();           // Orchestrator: dispatches per-category refreshes
    void refreshCoreData();       // Balance + blockchain info (can use fast_worker_)
    void refreshAddressData();    // Address lists + balances
    void refreshTransactionData(); // Transaction list + z_viewtransaction enrichment
    void refreshRecentTransactionData(); // Lightweight recent/unconfirmed tx poll
    bool refreshEncryptionState(); // Wallet encryption/lock state
    void refreshBalance();        // Legacy: balance-only refresh (used by specific callers)
    void refreshAddresses();      // Legacy: standalone address refresh
    void refreshPrice();
    void refreshWalletEncryptionState();
    void applyRefreshPolicy(ui::NavPage page);
    bool currentPageNeedsWalletDataRefresh() const;
    bool shouldRunWalletTransactionRefresh() const;
    bool shouldRefreshTransactions() const;
    bool shouldRefreshRecentTransactions() const;
    void checkAutoLock();
    void checkIdleMining();
};

} // namespace dragonx
