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

#pragma once

#include <string>
#include <vector>
#include <mutex>
#include <atomic>
#include <thread>

namespace dragonx {
namespace util {

/**
 * @brief Bootstrap downloader + extractor for DRAGONX blockchain data.
 *
 * Downloads a blockchain snapshot zip from bootstrap.dragonx.is, verifies
 * SHA-256 / MD5 checksums, extracts it into the DRAGONX data directory
 * (skipping wallet.dat), and cleans old chain data before extraction.
 * All work runs on a background thread; progress is queried thread-safely
 * from the UI thread.
 */
class Bootstrap {
public:
    enum class State {
        Idle,
        Downloading,
        Verifying,
        Extracting,
        Completed,
        Failed
    };

    struct Progress {
        State   state          = State::Idle;
        double  downloaded_bytes = 0;
        double  total_bytes    = 0;       // 0 if server doesn't send Content-Length
        float   percent        = 0.0f;    // 0..100
        std::string status_text;          // human-readable status
        std::string error;                // non-empty on failure
    };

    Bootstrap() = default;
    ~Bootstrap();

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

    /// Base URL for bootstrap downloads (zip + checksum files).
    static constexpr const char* kBaseUrl = "https://bootstrap.dragonx.is";
    static constexpr const char* kMirrorUrl = "https://bootstrap2.dragonx.is";
    static constexpr const char* kZipName = "DRAGONX.zip";

    /// Start the bootstrap process on a background thread.
    /// @param dataDir  Path to DRAGONX data dir (e.g. ~/.hush/DRAGONX/)
    /// @param url      Bootstrap zip URL
    void start(const std::string& dataDir,
               const std::string& url = "https://bootstrap.dragonx.is/DRAGONX.zip");

    /// Cancel an in-progress download/extract.
    void cancel();

    /// Thread-safe read of current progress.
    Progress getProgress() const;

    /// Whether the operation is finished (success or failure).
    bool isDone() const;

    /// Format a byte count as human-readable string (e.g. "1.24 GB")
    static std::string formatSize(double bytes);

private:
    mutable std::mutex mutex_;
    Progress progress_;
    std::atomic<bool> cancel_requested_{false};
    std::thread worker_;
    std::atomic<bool> worker_running_{false};

    bool download(const std::string& url, const std::string& destZip);
    bool extract(const std::string& zipPath, const std::string& dataDir);
    void cleanChainData(const std::string& dataDir);
    void setProgress(State state, const std::string& text, double downloaded = 0, double total = 0);

    /// Download a small text file (checksum) into a string. Returns empty on failure.
    std::string downloadSmallFile(const std::string& url);

    /// Compute SHA-256 of a file, return lowercase hex digest.
    /// pctBase/pctRange map file progress onto a portion of the overall percent.
    std::string computeSHA256(const std::string& filePath, float pctBase = 0.0f, float pctRange = 100.0f);

    /// Compute MD5 of a file, return lowercase hex digest.
    /// pctBase/pctRange map file progress onto a portion of the overall percent.
    std::string computeMD5(const std::string& filePath, float pctBase = 0.0f, float pctRange = 100.0f);

    /// Parse the first hex token from a checksum file (handles "<hash>  <filename>" format).
    static std::string parseChecksumFile(const std::string& content);

    /// Verify downloaded zip against remote checksums. Returns true if valid.
    bool verifyChecksums(const std::string& zipPath, const std::string& baseUrl);

    /// Perform a HEAD request to get remote file size.
    /// Returns -1 on failure.
    long long getRemoteFileSize(const std::string& url);

    /// Byte offset of the partial file when resuming a download.
    /// Added to dlnow in progress reports so the bar reflects true total.
    double resume_offset_{0};

    // libcurl progress callback (static → calls into instance via clientp)
    static int progressCallback(void* clientp, long long dltotal, long long dlnow,
                                long long ultotal, long long ulnow);
};

} // namespace util
} // namespace dragonx
