// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
//
// App-owned controller that drives the lite wallet. It constructs and owns the
// lite services with bridge calls ENABLED (the services otherwise default to
// allowBridgeCalls=false and are never instantiated), executes real create/open/
// restore operations through the linked SDXL backend, securely wipes secrets after
// use, tracks wallet-open state, and invokes a persistence callback on success.
//
// Construction:
//   - Production:  LiteWalletController::createLinked(caps, connectionSettings)
//                  (uses LiteClientBridge::linkedSdxl(); requires DRAGONX_ENABLE_LITE_BACKEND).
//   - Tests:       construct directly with an injected bridge
//                  (e.g. LiteClientBridge::fromApi(makeFakeLiteApi())).

#pragma once

#include "lite_client_bridge.h"
#include "lite_connection_service.h"
#include "lite_wallet_lifecycle_service.h"
#include "lite_wallet_gateway.h"
#include "lite_sync_service.h"
#include "lite_wallet_state_mapper.h"
#include "wallet_backend.h"
#include "wallet_capabilities.h"

#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <thread>

namespace dragonx {
struct WalletState;  // data/wallet_state.h

namespace wallet {

// Securely zero and clear a string holding secret material (seed/passphrase).
void secureWipeLiteSecret(std::string& secret);

// Apply a normalized lite refresh model onto the app's WalletState (the last hop the
// existing Balance/Receive/Transactions tabs read from). Converts zatoshis -> DRGX,
// splits addresses into shielded/transparent, and maps sync progress. Mutates in place
// (WalletState is non-copyable); only sections present in the model are touched.
void applyLiteRefreshModelToWalletState(const LiteWalletAppRefreshModel& model,
                                        dragonx::WalletState& state);

struct LiteWalletControllerOptions {
    bool allowBridgeCalls = true;
};

struct LiteNewAddressResult {
    bool ok = false;
    std::string address;
    std::string error;
};

class LiteWalletController {
public:
    LiteWalletController(WalletCapabilities capabilities,
                         LiteConnectionSettings connectionSettings,
                         LiteClientBridge bridge,
                         LiteWalletControllerOptions options = LiteWalletControllerOptions{});

    ~LiteWalletController();  // stops + joins the background refresh worker

    LiteWalletController(const LiteWalletController&) = delete;
    LiteWalletController& operator=(const LiteWalletController&) = delete;

    // Production factory: links the SDXL backend compiled into this build.
    static std::unique_ptr<LiteWalletController> createLinked(
        WalletCapabilities capabilities,
        LiteConnectionSettings connectionSettings);

    // Invoked after a wallet becomes ready, so the owner can persist settings.
    void setPersistCallback(std::function<void()> callback) { persist_ = std::move(callback); }

    bool walletOpen() const { return walletOpen_; }
    const WalletBackendStatus& status() const { return status_; }
    LiteWalletLifecycleAvailability availability() const { return lifecycle_.availability(); }

    // Execute a real lifecycle operation. The request is taken by value; its secret
    // fields are securely wiped before returning. On a ready wallet, walletOpen()
    // becomes true and the persist callback (if any) fires.
    LiteWalletLifecycleResult createWallet(LiteWalletCreateRequest request);
    LiteWalletLifecycleResult openWallet(LiteWalletOpenRequest request);
    LiteWalletLifecycleResult restoreWallet(LiteWalletRestoreRequest request);

    bool syncStarted() const { return syncStarted_; }
    bool syncComplete() const { return syncDone_ && syncDone_->load(); }

    // Launch the backend sync on a detached background thread (NON-blocking; the backend's
    // `sync` command runs a full, uninterruptible chain scan). Auto-invoked when a lifecycle
    // op produces a ready wallet; safe to call once.
    void startSync();

    // Generate a new address (shielded if true, else transparent) via the backend. Fast (local
    // key derivation), safe to call on the UI thread; the next refresh lists the new address.
    LiteNewAddressResult newAddress(bool shielded);

    // Poll sync status + fetch balance/addresses/transactions, and apply the result into the
    // app's WalletState. Returns true if state was updated. Safe no-op when no wallet is open.
    // Synchronous (blocks on the backend); used by tests and as the worker's unit of work.
    bool refreshWalletState(dragonx::WalletState& state);

    // Synchronous refresh that returns the mapped model (or nullopt). Pure w.r.t. shared
    // state; safe to call from the background worker. nullopt when no wallet is open or the
    // refresh produced nothing usable.
    std::optional<LiteWalletAppRefreshModel> refreshModel();

    // Main-thread handoff: if the background worker has produced a fresh model since the last
    // call, move it into `out` and return true. Apply it with applyLiteRefreshModelToWalletState.
    bool takeRefreshedModel(LiteWalletAppRefreshModel& out);

private:
    void onLifecycleResult(const LiteWalletLifecycleResult& result);
    void startWorker();
    void stopWorker();
    void workerLoop();

    // The bridge is shared (not just owned) so the detached, uninterruptible sync thread can
    // safely outlive the controller: it holds a ref, so the underlying bridge is destroyed
    // (and litelib_shutdown called) only once BOTH the controller and a running sync release it.
    //
    // Concurrent execute() is intentionally NOT guarded by a C++ mutex here. The design relies
    // on overlapping calls (the detached sync thread runs the long `sync` while the refresh
    // worker polls `syncstatus`, and the UI thread may call `new`). litelib serializes wallet
    // access internally: LightClient.wallet is an Arc<RwLock<LightWallet>> (do_new_address takes
    // write(), do_balance takes read(), do_sync_internal takes read()/write() and writes
    // sync_status separately), so concurrent execute() calls cannot corrupt state. A coarse
    // mutex would instead serialize sync against syncstatus polling and defeat the design.
    std::shared_ptr<LiteClientBridge> bridge_;
    LiteWalletLifecycleService lifecycle_;
    LiteWalletGateway gateway_;
    LiteSyncService sync_;
    std::function<void()> persist_;
    std::atomic<bool> walletOpen_{false};
    std::atomic<bool> syncStarted_{false};
    WalletBackendStatus status_;  // written only on the main thread (lifecycle ops)

    // Detached background sync (backend `sync` is a blocking, uninterruptible full scan).
    std::thread syncThread_;
    std::atomic<bool> syncLaunched_{false};  // startSync() guard (set on the main thread)
    std::shared_ptr<std::atomic<bool>> syncDone_ = std::make_shared<std::atomic<bool>>(false);

    // Joinable background refresh worker (fast iterations: syncstatus, plus data once synced).
    std::thread worker_;
    std::atomic<bool> running_{false};
    std::mutex wakeMutex_;
    std::condition_variable wakeCv_;
    std::mutex modelMutex_;
    std::optional<LiteWalletAppRefreshModel> pendingModel_;  // guarded by modelMutex_
    static constexpr int kRefreshIntervalMs = 2000;
};

} // namespace wallet
} // namespace dragonx
