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

#pragma once

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

#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#else
#include <sys/types.h>
#endif

namespace dragonx {
namespace daemon {

/**
 * @brief Manages the xmrig pool mining process
 *
 * Handles starting/stopping xmrig for pool mining, polling stats via its
 * HTTP API, and capturing stdout for the log panel.  Modelled on
 * EmbeddedDaemon (same fork/pipe/monitor pattern, same #ifdef split).
 */
class XmrigManager {
public:
    enum class State { Stopped, Starting, Running, Stopping, Error };

    /// Live stats polled from xmrig HTTP API (/2/summary)
    struct PoolStats {
        double hashrate_10s  = 0;
        double hashrate_60s  = 0;
        double hashrate_15m  = 0;
        int64_t accepted     = 0;
        int64_t rejected     = 0;
        int64_t uptime_sec   = 0;
        double  pool_diff    = 0;
        std::string pool_url;
        std::string algo;
        std::string version;      // miner version reported by the running xmrig API
        bool connected       = false;
        // Memory usage
        int64_t memory_free  = 0;   // bytes
        int64_t memory_total = 0;   // bytes
        int64_t memory_used  = 0;   // bytes (resident set size)
        int     threads_active = 0; // actual mining threads
        // Pool-side hashrate (from pool stats API, not xmrig)
        double pool_hashrate = 0;
    };

    /// User-facing config (maps 1:1 to UI fields / Settings)
    struct Config {
        std::string pool_url        = "pool.dragonx.is:3433";
        std::string wallet_address;
        std::string worker_name     = "x";
        std::string algo            = "rx/hush";
        int  threads                = 0;   // 0 = xmrig auto
        bool tls                    = false;
        bool hugepages              = true;
    };

    XmrigManager();
    ~XmrigManager();

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

    /**
     * @brief Start xmrig with the given config.
     * Generates a JSON config file, finds the binary, and spawns the process.
     */
    bool start(const Config& cfg);

    /**
     * @brief Stop xmrig gracefully (SIGTERM → wait → SIGKILL).
     */
    void stop(int wait_ms = 5000);

    bool  isRunning() const;
    State getState() const { return state_.load(std::memory_order_relaxed); }

    // Return COPIES under lock: stats_ and last_error_ are mutated by the monitor thread while the UI
    // thread reads them, so handing out a reference is a torn-read / use-after-free race (M-03, M-04).
    PoolStats getStats() const { std::lock_guard<std::mutex> lk(stats_mutex_); return stats_; }
    std::string getLastError() const { std::lock_guard<std::mutex> lk(error_mutex_); return last_error_; }

    /// Thread count requested at start() — available immediately, unlike
    /// PoolStats::threads_active which requires an API response.
    int getRequestedThreads() const { return threads_; }

    /**
     * @brief Get last N lines of xmrig stdout (thread-safe snapshot).
     */
    std::vector<std::string> getRecentLines(int maxLines = 30) const;

    /**
     * @brief Get new output since a given offset (thread-safe).
     * Returns the new text and updates offset to the current size.
     */
    std::string getOutputSince(size_t& offset) const {
        std::lock_guard<std::mutex> lk(output_mutex_);
        if (offset >= process_output_.size()) {
            offset = process_output_.size();
            return {};
        }
        std::string result = process_output_.substr(offset);
        offset = process_output_.size();
        return result;
    }

    /**
     * @brief Get current output size (thread-safe, no copy)
     */
    size_t getOutputSize() const {
        std::lock_guard<std::mutex> lk(output_mutex_);
        return process_output_.size();
    }

    /**
     * @brief Poll the xmrig HTTP API for live stats.
     * Lightweight — reads cached stats updated by the monitor thread.
     * Called from App::update() every ~2 s while running.
     */
    void pollStats();

    /**
     * @brief Get xmrig process memory usage in MB (from OS, not API).
     */
    double getMemoryUsageMB() const;

    /**
     * @brief Find xmrig binary in standard locations.
     */
    static std::string findXmrigBinary();

    /**
     * @brief Kick a one-shot background `<binary> --version` detection (idempotent).
     * Lets the UI show the installed miner's version before mining is ever started.
     */
    static void startVersionDetection();

    /**
     * @brief Cached installed-miner version parsed by startVersionDetection().
     * Empty until detection completes (or if no binary/parse failed). Thread-safe.
     */
    static std::string installedVersion();

private:
    bool generateConfig(const Config& cfg, const std::string& outPath);
    bool startProcess(const std::string& xmrigPath, const std::string& cfgPath, int threads);
    void monitorProcess();
    void drainOutput();
    void appendOutput(const char* data, size_t len);
    // Set last_error_ under error_mutex_ (writers run on both the main thread and the monitor thread).
    void setLastError(std::string e) { std::lock_guard<std::mutex> lk(error_mutex_); last_error_ = std::move(e); }
    void fetchStatsHttp();  // Blocking HTTP call — runs on monitor thread only
    void fetchPoolApiStats();  // Fetch pool-side stats (hashrate) from pool HTTP API

    std::atomic<State> state_{State::Stopped};
    std::string last_error_;
    mutable std::mutex error_mutex_;   // guards last_error_ (written by main + monitor threads)

    mutable std::mutex output_mutex_;
    std::string process_output_;

    // xmrig HTTP API credentials (random per session)
    int         api_port_  = 0;
    std::string api_token_;
    int         threads_   = 0;  // Thread count for mining
    std::string pool_host_;       // Pool hostname for stats API
    PoolStats   stats_;
    mutable std::mutex stats_mutex_;

    // Process handles
#ifdef _WIN32
    HANDLE process_handle_ = nullptr;
    HANDLE stdout_read_    = nullptr;
#else
    pid_t process_pid_     = 0;
    int   stdout_fd_       = -1;
#endif

    std::thread monitor_thread_;
    std::atomic<bool> should_stop_{false};
};

} // namespace daemon
} // namespace dragonx
