// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
//
// Modal dialog to download / update the dragonxd full node from the project Gitea, driven by
// util::DaemonUpdater. Sibling of XmrigDownloadDialog (the miner updater). States follow the
// updater: Checking -> UpToDate/UpdateAvailable -> Downloading/Verifying/Extracting -> Done /
// Failed. Header-only (static inline state). The new binary is installed into the daemon directory
// without touching the running node; on Done the user is offered a daemon restart to apply it.

#pragma once

#include <memory>
#include <string>
#include <vector>

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

namespace dragonx {
namespace ui {

class DaemonUpdateDialog {
public:
    // `installedVersion` is the version scanned from the installed dragonxd (may be empty/unknown),
    // used to tell "up to date" from "update available".
    static void show(App* app, const std::string& installedVersion) {
        if (!app) return;
        s_app = app;
        s_open = true;
        s_notified = false;
        s_installed_flag = false;   // start each session clean (don't leak a prior install's flag)
        s_installed_version = installedVersion;
        s_rows.clear();
        s_releases.clear();
        s_updater = std::make_unique<util::DaemonUpdater>();
        s_updater->startCheck(installedVersion);
    }

    static bool isOpen() { return s_open; }

    // True (once) after an install succeeded, so the Settings panel can refresh its cached daemon
    // info. Clears on read.
    static bool consumeInstalled() {
        const bool v = s_installed_flag;
        s_installed_flag = false;
        return v;
    }

    static void render() {
        if (!s_open || !s_app || !s_updater) {
            if (!s_open) s_updater.reset();  // closed: drop the updater (dtor joins the worker)
            return;
        }
        using namespace material;
        const float dp = Layout::dpiScale();
        if (BeginOverlayDialog(TR("daemon_update_title"), &s_open, 480.0f, 0.94f)) {
            const auto p = s_updater->getProgress();
            using St = util::DaemonUpdater::State;
            switch (p.state) {
            case St::Checking:        renderChecking(dp, p); break;
            case St::UpToDate:
            case St::UpdateAvailable: renderPrompt(dp, p); break;
            case St::Unavailable:     renderUnavailable(dp, p); break;
            case St::Listing:         renderListing(dp, p); break;
            case St::ReleaseList:     renderReleaseList(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;
            }
            EndOverlayDialog();
        }
        if (!s_open) s_updater.reset();
    }

private:
    using Progress = util::DaemonUpdater::Progress;
    using St = util::DaemonUpdater::State;

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

    static void installAction(const char* label) {
        using namespace material;
        if (TactileButton(label, ImVec2(fullW(), 0)))
            s_updater->startInstall(resources::getDaemonDirectory());
    }

    static void renderChecking(float, const Progress&) {
        using namespace material;
        Type().text(TypeStyle::Body2, TR("daemon_update_checking"));
    }

    static void renderUnavailable(float, const Progress& p) {
        using namespace material;
        Type().text(TypeStyle::Subtitle2, TR("daemon_update_unavailable_title"));
        ImGui::Spacing();
        ImGui::TextWrapped("%s", p.status_text.empty()
            ? TR("daemon_update_unavailable_body")
            : p.status_text.c_str());
        ImGui::Spacing();
        if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
    }

    static void renderPrompt(float, const Progress& p) {
        using namespace material;
        const std::string installed = p.installed_tag.empty() ? TR("xmrig_none") : p.installed_tag;
        if (p.state == St::UpdateAvailable) {
            Type().text(TypeStyle::Subtitle2, TR("daemon_update_available"));
            ImGui::Spacing();
            ImGui::Text("%s %s", TR("daemon_update_latest"), p.latest_tag.c_str());
            ImGui::Text("%s %s", TR("daemon_update_installed"), installed.c_str());
            ImGui::Spacing();
            Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("daemon_update_verify_note"));
            ImGui::Spacing();
            installAction(TR("daemon_update_download_install"));
        } else {
            Type().text(TypeStyle::Subtitle2, TR("daemon_update_up_to_date"));
            ImGui::Spacing();
            ImGui::Text("%s %s", TR("daemon_update_installed"), p.latest_tag.c_str());
            ImGui::Spacing();
            installAction(TR("daemon_update_reinstall"));
        }
        ImGui::Spacing();
        // Browse all releases (pre-releases / older versions).
        if (TactileButton(TR("daemon_update_browse"), ImVec2(fullW(), 0))) {
            s_rows.clear();
            s_updater->startListReleases();
        }
        ImGui::Spacing();
        if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
    }

    static void renderListing(float, const Progress&) {
        using namespace material;
        Type().text(TypeStyle::Body2, TR("daemon_update_loading"));
    }

    static void renderReleaseList(float dp, const Progress&) {
        using namespace material;
        // Snapshot the worker's list + build the rows once per listing (not every frame — the
        // release bodies are large). Caches are cleared when a new listing starts (browse / show).
        if (s_rows.empty()) {
            s_releases = s_updater->getReleases();
            const std::string token = util::currentDaemonPlatformToken();
            const std::string instCore = util::daemonVersionCore(s_installed_version);
            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::selectDaemonAsset(r, token) >= 0;
                row.installed = !instCore.empty() && util::daemonVersionCore(r.tag) == instCore;
                s_rows.push_back(std::move(row));
            }
        }
        // Downgrade caution: an older node binary may not match the current chain data.
        Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("daemon_update_downgrade_note"));
        ImGui::Spacing();
        bool back = false;
        const int idx = RenderReleaseList(s_rows, dp, &back);
        if (back) { s_rows.clear(); s_updater->startCheck(s_installed_version); return; }
        if (idx >= 0 && idx < static_cast<int>(s_releases.size())) {
            const util::DaemonRelease rel = s_releases[idx];
            s_rows.clear();
            s_updater->startInstallRelease(resources::getDaemonDirectory(), rel);
        }
    }

    static void renderProgress(float dp, const Progress& p) {
        using namespace material;
        const char* title = p.state == St::Downloading ? TR("daemon_update_downloading")
                          : p.state == St::Verifying   ? TR("daemon_update_verifying")
                                                       : TR("daemon_update_installing");
        Type().text(TypeStyle::Subtitle2, title);
        ImGui::Spacing();
        const float barH = 8.0f * dp;
        const float barW = fullW();
        const ImVec2 bMin = ImGui::GetCursorScreenPos();
        const ImVec2 bMax(bMin.x + barW, bMin.y + barH);
        ImDrawList* dl = ImGui::GetWindowDrawList();
        dl->AddRectFilled(bMin, bMax, IM_COL32(255, 255, 255, 30), 4.0f * dp);
        const float fillW = barW * (p.percent / 100.0f);
        if (fillW > 0)
            dl->AddRectFilled(bMin, ImVec2(bMin.x + fillW, bMax.y), Primary(), 4.0f * dp);
        ImGui::Dummy(ImVec2(0, barH));
        ImGui::Spacing();
        ImGui::Text("%s", p.status_text.c_str());
        ImGui::Spacing();
        // Cancel aborts the in-flight transfer promptly (the curl progress callback returns abort).
        if (TactileButton(TR("cancel"), ImVec2(fullW(), 0))) {
            s_updater->cancel();
            s_open = false;
        }
    }

    static void renderDone(float, const Progress& p) {
        using namespace material;
        // Tell the Settings panel to refresh its cached daemon info — once, not every frame (a
        // re-scan reads the whole daemon binary).
        if (!s_notified) { s_installed_flag = true; s_notified = true; }
        Type().textColored(TypeStyle::Subtitle2, Success(), TR("daemon_update_installed_ok"));
        ImGui::Spacing();
        ImGui::Text("%s %s", TR("daemon_update_version"), p.latest_tag.c_str());
        ImGui::Spacing();
        Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("daemon_update_restart_note"));
        ImGui::Spacing();
        if (s_app->isUsingEmbeddedDaemon()) {
            if (TactileButton(TR("daemon_update_restart_now"), ImVec2(fullW(), 0))) {
                s_app->restartDaemon();
                s_open = false;
            }
            ImGui::Spacing();
            if (TactileButton(TR("daemon_update_later"), ImVec2(fullW(), 0))) s_open = false;
        } else {
            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("daemon_update_failed"));
        ImGui::Spacing();
        ImGui::TextWrapped("%s", p.error.empty() ? TR("daemon_update_unknown_error") : p.error.c_str());
        ImGui::Spacing();
        installAction(TR("retry"));
        ImGui::Spacing();
        if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
    }

    static inline bool s_open = false;
    static inline bool s_installed_flag = false;
    static inline bool s_notified = false;
    static inline std::string s_installed_version;
    static inline std::vector<ReleaseRow> s_rows;            // built once per listing (UI rows)
    static inline std::vector<util::DaemonRelease> s_releases;  // matching releases (install by index)
    static inline App* s_app = nullptr;
    static inline std::unique_ptr<util::DaemonUpdater> s_updater;
};

} // namespace ui
} // namespace dragonx
