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

#include "exchange_info.h"

#include <nlohmann/json.hpp>

#include <algorithm>
#include <unordered_map>

namespace dragonx {
namespace data {

const std::vector<ExchangeInfo>& getExchangeRegistry()
{
    // Offline seed / fallback. The live list from CoinGecko (parseCoinGeckoTickers)
    // supersedes this when available; keep the known venues here so the tab is never
    // empty without network.
    static const std::vector<ExchangeInfo> registry = {
        {
            "Nonkyc.io",
            "https://nonkyc.io",
            {
                {"DRGX", "USDT", "DRGX/USDT", "https://nonkyc.io/market/DRGX_USDT", "nonkyc_io"},
            }
        },
        {
            "OurBit",
            "https://www.ourbit.com",
            {
                {"DRGX", "USDT", "DRGX/USDT", "https://www.ourbit.com/exchange/DRGX_USDT", "ourbit"},
            }
        },
    };
    return registry;
}

namespace {
// Extract the scheme://host origin from a URL (for ExchangeInfo.baseUrl).
std::string originOf(const std::string& url)
{
    const auto scheme = url.find("://");
    if (scheme == std::string::npos) return url;
    const auto host = url.find('/', scheme + 3);
    return (host == std::string::npos) ? url : url.substr(0, host);
}
} // namespace

std::vector<ExchangeInfo> parseCoinGeckoTickers(const std::string& body)
{
    std::vector<ExchangeInfo> exchanges;
    try {
        const nlohmann::json j = nlohmann::json::parse(body);
        if (!j.contains("tickers") || !j["tickers"].is_array()) return {};

        std::unordered_map<std::string, size_t> byName;  // exchange name -> index
        for (const auto& t : j["tickers"]) {
            if (!t.is_object()) continue;
            const std::string base   = t.value("base", std::string{});
            const std::string target = t.value("target", std::string{});
            std::string market, identifier;
            if (t.contains("market") && t["market"].is_object()) {
                market     = t["market"].value("name", std::string{});
                identifier = t["market"].value("identifier", std::string{});  // keys the per-exchange candle adapter
            }
            const std::string tradeUrl = t.value("trade_url", std::string{});
            if (base.empty() || target.empty() || market.empty()) continue;
            double lastUsd = 0.0;   // this venue's current USD price + 24h volume — differ per exchange
            if (t.contains("converted_last") && t["converted_last"].is_object())
                lastUsd = t["converted_last"].value("usd", 0.0);
            double volumeUsd = 0.0;
            if (t.contains("converted_volume") && t["converted_volume"].is_object())
                volumeUsd = t["converted_volume"].value("usd", 0.0);

            auto it = byName.find(market);
            if (it == byName.end()) {
                byName[market] = exchanges.size();
                exchanges.push_back(ExchangeInfo{market, originOf(tradeUrl), {}});
                it = byName.find(market);
            }
            ExchangePair pair{base, target, base + "/" + target, tradeUrl, identifier, lastUsd, volumeUsd};
            // Skip duplicate pairs on the same exchange.
            bool dup = false;
            for (const auto& p : exchanges[it->second].pairs)
                if (p.displayName == pair.displayName) { dup = true; break; }
            if (!dup) exchanges[it->second].pairs.push_back(std::move(pair));
        }
    } catch (...) {
        return {};
    }
    // Drop exchanges that ended up with no pairs (defensive).
    exchanges.erase(std::remove_if(exchanges.begin(), exchanges.end(),
                    [](const ExchangeInfo& e) { return e.pairs.empty(); }),
                    exchanges.end());
    return exchanges;
}

} // namespace data
} // namespace dragonx
