// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
//
// PoolRegistry — the small table of official DragonX mining pools plus the pure
// (no-I/O) helpers used to compare their hashrate and pick one for auto-balance.
//
// A pool is more than a URL: the two official pools speak DIFFERENT stats APIs
// (pool.dragonx.is = a custom /api/stats; pool.dragonx.cc = Miningcore /api/pools)
// and mine with DIFFERENT algo strings (rx/hush vs rx/dragonx), so each entry
// carries how to read its hashrate and which algo xmrig must be told to use.
//
// Everything here is pure and unit-tested (see tests/test_phase4.cpp); the live
// HTTP fetch lives in pool_stats_service.{h,cpp}.

#pragma once

#include <random>
#include <string>
#include <vector>

namespace dragonx {
namespace util {

// How to read a pool's total hashrate from its public stats JSON.
enum class PoolStatsSchema {
    DragonXIs,    // GET /api/stats  ->  pools.<name>.hashrate                     (custom)
    Miningcore,   // GET /api/pools  ->  pools[id==poolId].poolStats.poolHashrate  (Miningcore)
};

struct KnownPool {
    std::string     id;               // stable internal id, e.g. "dragonx-cc-pplns"
    std::string     label;            // human label for the UI (host, sans port)
    std::string     stratum;          // host:port the miner connects to
    std::string     algo;             // xmrig algo string for THIS pool
    std::string     statsUrl;         // absolute https URL of the stats endpoint
    PoolStatsSchema schema = PoolStatsSchema::DragonXIs;
    std::string     miningcorePoolId; // Miningcore pool id (empty for DragonXIs)
    double          feePercent = 0.0; // pool fee, for display only
    bool            official = true;
};

// A pool's live total hashrate sample (H/s). ok=false when the fetch/parse failed.
struct PoolHashrate {
    std::string id;
    double      hashrateHs = 0.0;
    bool        ok = false;
    // Live pool fee (%) read from the same stats JSON. <0 means "not available" —
    // callers fall back to the compile-time KnownPool.feePercent.
    double      feePercent = -1.0;
};

// The built-in official pools (PPLNS only — never a SOLO pool, whose hashrate is
// meaningless to balance against). Stable order.
const std::vector<KnownPool>& knownPools();

// The pool in `pools` whose stratum matches `url` (host, and port when both specify
// one), or nullptr. `url` may be a bare host, host:port, or carry a scheme/path.
const KnownPool* findPoolByUrl(const std::vector<KnownPool>& pools, const std::string& url);

// Same, over the built-in official pools only.
const KnownPool* findKnownPoolByUrl(const std::string& url);

// The full list the UI should show: the official knownPools(), plus a row for every
// user-saved pool URL and for `currentPoolUrl` when it isn't one of those — so a
// custom/bookmarked pool is a first-class, selectable row. Synthetic (user) rows are
// official=false and carry no statsUrl (feePercent<0, no live hashrate), and endpoints
// are de-duplicated so a saved URL that equals an official pool isn't listed twice.
std::vector<KnownPool> effectivePools(const std::string& currentPoolUrl,
                                      const std::vector<std::string>& savedPoolUrls);

// The algo xmrig must use for `url`: the matching known pool's algo, else `fallback`.
std::string resolvePoolAlgo(const std::string& url, const std::string& fallback);

// Parse a pool's total hashrate (H/s) out of its stats JSON per `schema`.
// `miningcorePoolId` selects the pool entry for the Miningcore schema (ignored
// otherwise). Sets ok=false and returns 0 on any parse failure / missing field.
double parsePoolHashrate(PoolStatsSchema schema, const std::string& json,
                         const std::string& miningcorePoolId, bool& ok);

// Parse a pool's advertised fee (%) out of the same stats JSON (DragonXIs:
// pools.<name>.poolFee; Miningcore: pools[id].poolFeePercent). Selects the same
// pool entry as parsePoolHashrate. Sets ok=false and returns 0 when the field is
// absent / malformed, so the caller keeps the compile-time fallback.
double parsePoolFee(PoolStatsSchema schema, const std::string& json,
                    const std::string& miningcorePoolId, bool& ok);

// Weighted-random pick among the usable (ok==true) pools: probability is inversely
// proportional to hashrate (smaller pools favored), so miners spread out instead of
// all stampeding to the single lowest pool. The current pool (`currentId`, may be
// empty) gets a stickiness multiplier so re-balancing rarely churns the miner.
// Returns the chosen pool id; returns `currentId` (or the sole usable id) when fewer
// than two pools are usable.
std::string chooseWeightedPool(const std::vector<PoolHashrate>& pools,
                               const std::string& currentId,
                               std::mt19937& rng);

// Stickiness applied to the incumbent pool's weight in chooseWeightedPool.
inline constexpr double kIncumbentStayBias = 2.0;

} // namespace util
} // namespace dragonx
