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

#include "market_tab.h"
#include "../../app.h"
#include "../../config/version.h"
#include "../../data/wallet_state.h"
#include "../../config/settings.h"
#include "../../data/exchange_info.h"
#include "../../util/i18n.h"
#include "../schema/ui_schema.h"
#include "../material/type.h"
#include "../material/draw_helpers.h"
#include "../material/colors.h"
#include "../material/typography.h"
#include "../../embedded/IconsMaterialDesign.h"
#include "../../util/platform.h"
#include "../layout.h"
#include "imgui.h"

#include <algorithm>
#include <cmath>
#include <ctime>

namespace dragonx {
namespace ui {

using namespace material;

// ---- Market tab persistent state ----
static std::vector<double> s_price_history;  // mirror of WalletState market.price_history
static bool s_history_initialized = false;
static double s_last_refresh_time = 0.0;

// Exchange / pair selection
static int s_exchange_idx = 0;
static int s_pair_idx = 0;
static float s_pair_scroll = 0.0f;
static float s_pair_scroll_target = 0.0f;
static bool s_pair_dragging = false;
static float s_pair_drag_start_x = 0.0f;
static float s_pair_drag_start_scroll = 0.0f;
static bool s_market_state_loaded = false;

// Helper: load selected exchange/pair from settings
static void LoadMarketState(config::Settings* settings)
{
    if (s_market_state_loaded || !settings) return;
    s_market_state_loaded = true;

    const auto& registry = data::getExchangeRegistry();
    std::string savedExchange = settings->getSelectedExchange();
    std::string savedPair = settings->getSelectedPair();

    for (int ei = 0; ei < (int)registry.size(); ei++) {
        if (registry[ei].name == savedExchange) {
            s_exchange_idx = ei;
            for (int pi = 0; pi < (int)registry[ei].pairs.size(); pi++) {
                if (registry[ei].pairs[pi].displayName == savedPair) {
                    s_pair_idx = pi;
                    break;
                }
            }
            break;
        }
    }
}

// Helper: format compact currency
static std::string FormatCompactUSD(double val)
{
    char buf[64];
    if (val >= 1e9)        snprintf(buf, sizeof(buf), "$%.2fB", val / 1e9);
    else if (val >= 1e6)   snprintf(buf, sizeof(buf), "$%.2fM", val / 1e6);
    else if (val >= 1e3)   snprintf(buf, sizeof(buf), "$%.2fK", val / 1e3);
    else                   snprintf(buf, sizeof(buf), "$%.2f", val);
    return std::string(buf);
}

// Helper: format price to sensible precision
static std::string FormatPrice(double price)
{
    char buf[64];
    if (price >= 0.01)         snprintf(buf, sizeof(buf), "$%.4f", price);
    else if (price >= 0.0001)  snprintf(buf, sizeof(buf), "$%.6f", price);
    else                       snprintf(buf, sizeof(buf), "$%.8f", price);
    return std::string(buf);
}

void RenderMarketTab(App* app)
{
    auto& S = schema::UI();
    auto summaryPanel   = S.table("tabs.market", "summary-panel");
    auto btcPriceLbl    = S.label("tabs.market", "btc-price-label");
    auto change24hLbl   = S.label("tabs.market", "change-24h-label");
    auto volumeLbl      = S.label("tabs.market", "volume-label");
    auto volumeValLbl   = S.label("tabs.market", "volume-value-label");
    auto mktCapLbl      = S.label("tabs.market", "market-cap-label");
    auto mktCapValLbl   = S.label("tabs.market", "market-cap-value-label");
    auto chartElem      = S.drawElement("tabs.market", "chart");
    auto portfolioValLbl = S.label("tabs.market", "portfolio-value-label");
    auto portfolioBtcLbl = S.label("tabs.market", "portfolio-btc-label");
    const auto& state = app->getWalletState();
    const auto& market = state.market;

    // Load persisted exchange/pair on first frame
    LoadMarketState(app->settings());

    // Exchange registry
    const auto& registry = data::getExchangeRegistry();
    if (s_exchange_idx >= (int)registry.size()) s_exchange_idx = 0;
    const auto& currentExchange = registry[s_exchange_idx];
    if (s_pair_idx >= (int)currentExchange.pairs.size()) s_pair_idx = 0;

    // Non-scrolling container — content resizes to fit available height
    ImVec2 marketAvail = ImGui::GetContentRegionAvail();
    ImGui::BeginChild("##MarketScroll", marketAvail, false,
        ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);

    // Responsive: scale factors per frame
    float availWidth = ImGui::GetContentRegionAvail().x;
    float hs = Layout::hScale(availWidth);
    float vs = Layout::vScale(marketAvail.y);
    float pad = Layout::cardInnerPadding();
    float gap = Layout::cardGap();

    ImDrawList* dl = ImGui::GetWindowDrawList();
    GlassPanelSpec glassSpec;
    glassSpec.rounding = Layout::glassRounding();
    ImFont* ovFont  = Type().overline();
    ImFont* capFont = Type().caption();
    ImFont* sub1    = Type().subtitle1();
    ImFont* h4      = Type().h4();
    ImFont* body2   = Type().body2();

    char buf[128];

    // ================================================================
    // Proportional section budget — all content fits without scrolling
    // ================================================================
    float mkSHdr = ovFont->LegacySize + Layout::spacingXs()
                 + ImGui::GetStyle().ItemSpacing.y * 2.0f;
    float mkGapOver = gap + ImGui::GetStyle().ItemSpacing.y;
    float mkOverhead = 3.0f * (mkSHdr + mkGapOver) + 2.0f * mkGapOver;
    float pairBarH = S.drawElement("tabs.market", "pair-bar-height").height;
    float mkCardBudget = std::max(200.0f, marketAvail.y - mkOverhead);

    Layout::SectionBudget mb(mkCardBudget);
    float portfolioBudgetH = mb.allocate(0.18f, 50.0f);

    // ================================================================
    // PRICE SUMMARY — Combined hero card with price, stats, and exchange
    // ================================================================
    {
        float dp = Layout::dpiScale();
        ImVec2 cardMin = ImGui::GetCursorScreenPos();
        float heroMinH = S.drawElement("tabs.market", "hero-card-min-height").size;
        float statsRowH = ovFont->LegacySize + Layout::spacingXs() + sub1->LegacySize + pad;
        float cardH = std::max(heroMinH + statsRowH + pad, (S.drawElement("tabs.market", "hero-card-height").size + statsRowH + pad) * vs);
        ImVec2 cardMax(cardMin.x + availWidth, cardMin.y + cardH);
        DrawGlassPanel(dl, cardMin, cardMax, glassSpec);

        float cx = cardMin.x + Layout::spacingLg();
        float cy = cardMin.y + Layout::spacingLg();

        if (market.price_usd > 0) {
            // ---- HERO PRICE (large, prominent) ----
            ImFont* h3 = Type().h3();
            std::string priceStr = FormatPrice(market.price_usd);
            ImU32 priceCol = Success();
            DrawTextShadow(dl, h3, h3->LegacySize, ImVec2(cx, cy), priceCol, priceStr.c_str());

            // Ticker label after price
            float priceW = h3->CalcTextSizeA(h3->LegacySize, FLT_MAX, 0, priceStr.c_str()).x;
            dl->AddText(body2, body2->LegacySize,
                        ImVec2(cx + priceW + Layout::spacingSm(), cy + (h3->LegacySize - body2->LegacySize)),
                        OnSurfaceMedium(), DRAGONX_TICKER);

            // 24h change badge — to the right of ticker
            float tickerW = body2->CalcTextSizeA(body2->LegacySize, FLT_MAX, 0, DRAGONX_TICKER).x;
            float badgeX = cx + priceW + Layout::spacingSm() + tickerW + Layout::spacingMd();
            ImU32 chgCol = market.change_24h >= 0 ? Success() : Error();
            snprintf(buf, sizeof(buf), "%s%.2f%% 24h", market.change_24h >= 0 ? "+" : "", market.change_24h);
            ImVec2 chgSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf);
            float badgePadH = Layout::spacingSm();
            float badgePadV = Layout::spacingXs();
            ImVec2 bMin(badgeX, cy + (h3->LegacySize - chgSz.y - badgePadV * 2) * 0.5f);
            ImVec2 bMax(badgeX + chgSz.x + badgePadH * 2, bMin.y + chgSz.y + badgePadV * 2);
            ImU32 badgeBg = market.change_24h >= 0 ? WithAlpha(Success(), 30) : WithAlpha(Error(), 30);
            dl->AddRectFilled(bMin, bMax, badgeBg, 4.0f * dp);
            dl->AddText(capFont, capFont->LegacySize, ImVec2(bMin.x + badgePadH, bMin.y + badgePadV), chgCol, buf);

            // ---- SEPARATOR ----
            float sepY = cy + h3->LegacySize + Layout::spacingMd();
            float rnd = glassSpec.rounding;
            dl->AddLine(ImVec2(cardMin.x + rnd * 0.5f, sepY),
                        ImVec2(cardMax.x - rnd * 0.5f, sepY),
                        WithAlpha(OnSurface(), 15), 1.0f * dp);

            // ---- STATS ROW (BTC Price | Volume | Market Cap) ----
            float statsY = sepY + Layout::spacingSm();
            float colW = (availWidth - Layout::spacingLg() * 2) / 3.0f;

            struct StatItem { const char* label; std::string value; ImU32 valueCol; };
            StatItem stats[3] = {
                {TR("market_btc_price"),  "", OnSurface()},
                {TR("market_24h_volume"), FormatCompactUSD(market.volume_24h), OnSurface()},
                {TR("market_cap"), FormatCompactUSD(market.market_cap), OnSurface()},
            };
            snprintf(buf, sizeof(buf), "%.10f", market.price_btc);
            stats[0].value = buf;

            for (int i = 0; i < 3; i++) {
                float sx = cardMin.x + Layout::spacingLg() + i * colW;
                float centerX = sx + colW * 0.5f;

                // Label (overline, centered)
                ImVec2 lblSz = ovFont->CalcTextSizeA(ovFont->LegacySize, 10000, 0, stats[i].label);
                dl->AddText(ovFont, ovFont->LegacySize,
                            ImVec2(centerX - lblSz.x * 0.5f, statsY), OnSurfaceMedium(), stats[i].label);

                // Value (subtitle1, centered)
                float valY = statsY + ovFont->LegacySize + Layout::spacingXs();
                ImVec2 valSz = sub1->CalcTextSizeA(sub1->LegacySize, 10000, 0, stats[i].value.c_str());
                dl->AddText(sub1, sub1->LegacySize,
                            ImVec2(centerX - valSz.x * 0.5f, valY), stats[i].valueCol, stats[i].value.c_str());
            }

            // ---- STALENESS INDICATOR ----
            {
                auto fetchTime = market.last_fetch_time;
                if (fetchTime.time_since_epoch().count() > 0) {
                    auto elapsed = std::chrono::steady_clock::now() - fetchTime;
                    int ageSecs = (int)std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();
                    bool stale = ageSecs > 300; // 5 minutes
                    if (ageSecs < 60)
                        snprintf(buf, sizeof(buf), "%s %ds %s", stale ? ICON_MD_WARNING : "", ageSecs, TR("ago"));
                    else
                        snprintf(buf, sizeof(buf), "%s %dm %s", stale ? ICON_MD_WARNING : "", ageSecs / 60, TR("ago"));
                    ImFont* staleFont = capFont;
                    ImU32 staleCol = stale ? Warning() : WithAlpha(OnSurface(), 100);
                    float staleY = statsY + ovFont->LegacySize + Layout::spacingXs() + sub1->LegacySize + Layout::spacingSm();
                    ImVec2 staleSz = staleFont->CalcTextSizeA(staleFont->LegacySize, FLT_MAX, 0, buf);
                    dl->AddText(staleFont, staleFont->LegacySize,
                                ImVec2(cardMin.x + Layout::spacingLg(), staleY), staleCol, buf);
                }
            }

            // ---- TRADE BUTTON (top-right of card) ----
            if (!currentExchange.pairs.empty()) {
                const char* pairName = currentExchange.pairs[s_pair_idx].displayName.c_str();
                ImFont* iconFont = Type().iconSmall();
                ImVec2 textSz = body2->CalcTextSizeA(body2->LegacySize, FLT_MAX, 0, pairName);
                ImVec2 iconSz = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_OPEN_IN_NEW);
                float iconGap = Layout::spacingSm();
                float tradePadH = Layout::spacingMd();
                float tradePadV = Layout::spacingSm();
                float tradeBtnW = textSz.x + iconGap + iconSz.x + tradePadH * 2;
                float tradeBtnH = std::max(textSz.y, iconSz.y) + tradePadV * 2;
                float tradeBtnX = cardMax.x - pad - tradeBtnW;
                float tradeBtnY = cardMin.y + Layout::spacingSm();

                ImVec2 tMin(tradeBtnX, tradeBtnY), tMax(tradeBtnX + tradeBtnW, tradeBtnY + tradeBtnH);
                bool tradeHov = material::IsRectHovered(tMin, tMax);

                // Glass pill background
                GlassPanelSpec tradeBtnGlass;
                tradeBtnGlass.rounding = tradeBtnH * 0.5f;
                tradeBtnGlass.fillAlpha = tradeHov ? 35 : 20;
                DrawGlassPanel(dl, tMin, tMax, tradeBtnGlass);
                if (tradeHov)
                    dl->AddRectFilled(tMin, tMax, WithAlpha(Primary(), 20), tradeBtnH * 0.5f);

                // Text (pair name with body2, icon with icon font)
                ImU32 tradeCol = tradeHov ? OnSurface() : OnSurfaceMedium();
                float contentY = tradeBtnY + tradePadV;
                float curX = tradeBtnX + tradePadH;
                dl->AddText(body2, body2->LegacySize,
                            ImVec2(curX, contentY), tradeCol, pairName);
                curX += textSz.x + iconGap;
                dl->AddText(iconFont, iconFont->LegacySize,
                            ImVec2(curX, contentY + (textSz.y - iconSz.y) * 0.5f), tradeCol, ICON_MD_OPEN_IN_NEW);

                // Click
                ImVec2 savedCur = ImGui::GetCursorScreenPos();
                ImGui::SetCursorScreenPos(tMin);
                ImGui::InvisibleButton("##TradeOnExchange", ImVec2(tradeBtnW, tradeBtnH));
                if (ImGui::IsItemClicked()) {
                    util::Platform::openUrl(currentExchange.pairs[s_pair_idx].tradeUrl);
                }
                if (ImGui::IsItemHovered()) {
                    ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                    ImGui::SetTooltip(TR("market_trade_on"), currentExchange.name.c_str());
                }
                ImGui::SetCursorScreenPos(savedCur);
            }
        } else {
            const char* status = market.price_loading ? TR("market_price_loading") : TR("market_price_unavailable");
            DrawTextShadow(dl, sub1, sub1->LegacySize, ImVec2(cx, cy + 10), OnSurfaceDisabled(), status);
            if (!market.price_loading && !market.price_error.empty()) {
                std::string errorText = market.price_error;
                float maxErrorW = cardMax.x - cx - Layout::spacingLg();
                while (errorText.size() > 4 &&
                       capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, errorText.c_str()).x > maxErrorW) {
                    errorText.pop_back();
                }
                if (errorText.size() < market.price_error.size()) errorText += "...";
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(cx, cy + 10 + sub1->LegacySize + Layout::spacingXs()),
                            Warning(), errorText.c_str());
            }
        }

        ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMax.y));
        ImGui::Dummy(ImVec2(availWidth, 0));
        ImGui::Dummy(ImVec2(0, gap));
    }

    // ================================================================
    // PRICE CHART — Custom drawn inside glass panel (matches app design)
    // ================================================================
    {
        // Plot the REAL accumulated price history (one point per refresh, oldest→newest).
        // No synthetic data: until at least two real samples exist, the empty-state below
        // ("not enough history yet") is shown rather than inventing a price curve.
        s_price_history = market.price_history;
        s_history_initialized = true;

        // Chart height from schema
        float chartH = std::max(60.0f, chartElem.height * vs);
        ImVec2 chartMin = ImGui::GetCursorScreenPos();
        ImVec2 chartMax(chartMin.x + availWidth, chartMin.y + chartH);
        DrawGlassPanel(dl, chartMin, chartMax, glassSpec);

        if (!s_price_history.empty() && s_price_history.size() >= 2) {
            float chartPad = pad;
            float labelPadLeft = std::max(S.drawElement("tabs.market", "chart-y-axis-min-padding").size, S.drawElement("tabs.market", "chart-y-axis-padding").size * hs);
            float labelPadBottom = Layout::spacingXl();

            float plotLeft   = chartMin.x + labelPadLeft;
            float plotRight  = chartMax.x - chartPad;
            float plotTop    = chartMin.y + chartPad;
            float plotBottom = chartMax.y - labelPadBottom;
            float plotW      = plotRight - plotLeft;
            float plotH      = plotBottom - plotTop;

            // Compute Y range with padding
            double yMin = *std::min_element(s_price_history.begin(), s_price_history.end());
            double yMax = *std::max_element(s_price_history.begin(), s_price_history.end());
            if (yMax <= yMin) { yMax = yMin + 1e-8; }
            double yRange = yMax - yMin;
            double yPadding = yRange * 0.12;
            yMin -= yPadding;
            yMax += yPadding;

            // Horizontal grid lines (4 lines)
            for (int g = 0; g <= 4; g++) {
                float gy = plotTop + plotH * (float)g / 4.0f;
                dl->AddLine(ImVec2(plotLeft, gy), ImVec2(plotRight, gy),
                            IM_COL32(255, 255, 255, 12), 1.0f);
                double labelVal = yMax - (yMax - yMin) * (double)g / 4.0;
                snprintf(buf, sizeof(buf), "$%.6f", labelVal);
                ImVec2 labelSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf);
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(plotLeft - labelSz.x - 6, gy - labelSz.y * 0.5f),
                            OnSurfaceDisabled(), buf);
            }

            // Build points
            size_t n = s_price_history.size();
            std::vector<ImVec2> points(n);

            ImU32 lineCol = market.change_24h >= 0
                ? WithAlpha(Success(), 220) : WithAlpha(Error(), 220);
            ImU32 fillCol = market.change_24h >= 0
                ? WithAlpha(Success(), 25) : WithAlpha(Error(), 25);
            ImU32 dotCol = market.change_24h >= 0
                ? Success() : Error();

            for (size_t i = 0; i < n; i++) {
                float t = (n > 1) ? (float)i / (float)(n - 1) : 0.0f;
                float x = plotLeft + t * plotW;
                float y = plotBottom - (float)((s_price_history[i] - yMin) / (yMax - yMin)) * plotH;
                points[i] = ImVec2(x, y);
            }

            // Fill under curve (single concave polygon to avoid AA seam artifacts)
            if (n >= 2) {
                for (size_t i = 0; i < n; i++)
                    dl->PathLineTo(points[i]);
                dl->PathLineTo(ImVec2(points[n - 1].x, plotBottom));
                dl->PathLineTo(ImVec2(points[0].x, plotBottom));
                dl->PathFillConcave(fillCol);
            }

            // Line
            dl->AddPolyline(points.data(), (int)points.size(), lineCol, ImDrawFlags_None, S.drawElement("tabs.market", "chart-line-thickness").size);

            // Dots
            float dotR = std::max(S.drawElement("tabs.market", "chart-dot-min-radius").size, S.drawElement("tabs.market", "chart-dot-radius").size * hs);
            for (size_t i = 0; i < n; i++) {
                dl->AddCircleFilled(points[i], dotR, dotCol);
            }

            // X-axis: samples are per-refresh, not hourly-timestamped, so only the most
            // recent point can be labelled truthfully ("Now"). The series simply runs
            // older→newer, left→right. (Avoids the old fake "24h…6h" hourly gradations.)
            {
                const char* nowText = TR("market_now");
                ImVec2 lblSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, nowText);
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(plotRight - lblSz.x, plotBottom + 2), OnSurfaceDisabled(), nowText);
            }

            // Hover crosshair + tooltip
            ImVec2 mousePos = ImGui::GetIO().MousePos;
            if (mousePos.x >= plotLeft && mousePos.x <= plotRight &&
                mousePos.y >= plotTop && mousePos.y <= plotBottom + labelPadBottom)
            {
                float mx = mousePos.x - plotLeft;
                float closest_t = mx / plotW;
                int idx = (int)(closest_t * (n - 1) + 0.5f);
                if (idx < 0) idx = 0;
                if (idx >= (int)n) idx = (int)n - 1;

                float px = points[idx].x;
                float py = points[idx].y;

                dl->AddLine(ImVec2(px, plotTop), ImVec2(px, plotBottom),
                            IM_COL32(255, 255, 255, 40), 1.0f);
                dl->AddLine(ImVec2(plotLeft, py), ImVec2(plotRight, py),
                            IM_COL32(255, 255, 255, 40), 1.0f);

                float hoverDotR = std::max(S.drawElement("tabs.market", "chart-hover-dot-min-radius").size, S.drawElement("tabs.market", "chart-hover-dot-radius").size * hs);
                float hoverRingR = std::max(S.drawElement("tabs.market", "chart-hover-ring-min-radius").size, S.drawElement("tabs.market", "chart-hover-ring-radius").size * hs);
                dl->AddCircleFilled(ImVec2(px, py), hoverDotR, dotCol);
                dl->AddCircle(ImVec2(px, py), hoverRingR, IM_COL32(255, 255, 255, 80), 0, 1.5f);

                // Samples are per-refresh, not timestamped, so we don't claim a specific
                // "Xh ago" — just show the price at the hovered point.
                snprintf(buf, sizeof(buf), "%s", FormatPrice(s_price_history[idx]).c_str());
                ImVec2 tipSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf);
                float tipPad = Layout::spacingSm() + Layout::spacingXs();
                float tipX = px + 10;
                float tipY = py - tipSz.y - tipPad * 2 - 4;
                if (tipX + tipSz.x + tipPad * 2 > plotRight)
                    tipX = px - tipSz.x - tipPad * 2 - 10;
                if (tipY < plotTop) tipY = py + 10;

                ImVec2 tipMin(tipX, tipY);
                ImVec2 tipMax(tipX + tipSz.x + tipPad * 2, tipY + tipSz.y + tipPad * 2);
                dl->AddRectFilled(tipMin, tipMax, IM_COL32(20, 20, 30, 230), 4.0f);
                dl->AddRect(tipMin, tipMax, IM_COL32(255, 255, 255, 30), 4.0f, 0, 1.0f);
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(tipX + tipPad, tipY + tipPad), dotCol, buf);
            }
        } else {
            const char* msg = TR("market_no_history");
            ImVec2 ts = sub1->CalcTextSizeA(sub1->LegacySize, FLT_MAX, 0, msg);
            dl->AddText(sub1, sub1->LegacySize,
                        ImVec2(chartMin.x + (availWidth - ts.x) * 0.5f, chartMin.y + chartH * 0.45f),
                        OnSurfaceDisabled(), msg);
        }

        // --- Refresh button + timestamp pinned in chart top-right ---
        {
            float iconBtnSz = capFont->LegacySize + 8.0f;
            float refreshX = chartMax.x - pad;
            float refreshY = chartMin.y + pad * 0.5f;

            // Draw refresh icon button
            ImVec2 btnMin(refreshX - iconBtnSz, refreshY);
            ImVec2 btnMax(refreshX, refreshY + iconBtnSz);
            bool refreshHov = material::IsRectHovered(btnMin, btnMax);
            if (refreshHov) {
                dl->AddRectFilled(btnMin, btnMax, IM_COL32(255, 255, 255, 20), 4.0f);
                ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
            }

            ImFont* iconSmall = material::Typography::instance().iconSmall();
            ImVec2 iconSz = iconSmall->CalcTextSizeA(iconSmall->LegacySize, FLT_MAX, 0, ICON_MD_REFRESH);
            dl->AddText(iconSmall, iconSmall->LegacySize,
                        ImVec2(btnMin.x + (iconBtnSz - iconSz.x) * 0.5f, btnMin.y + (iconBtnSz - iconSz.y) * 0.5f),
                        refreshHov ? OnSurface() : OnSurfaceMedium(), ICON_MD_REFRESH);

            ImGui::SetCursorScreenPos(btnMin);
            if (ImGui::InvisibleButton("##RefreshMarket", ImVec2(iconBtnSz, iconBtnSz))) {
                app->refreshMarketData();
                s_history_initialized = false;
                s_last_refresh_time = ImGui::GetTime();
            }
            if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", TR("market_refresh_price"));

            // Timestamp text to the left of refresh button
            if (s_last_refresh_time > 0.0) {
                double elapsed = ImGui::GetTime() - s_last_refresh_time;
                if (elapsed < 60.0)
                    snprintf(buf, sizeof(buf), "%.0fs ago", elapsed);
                else if (elapsed < 3600.0)
                    snprintf(buf, sizeof(buf), "%.0fm ago", elapsed / 60.0);
                else
                    snprintf(buf, sizeof(buf), "%.1fh ago", elapsed / 3600.0);
                ImVec2 tsSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf);
                dl->AddText(capFont, capFont->LegacySize,
                            ImVec2(btnMin.x - tsSz.x - 6, btnMin.y + (iconBtnSz - tsSz.y) * 0.5f),
                            OnSurfaceDisabled(), buf);
            }
        }

        ImGui::SetCursorScreenPos(ImVec2(chartMin.x, chartMin.y + chartH));
        ImGui::Dummy(ImVec2(availWidth, 0));
    }

    // ================================================================
    // EXCHANGE SELECTOR — Combo dropdown + attribution
    // ================================================================
    ImGui::Dummy(ImVec2(0, S.drawElement("tabs.market", "exchange-top-gap").size));
    {
        float comboW = S.drawElement("tabs.market", "exchange-combo-width").size;

        ImGui::PushFont(body2);
        ImGui::PushItemWidth(comboW);
        if (ImGui::BeginCombo("##ExchangeCombo", currentExchange.name.c_str())) {
            for (int i = 0; i < (int)registry.size(); i++) {
                bool selected = (i == s_exchange_idx);
                if (ImGui::Selectable(registry[i].name.c_str(), selected)) {
                    if (i != s_exchange_idx) {
                        s_exchange_idx = i;
                        s_pair_idx = 0;
                        s_pair_scroll = 0.0f;
                        s_pair_scroll_target = 0.0f;
                        s_history_initialized = false;
                        app->settings()->setSelectedExchange(registry[i].name);
                        if (!registry[i].pairs.empty())
                            app->settings()->setSelectedPair(registry[i].pairs[0].displayName);
                        app->settings()->save();
                        app->refreshMarketData();
                    }
                }
                if (selected) ImGui::SetItemDefaultFocus();
            }
            ImGui::EndCombo();
        }
        ImGui::PopItemWidth();

        // Attribution
        ImGui::SameLine(0, Layout::spacingLg());
        Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("market_attribution"));

        if (!market.last_updated.empty()) {
            ImGui::SameLine(0, 12);
            snprintf(buf, sizeof(buf), " \xc2\xb7 Updated %s", market.last_updated.c_str());
            Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), buf);
        }

        ImGui::PopFont();
        ImGui::Dummy(ImVec2(0, gap));
    }

    // ================================================================
    // PAIR BAR — Horizontally scrolling chip selector (always visible)
    // ================================================================
    {
        float chipH = S.drawElement("tabs.market", "pair-chip-height").height;
        float chipR = S.drawElement("tabs.market", "pair-chip-radius").radius;
        float chipSpacing = S.drawElement("tabs.market", "pair-chip-spacing").size;
        float fadeW = S.drawElement("tabs.market", "pair-bar-fade-width").size;
        float arrowSz = S.drawElement("tabs.market", "pair-bar-arrow-size").size;

        ImVec2 barOrigin = ImGui::GetCursorScreenPos();
        float barW = availWidth;
        float barH = pairBarH;

        // Compute total content width of all chips
        float totalChipW = 0.0f;
        std::vector<float> chipWidths;
        for (const auto& pair : currentExchange.pairs) {
            float tw = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, pair.displayName.c_str()).x;
            float cw = tw + Layout::spacingLg() * 2.0f;
            chipWidths.push_back(cw);
            totalChipW += cw + chipSpacing;
        }
        totalChipW -= chipSpacing; // no trailing spacing

        float scrollableW = barW - arrowSz * 2.0f - Layout::spacingSm() * 2.0f;
        float maxScroll = std::max(0.0f, totalChipW - scrollableW);

        // Smooth scroll lerp
        s_pair_scroll += (s_pair_scroll_target - s_pair_scroll) * 0.15f;
        if (std::abs(s_pair_scroll - s_pair_scroll_target) < 0.5f)
            s_pair_scroll = s_pair_scroll_target;

        // Clamp
        s_pair_scroll_target = std::clamp(s_pair_scroll_target, 0.0f, maxScroll);
        s_pair_scroll = std::clamp(s_pair_scroll, 0.0f, maxScroll);

        // Left arrow button
        float arrowY = barOrigin.y + (barH - arrowSz) * 0.5f;
        bool canScrollLeft = s_pair_scroll_target > 0.01f;
        ImGui::SetCursorScreenPos(ImVec2(barOrigin.x, arrowY));
        ImGui::BeginDisabled(!canScrollLeft);
        ImGui::PushFont(material::Typography::instance().iconSmall());
        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
        if (ImGui::Button(ICON_MD_CHEVRON_LEFT "##PairLeft", ImVec2(arrowSz, arrowSz))) {
            // Scroll left by average chip width
            float avgChipW = totalChipW / currentExchange.pairs.size();
            s_pair_scroll_target -= avgChipW + chipSpacing;
            if (s_pair_scroll_target < 0) s_pair_scroll_target = 0;
        }
        ImGui::PopStyleVar();
        ImGui::PopFont();
        ImGui::EndDisabled();

        // Chip area with clipping
        float chipAreaLeft = barOrigin.x + arrowSz + Layout::spacingSm();
        float chipAreaRight = barOrigin.x + barW - arrowSz - Layout::spacingSm();
        float chipY = barOrigin.y + (barH - chipH) * 0.5f;

        dl->PushClipRect(ImVec2(chipAreaLeft, barOrigin.y),
                         ImVec2(chipAreaRight, barOrigin.y + barH), true);

        // Render chips
        float cx = chipAreaLeft - s_pair_scroll;
        bool anyClicked = false;
        for (int i = 0; i < (int)currentExchange.pairs.size(); i++) {
            float cw = chipWidths[i];
            ImVec2 cMin(cx, chipY);
            ImVec2 cMax(cx + cw, chipY + chipH);

            bool selected = (i == s_pair_idx);
            ImU32 chipBg = selected ? WithAlpha(Primary(), 200) : WithAlpha(OnSurface(), 20);
            ImU32 chipBorder = selected ? Primary() : WithAlpha(OnSurface(), 40);
            ImU32 chipText = selected ? IM_COL32(255, 255, 255, 255) : OnSurface();

            dl->AddRectFilled(cMin, cMax, chipBg, chipR);
            dl->AddRect(cMin, cMax, chipBorder, chipR, 0, 1.0f);

            ImVec2 textSz = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0,
                                                     currentExchange.pairs[i].displayName.c_str());
            float textX = cx + (cw - textSz.x) * 0.5f;
            float textY = chipY + (chipH - textSz.y) * 0.5f;
            dl->AddText(capFont, capFont->LegacySize, ImVec2(textX, textY), chipText,
                        currentExchange.pairs[i].displayName.c_str());

            // Click detection via invisible button
            ImGui::SetCursorScreenPos(cMin);
            snprintf(buf, sizeof(buf), "##PairChip%d", i);
            if (ImGui::InvisibleButton(buf, ImVec2(cw, chipH))) {
                if (!s_pair_dragging || std::abs(ImGui::GetIO().MousePos.x - s_pair_drag_start_x) < 4.0f) {
                    s_pair_idx = i;
                    anyClicked = true;
                    app->settings()->setSelectedPair(currentExchange.pairs[i].displayName);
                    app->settings()->save();
                    s_history_initialized = false;
                    app->refreshMarketData();
                }
            }

            cx += cw + chipSpacing;
        }

        dl->PopClipRect();

        // Fade overlays on edges
        ImU32 bgCol = IM_COL32(0, 0, 0, 0);
        ImU32 surfaceCol = Surface();
        if (s_pair_scroll > 1.0f) {
            // Left fade
            dl->AddRectFilledMultiColor(
                ImVec2(chipAreaLeft, barOrigin.y), ImVec2(chipAreaLeft + fadeW, barOrigin.y + barH),
                surfaceCol, bgCol, bgCol, surfaceCol);
        }
        if (s_pair_scroll < maxScroll - 1.0f) {
            // Right fade
            dl->AddRectFilledMultiColor(
                ImVec2(chipAreaRight - fadeW, barOrigin.y), ImVec2(chipAreaRight, barOrigin.y + barH),
                bgCol, surfaceCol, surfaceCol, bgCol);
        }

        // Right arrow button
        bool canScrollRight = s_pair_scroll_target < maxScroll - 0.01f;
        ImGui::SetCursorScreenPos(ImVec2(barOrigin.x + barW - arrowSz, arrowY));
        ImGui::BeginDisabled(!canScrollRight);
        ImGui::PushFont(material::Typography::instance().iconSmall());
        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
        if (ImGui::Button(ICON_MD_CHEVRON_RIGHT "##PairRight", ImVec2(arrowSz, arrowSz))) {
            float avgChipW = totalChipW / currentExchange.pairs.size();
            s_pair_scroll_target += avgChipW + chipSpacing;
            if (s_pair_scroll_target > maxScroll) s_pair_scroll_target = maxScroll;
        }
        ImGui::PopStyleVar();
        ImGui::PopFont();
        ImGui::EndDisabled();

        // Mouse wheel horizontal scroll when hovering pair bar
        ImVec2 mPos = ImGui::GetIO().MousePos;
        if (mPos.x >= barOrigin.x && mPos.x <= barOrigin.x + barW &&
            mPos.y >= barOrigin.y && mPos.y <= barOrigin.y + barH) {
            float wheel = ImGui::GetIO().MouseWheel;
            if (wheel != 0.0f) {
                float avgChipW = totalChipW / currentExchange.pairs.size();
                s_pair_scroll_target -= wheel * (avgChipW + chipSpacing);
                s_pair_scroll_target = std::clamp(s_pair_scroll_target, 0.0f, maxScroll);
            }
        }

        // Mouse drag scrolling
        if (ImGui::IsMouseClicked(0) && mPos.x >= chipAreaLeft && mPos.x <= chipAreaRight &&
            mPos.y >= barOrigin.y && mPos.y <= barOrigin.y + barH) {
            s_pair_dragging = true;
            s_pair_drag_start_x = mPos.x;
            s_pair_drag_start_scroll = s_pair_scroll_target;
        }
        if (s_pair_dragging) {
            if (ImGui::IsMouseDown(0)) {
                float dx = mPos.x - s_pair_drag_start_x;
                s_pair_scroll_target = std::clamp(s_pair_drag_start_scroll - dx, 0.0f, maxScroll);
            } else {
                s_pair_dragging = false;
            }
        }

        // Arrow key navigation
        if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
            if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && s_pair_idx > 0) {
                s_pair_idx--;
                app->settings()->setSelectedPair(currentExchange.pairs[s_pair_idx].displayName);
                app->settings()->save();
                s_history_initialized = false;
                app->refreshMarketData();
                anyClicked = true;
            }
            if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && s_pair_idx < (int)currentExchange.pairs.size() - 1) {
                s_pair_idx++;
                app->settings()->setSelectedPair(currentExchange.pairs[s_pair_idx].displayName);
                app->settings()->save();
                s_history_initialized = false;
                app->refreshMarketData();
                anyClicked = true;
            }
        }

        // Auto-scroll to keep selected chip visible
        if (anyClicked) {
            float chipLeft = 0;
            for (int i = 0; i < s_pair_idx; i++)
                chipLeft += chipWidths[i] + chipSpacing;
            float chipRight = chipLeft + chipWidths[s_pair_idx];
            if (chipLeft < s_pair_scroll_target)
                s_pair_scroll_target = chipLeft - chipSpacing;
            if (chipRight > s_pair_scroll_target + scrollableW)
                s_pair_scroll_target = chipRight - scrollableW + chipSpacing;
            s_pair_scroll_target = std::clamp(s_pair_scroll_target, 0.0f, maxScroll);
        }

        // Advance cursor past the bar
        ImGui::SetCursorScreenPos(ImVec2(barOrigin.x, barOrigin.y + barH));
        ImGui::Dummy(ImVec2(availWidth, 0));
        ImGui::Dummy(ImVec2(0, gap));
    }

    // ================================================================
    // PORTFOLIO — Glass card with balance breakdown
    // ================================================================
    {
        Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("market_portfolio"));
        ImGui::Dummy(ImVec2(0, Layout::spacingXs()));

        double total_balance = state.totalBalance;
        double private_balance = state.privateBalance;
        double transparent_balance = state.transparentBalance;

        ImVec2 cardMin = ImGui::GetCursorScreenPos();
        float cardH = std::max(50.0f, portfolioBudgetH);
        ImVec2 cardMax(cardMin.x + availWidth, cardMin.y + cardH);
        DrawGlassPanel(dl, cardMin, cardMax, glassSpec);

        float cx = cardMin.x + Layout::spacingLg();
        float cy = cardMin.y + Layout::spacingLg();

        if (market.price_usd > 0) {
            double portfolio_usd = total_balance * market.price_usd;
            if (portfolio_usd >= 1.0)
                snprintf(buf, sizeof(buf), "$%.2f USD", portfolio_usd);
            else
                snprintf(buf, sizeof(buf), "$%.6f USD", portfolio_usd);
            DrawTextShadow(dl, sub1, sub1->LegacySize, ImVec2(cx, cy), Success(), buf);

            double portfolio_btc = total_balance * market.price_btc;
            snprintf(buf, sizeof(buf), "%.10f BTC", portfolio_btc);
            float valW = sub1->CalcTextSizeA(sub1->LegacySize, FLT_MAX, 0, buf).x;
            dl->AddText(capFont, capFont->LegacySize,
                        ImVec2(cardMax.x - valW - pad, cy + 2), OnSurfaceMedium(), buf);
        } else {
            dl->AddText(sub1, sub1->LegacySize, ImVec2(cx, cy), OnSurfaceDisabled(), TR("market_no_price"));
        }

        cy += sub1->LegacySize + 8;

        snprintf(buf, sizeof(buf), "%.8f %s", total_balance, DRAGONX_TICKER);
        dl->AddText(body2, body2->LegacySize, ImVec2(cx, cy), OnSurface(), buf);

        snprintf(buf, sizeof(buf), "Z: %.4f  |  T: %.4f", private_balance, transparent_balance);
        float brkW = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, 0, buf).x;
        dl->AddText(capFont, capFont->LegacySize,
                    ImVec2(cardMax.x - brkW - pad, cy + 2), OnSurfaceDisabled(), buf);

        cy += body2->LegacySize + 8;

        if (total_balance > 0) {
            float barW = availWidth - Layout::spacingXxl() * 1.5f;
            float barH = std::max(S.drawElement("tabs.market", "ratio-bar-min-height").size, S.drawElement("tabs.market", "ratio-bar-height").size * vs);
            float shieldedRatio = (float)(private_balance / total_balance);
            if (shieldedRatio > 1.0f) shieldedRatio = 1.0f;
            if (shieldedRatio < 0.0f) shieldedRatio = 0.0f;
            float shieldedW = barW * shieldedRatio;
            float transpW   = barW - shieldedW;

            ImVec2 barStart(cx, cy);

            dl->AddRectFilled(barStart, ImVec2(barStart.x + barW, barStart.y + barH),
                              IM_COL32(255, 255, 255, 10), 3.0f);
            if (shieldedW > 0.5f)
                dl->AddRectFilled(barStart, ImVec2(barStart.x + shieldedW, barStart.y + barH),
                                  WithAlpha(Success(), 200),
                                  transpW > 0.5f ? ImDrawFlags_RoundCornersLeft : ImDrawFlags_RoundCornersAll, 3.0f);
            if (transpW > 0.5f)
                dl->AddRectFilled(ImVec2(barStart.x + shieldedW, barStart.y),
                                  ImVec2(barStart.x + barW, barStart.y + barH),
                                  WithAlpha(Warning(), 200),
                                  shieldedW > 0.5f ? ImDrawFlags_RoundCornersRight : ImDrawFlags_RoundCornersAll, 3.0f);

            // market_pct_shielded is "%.0f%% Shielded" — pass a double, not an int (passing
            // an int to a %f conversion is UB and renders garbage on x86-64).
            snprintf(buf, sizeof(buf), TR("market_pct_shielded"),
                     static_cast<double>(shieldedRatio) * 100.0);
            dl->AddText(capFont, capFont->LegacySize,
                        ImVec2(cx, cy + barH + 2), OnSurfaceDisabled(), buf);
        }

        float actualCardH = (total_balance > 0) ? std::max(60.0f, portfolioBudgetH) : cardH;
        ImGui::SetCursorScreenPos(ImVec2(cardMin.x, cardMin.y + actualCardH));
        ImGui::Dummy(ImVec2(availWidth, 0));
        ImGui::Dummy(ImVec2(0, gap));
    }

    ImGui::EndChild(); // ##MarketScroll
}

} // namespace ui
} // namespace dragonx
