// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
//
// Modal dialog to download / update the DRG-XMRig miner from the project Gitea, driven by
// util::XmrigUpdater. Two-pane layout (mirrors the node updater / Manage-Portfolio editor): the list
// of miner versions on the LEFT, the selected version's detail + install action on the RIGHT.
// States: Listing -> ReleaseList -> Downloading/Verifying/Extracting -> Done / Failed. Header-only.
// The installed release tag is persisted to settings on success. Installs are refused while the
// miner is running (you can't replace a running binary).

#pragma once

#include <algorithm>
#include <cctype>
#include <memory>
#include <sstream>
#include <string>
#include <vector>

#include "../../app.h"
#include "../../config/settings.h"
#include "../../resources/embedded_resources.h"   // resources::getDaemonDirectory()
#include "../../util/i18n.h"
#include "../../util/xmrig_updater.h"
#include "../material/colors.h"
#include "../material/draw_helpers.h"
#include "../material/markdown.h"
#include "../material/type.h"
#include "../theme.h"
#include "release_list_view.h"
#include "imgui.h"

namespace dragonx {
namespace ui {

class XmrigDownloadDialog {
public:
    using Progress = util::XmrigUpdater::Progress;
    using St = util::XmrigUpdater::State;

    static void show(App* app) {
        if (!app) return;
        s_app = app;
        s_open = true;
        s_persisted = false;
        s_sel = 0;
        s_sweep = false;
        s_installed_tag = app->settings() ? app->settings()->getXmrigVersion() : std::string();
        s_rows.clear();
        s_releases.clear();
        s_updater = std::make_unique<util::XmrigUpdater>();
        s_updater->startListReleases();   // fetch every version up front for the two-pane picker
    }

    static bool isOpen() { return s_open; }

    // Sweep-only: seed fake releases + a forced state so the offline UI sweep can render the modal.
    static void sweepSeed(App* app, std::vector<util::XmrigRelease> releases, St state,
                          const std::string& installedTag) {
        s_app = app;
        s_open = true;
        s_persisted = false;
        s_installed_tag = installedTag;
        s_sel = 0;
        s_sweep = true;
        s_releases = std::move(releases);
        s_rows.clear();
        s_updater.reset();
        s_sweepProgress = Progress{};
        s_sweepProgress.state = state;
        if (!s_releases.empty()) s_sweepProgress.latest_tag = s_releases.front().tag;
        s_sweepProgress.installed_tag = installedTag;
        s_sweepProgress.percent = 62.0f;
        s_sweepProgress.status_text = "drg-xmrig-linux-x64.zip";
    }
    static void sweepClose() { s_open = false; s_sweep = false; s_releases.clear(); s_rows.clear(); }

    static void render() {
        if (!s_open || !s_app) {
            if (!s_open) s_updater.reset();
            return;
        }
        using namespace material;
        const float dp = Layout::dpiScale();
        const Progress p = s_sweep ? s_sweepProgress
                                   : (s_updater ? s_updater->getProgress() : Progress{});
        const bool active = (p.state == St::Downloading || p.state == St::Verifying || p.state == St::Extracting);
        OverlayDialogSpec ov;
        ov.title = TR("xmrig_update_title");
        ov.p_open = active ? nullptr : &s_open;
        ov.style = OverlayStyle::BlurFloat;
        ov.cardWidth  = 880.0f;
        ov.cardHeight = std::min(760.0f, ImGui::GetMainViewport()->Size.y * 0.84f / dp);
        ov.idSuffix   = "xmrigupd";
        if (BeginOverlayDialog(ov)) {
            switch (p.state) {
            case St::Idle:
            case St::Checking:
            case St::UpToDate:
            case St::UpdateAvailable:
            case St::Listing:      renderCentered(TR("xmrig_loading_releases")); break;
            case St::Unavailable:  renderUnavailable(p); break;
            case St::Failed:
                if (s_releases.empty()) { renderListFailed(p); break; }
                renderTwoPane(dp, p, active); break;
            default:               renderTwoPane(dp, p, active); break;
            }
            EndOverlayDialog();
        }
        if (!s_open) { s_updater.reset(); s_sweep = false; }
    }

private:
    static float fullW() { return ImGui::GetContentRegionAvail().x; }

    static void renderCentered(const char* msg) {
        using namespace material;
        ImVec2 av = ImGui::GetContentRegionAvail();
        ImFont* f = Type().body2();
        ImVec2 ts = f->CalcTextSizeA(f->LegacySize, FLT_MAX, 0, msg);
        ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + std::max(0.0f, (av.x - ts.x) * 0.5f),
                                   ImGui::GetCursorPosY() + av.y * 0.45f));
        Type().text(TypeStyle::Body2, msg);
    }

    static void renderUnavailable(const Progress& p) {
        using namespace material;
        Type().text(TypeStyle::Subtitle2, TR("xmrig_unavailable_title"));
        ImGui::Spacing();
        ImGui::TextWrapped("%s", p.status_text.empty() ? TR("xmrig_unavailable_body")
                                                       : p.status_text.c_str());
        ImGui::Spacing();
        if (TactileButton(TR("close"), ImVec2(std::min(fullW(), 200.0f * Layout::dpiScale()), 0)))
            s_open = false;
    }

    static void renderListFailed(const Progress& p) {
        using namespace material;
        const float bw = std::min(fullW(), 220.0f * Layout::dpiScale());
        Type().textColored(TypeStyle::Subtitle2, Error(), TR("xmrig_update_failed"));
        ImGui::Spacing();
        ImGui::TextWrapped("%s", p.error.empty() ? TR("xmrig_unknown_error") : p.error.c_str());
        ImGui::Spacing();
        if (TactileButton(TR("retry"), ImVec2(bw, 0)) && s_updater) {
            s_releases.clear();
            s_rows.clear();
            s_updater->startListReleases();
        }
        ImGui::Spacing();
        if (TactileButton(TR("close"), ImVec2(bw, 0))) s_open = false;
    }

    static void buildRows() {
        if (!s_rows.empty() || s_releases.empty()) return;
        const std::string token = util::currentXmrigPlatformToken();
        s_rows.reserve(s_releases.size());
        for (const auto& r : s_releases) {
            ReleaseRow row;
            row.tag = r.tag;
            row.title = r.name;
            row.date = r.publishedAt.size() >= 10 ? r.publishedAt.substr(0, 10) : r.publishedAt;
            row.prerelease = r.prerelease;
            row.hasAsset = util::selectXmrigAsset(r, token) >= 0;
            row.installed = !s_installed_tag.empty() && r.tag == s_installed_tag;
            s_rows.push_back(std::move(row));
        }
    }

    static std::string cleanNotes(const std::string& body) {
        std::istringstream ss(body);
        std::string line, out;
        while (std::getline(ss, line)) {
            size_t a = line.find_first_not_of(" \t");
            std::string lt = (a == std::string::npos) ? std::string() : line.substr(a);
            std::string low = lt;
            std::transform(low.begin(), low.end(), low.begin(),
                           [](unsigned char c) { return (char)std::tolower(c); });
            if (!lt.empty() && lt[0] == '|') break;
            if (low.rfind("## checksum", 0) == 0 || low.rfind("#### checksum", 0) == 0 ||
                low.rfind("checksums", 0) == 0 || low.rfind("### sha", 0) == 0) break;
            out += line;
            out += '\n';
        }
        while (!out.empty() && (out.back() == '\n' || out.back() == '\r' ||
                                out.back() == ' '  || out.back() == '\t')) out.pop_back();
        return out;
    }

    static void startInstallSelected() {
        if (!s_updater || s_app->isPoolMinerRunning()) return;   // can't replace a running miner
        if (s_sel < 0 || s_sel >= (int)s_releases.size()) return;
        const util::XmrigRelease rel = s_releases[s_sel];
        s_updater->startInstallRelease(resources::getDaemonDirectory(), rel);
    }

    // ================= Two-pane: version list (left) + detail/progress (right) =================
    static void renderTwoPane(float dp, const Progress& p, bool active) {
        using namespace material;
        if (!s_sweep && s_releases.empty() && s_updater) s_releases = s_updater->getReleases();
        buildRows();
        if (s_sel >= (int)s_rows.size()) s_sel = s_rows.empty() ? 0 : (int)s_rows.size() - 1;

        ImFont* bodyF = Type().body2();
        ImFont* capF  = Type().caption();

        const bool showFooter = (p.state == St::ReleaseList);
        const float footerH = showFooter
            ? (ImGui::GetFrameHeight() * 1.25f + ImGui::GetStyle().ItemSpacing.y * 2.0f + Layout::spacingSm())
            : 0.0f;
        const float bodyH   = std::max(120.0f * dp, ImGui::GetContentRegionAvail().y - footerH);
        const float contentW = ImGui::GetContentRegionAvail().x;
        const float gap = Layout::spacingLg();
        const float masterW = std::min(std::max(contentW * 0.34f, 240.0f * dp), 340.0f * dp);
        const float detailW = contentW - masterW - gap;

        // ---- LEFT: borderless list of tactile version cards ----
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
        ImGui::BeginChild("##xmrigVersions", ImVec2(masterW, bodyH), false);
        {
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, Layout::spacingSm()));
            ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0, 0, 0, 0));
            ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0, 0, 0, 0));
            ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0, 0, 0, 0));
            ImDrawList* mdl = ImGui::GetWindowDrawList();
            ImFont* icoF = Type().iconSmall();
            const float rowH = Layout::spacingMd() * 2.0f + bodyF->LegacySize + Layout::spacingXs() + capF->LegacySize;
            const float rowW = ImGui::GetContentRegionAvail().x;
            const float round = 8.0f * dp;
            for (int i = 0; i < (int)s_rows.size(); ++i) {
                const ReleaseRow& r = s_rows[i];
                ImGui::PushID(i);
                bool sel = (s_sel == i);
                ImVec2 rmn = ImGui::GetCursorScreenPos();
                if (ImGui::Selectable("##ver", sel, ImGuiSelectableFlags_SpanAvailWidth, ImVec2(0, rowH))) {
                    if (!active) s_sel = i;
                }
                bool hov = ImGui::IsItemHovered();
                bool held = ImGui::IsItemActive();
                ImVec2 rmx(rmn.x + rowW, rmn.y + rowH);
                ImU32 fill = sel   ? WithAlpha(Primary(), held ? 55 : 42)
                           : held  ? WithAlpha(OnSurface(), 34)
                           : hov   ? WithAlpha(OnSurface(), 24)
                                   : WithAlpha(OnSurface(), 12);
                mdl->AddRectFilled(rmn, rmx, fill, round);
                if (sel) mdl->AddRect(rmn, rmx, WithAlpha(Primary(), 150), round, 0, 1.6f * dp);
                if (hov || held) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
                const char* ico = nullptr; ImU32 statusC = 0; const char* tt = nullptr; const char* lbl = nullptr;
                if (r.installed)       { ico = ICON_MD_CHECK_CIRCLE; statusC = Success(); tt = TR("xmrig_active_tt");     lbl = TR("upd_installed_badge"); }
                else if (r.prerelease) { ico = ICON_MD_SCIENCE;      statusC = Warning(); tt = TR("xmrig_prerelease_tt"); lbl = TR("upd_prerelease"); }
                else if (i == 0)       { ico = ICON_MD_NEW_RELEASES; statusC = Primary(); tt = TR("xmrig_latest_tt");     lbl = TR("xmrig_latest_badge"); }
                float rightLimit = rmx.x - Layout::spacingMd();
                if (ico) {
                    float cs = bodyF->LegacySize * 1.3f;
                    ImVec2 cp(rmx.x - Layout::spacingMd() - cs, (rmn.y + rmx.y) * 0.5f - cs * 0.5f);
                    mdl->AddText(icoF, cs, cp, statusC, ico);
                    if (tt && hov && ImGui::IsMouseHoveringRect(cp, ImVec2(cp.x + cs, cp.y + cs)))
                        Tooltip("%s", tt);
                    rightLimit = cp.x - Layout::spacingSm();
                }
                float x = rmn.x + Layout::spacingMd();
                float tagY = rmn.y + Layout::spacingMd();
                mdl->AddText(bodyF, bodyF->LegacySize, ImVec2(x, tagY),
                             r.hasAsset ? OnSurface() : OnSurfaceDisabled(), r.tag.c_str());
                if (lbl) {
                    float bx = x + bodyF->CalcTextSizeA(bodyF->LegacySize, FLT_MAX, 0, r.tag.c_str()).x + Layout::spacingSm();
                    float by = tagY + (bodyF->LegacySize - capF->LegacySize) * 0.5f;
                    if (bx + capF->CalcTextSizeA(capF->LegacySize, FLT_MAX, 0, lbl).x <= rightLimit)
                        mdl->AddText(capF, capF->LegacySize, ImVec2(bx, by), statusC, lbl);
                }
                if (!r.date.empty())
                    mdl->AddText(capF, capF->LegacySize,
                                 ImVec2(x, rmx.y - Layout::spacingMd() - capF->LegacySize),
                                 OnSurfaceMedium(), r.date.c_str());
                ImGui::PopID();
            }
            ImGui::PopStyleColor(3);
            ImGui::PopStyleVar();
        }
        ImGui::EndChild();
        ImGui::PopStyleVar();

        ImGui::SameLine(0.0f, gap);

        // ---- RIGHT: detail / progress / done / failed for the selected version ----
        ImGui::BeginChild("##xmrigDetail", ImVec2(detailW, bodyH), false);
        switch (p.state) {
        case St::ReleaseList: renderDetail(dp, p); break;
        case St::Downloading:
        case St::Verifying:
        case St::Extracting:  renderProgress(dp, p); break;
        case St::Done:        renderDone(dp, p); break;
        case St::Failed:      renderFailed(dp, p); break;
        default: break;
        }
        ImGui::EndChild();

        if (showFooter) {
            ImGui::Spacing();
            const float bw = 170.0f * dp, bh = ImGui::GetFrameHeight() * 1.25f;
            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + std::max(0.0f, ImGui::GetContentRegionAvail().x - bw));
            if (TactileButton(TR("close"), ImVec2(bw, bh))) s_open = false;
        }
    }

    static void renderDetail(float dp, const Progress&) {
        using namespace material;
        if (s_sel < 0 || s_sel >= (int)s_releases.size() || s_sel >= (int)s_rows.size()) return;
        const util::XmrigRelease& rel = s_releases[s_sel];
        const ReleaseRow& row = s_rows[s_sel];
        const bool haveInstalled = !s_installed_tag.empty();
        const bool downgrade = !row.installed && s_sel != 0 && haveInstalled;
        const bool mining = s_app->isPoolMinerRunning();

        const char* noteStr = mining        ? TR("xmrig_stop_mining_first")
                            : !row.hasAsset  ? TR("upd_no_build_platform")
                            : downgrade      ? TR("xmrig_downgrade_note")
                                             : TR("xmrig_verify_note");
        const char* label = row.installed ? TR("xmrig_reinstall")
                          : (s_sel == 0)   ? TR("xmrig_download_install")
                                           : TR("xmrig_install_this");

        const float btnH  = ImGui::GetFrameHeight() * 1.15f;
        const float noteH = ImGui::CalcTextSize(noteStr, nullptr, false, ImGui::GetContentRegionAvail().x).y;
        const float reserve = noteH + btnH + Layout::spacingMd() * 3.0f;
        const float infoH = std::max(120.0f * dp, ImGui::GetContentRegionAvail().y - reserve);

        // ---- Info card: ONE rounded Material surface (version header + release notes markdown) ----
        ImGui::PushStyleColor(ImGuiCol_ChildBg, WithAlpha(OnSurface(), 22));
        ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f * dp);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(Layout::spacingLg(), Layout::spacingLg()));
        ImGui::BeginChild("##xmrigInfo", ImVec2(0, infoH), ImGuiChildFlags_AlwaysUseWindowPadding);
        {
            Type().text(TypeStyle::H6, rel.tag.c_str());
            if (row.installed)
                Type().textColored(TypeStyle::Subtitle2, Success(), TR("xmrig_status_current"));
            else if (s_sel == 0)
                Type().textColored(TypeStyle::Subtitle2, Primary(),
                    haveInstalled ? TR("xmrig_update_available") : TR("xmrig_status_latest"));
            else if (haveInstalled)
                Type().textColored(TypeStyle::Subtitle2, Warning(), TR("xmrig_status_older"));
            else
                Type().text(TypeStyle::Subtitle2, rel.name.empty() ? rel.tag.c_str() : rel.name.c_str());

            const std::string installed = s_installed_tag.empty() ? std::string(TR("xmrig_none"))
                                                                  : s_installed_tag;
            ImGui::Spacing();
            Type().textColored(TypeStyle::Caption, OnSurfaceMedium(),
                               (std::string(TR("xmrig_installed")) + " " + installed).c_str());
            if (!row.date.empty())
                Type().textColored(TypeStyle::Caption, OnSurfaceMedium(),
                                   (std::string(TR("xmrig_released")) + " " + row.date).c_str());
            ImGui::Dummy(ImVec2(0, Layout::spacingSm()));

            const std::string notes = cleanNotes(rel.body);
            ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
            ImGui::BeginChild("##xmrigNotes", ImVec2(0, std::max(0.0f, ImGui::GetContentRegionAvail().y)), false);
            if (notes.empty())
                Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("xmrig_no_notes"));
            else
                RenderMarkdown(notes);
            ImGui::EndChild();
            ImGui::PopStyleColor();
        }
        ImGui::EndChild();
        ImGui::PopStyleVar(2);
        ImGui::PopStyleColor();

        // ---- Below the info card: verify / stop-mining note + install button (centered, text-fit) ----
        ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
        ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x);
        Type().textColored(TypeStyle::Caption, (mining || downgrade) ? Warning() : OnSurfaceMedium(), noteStr);
        ImGui::PopTextWrapPos();
        ImGui::Spacing();
        const float bw = ImGui::CalcTextSize(label).x + ImGui::GetStyle().FramePadding.x * 2.0f + Layout::spacingLg();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() + std::max(0.0f, (ImGui::GetContentRegionAvail().x - bw) * 0.5f));
        ImGui::BeginDisabled(!row.hasAsset || mining);
        if (TactileButton(label, ImVec2(bw, btnH))) startInstallSelected();
        ImGui::EndDisabled();
    }

    static void renderProgress(float dp, const Progress& p) {
        using namespace material;
        const char* title = p.state == St::Downloading ? TR("xmrig_downloading")
                          : p.state == St::Verifying   ? TR("xmrig_verifying")
                                                       : TR("xmrig_installing");
        Type().text(TypeStyle::Subtitle2, title);
        ImGui::Spacing();
        material::ProgressBar(fullW(), 8.0f * dp, p.percent / 100.0f);
        ImGui::Spacing();
        ImGui::TextWrapped("%s", p.status_text.c_str());
        ImGui::Spacing();
        if (TactileButton(TR("cancel"), ImVec2(fullW(), 0))) {
            if (s_updater) s_updater->cancel();
            s_open = false;
        }
    }

    static void renderDone(float, const Progress& p) {
        using namespace material;
        if (!s_persisted) {
            if (s_app->settings() && !p.latest_tag.empty()) {
                s_app->settings()->setXmrigVersion(p.latest_tag);
                s_app->settings()->save();
            }
            s_persisted = true;
        }
        Type().textColored(TypeStyle::Subtitle2, Success(), TR("xmrig_installed_ok"));
        ImGui::Spacing();
        ImGui::Text("%s %s", TR("xmrig_version"), p.latest_tag.c_str());
        ImGui::Spacing();
        if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
    }

    static void renderFailed(float, const Progress& p) {
        using namespace material;
        Type().textColored(TypeStyle::Subtitle2, Error(), TR("xmrig_update_failed"));
        ImGui::Spacing();
        ImGui::TextWrapped("%s", p.error.empty() ? TR("xmrig_unknown_error") : p.error.c_str());
        ImGui::Spacing();
        ImGui::BeginDisabled(s_app->isPoolMinerRunning());
        if (TactileButton(TR("retry"), ImVec2(fullW(), 0))) startInstallSelected();
        ImGui::EndDisabled();
        ImGui::Spacing();
        if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
    }

    static inline bool s_open = false;
    static inline bool s_persisted = false;
    static inline bool s_sweep = false;
    static inline int  s_sel = 0;
    static inline std::string s_installed_tag;
    static inline std::vector<ReleaseRow> s_rows;             // built once per listing (UI rows)
    static inline std::vector<util::XmrigRelease> s_releases;   // matching releases (install by index)
    static inline Progress s_sweepProgress;
    static inline App* s_app = nullptr;
    static inline std::unique_ptr<util::XmrigUpdater> s_updater;
};

} // namespace ui
} // namespace dragonx
