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

#include "block_info_dialog.h"
#include "../../app.h"
#include "../../rpc/rpc_client.h"
#include "../../rpc/rpc_worker.h"
#include "../../util/i18n.h"
#include "../../util/text_format.h"
#include "../notifications.h"
#include "../schema/ui_schema.h"
#include "../material/draw_helpers.h"
#include "imgui.h"

#include <nlohmann/json.hpp>
#include <string>
#include <ctime>

namespace dragonx {
namespace ui {

using json = nlohmann::json;

// Static state
static bool s_open = false;
static int s_height = 0;
static bool s_loading = false;
static bool s_has_data = false;
static std::string s_error;

// Block data
static std::string s_block_hash;
static int64_t s_block_time = 0;
static int s_tx_count = 0;
static int s_block_size = 0;
static std::string s_bits;
static double s_difficulty = 0.0;
static std::string s_prev_hash;
static std::string s_next_hash;
static std::string s_merkle_root;
static int s_confirmations = 0;

// Pending RPC app pointer (for async callback)
static App* s_pending_app = nullptr;

void BlockInfoDialog::show(int initialHeight)
{
    s_open = true;
    s_height = initialHeight > 0 ? initialHeight : 1;
    s_loading = false;
    s_has_data = false;
    s_error.clear();
}

// Callback to handle getblock response  
static void handleBlockResponseUnified(const json& result, const std::string& error)
{
    s_loading = false;
    
    if (!error.empty()) {
        s_error = std::string(TR("grpa_error_prefix")) + error;
        return;
    }
    
    if (!result.is_null()) {
        auto block = result;
        
        s_block_hash = block.value("hash", "");
        s_block_time = block.value("time", (int64_t)0);
        s_confirmations = block.value("confirmations", 0);
        s_block_size = block.value("size", 0);
        s_bits = block.value("bits", "");
        s_difficulty = block.value("difficulty", 0.0);
        s_prev_hash = block.value("previousblockhash", "");
        s_next_hash = block.value("nextblockhash", "");
        s_merkle_root = block.value("merkleroot", "");
        
        if (block.contains("tx") && block["tx"].is_array()) {
            s_tx_count = static_cast<int>(block["tx"].size());
        } else {
            s_tx_count = 0;
        }
        
        s_has_data = true;
    } else {
        s_error = TR("grpa_invalid_response_from_daemon");
    }
}

void BlockInfoDialog::render(App* app)
{
    if (!s_open) return;

    auto& S = schema::UI();
    auto win = S.window("dialogs.block-info");
    auto heightInput = S.input("dialogs.block-info", "height-input");
    auto lbl = S.label("dialogs.block-info", "label");
    auto hashLbl = S.label("dialogs.block-info", "hash-label");
    auto hashFrontLbl = S.label("dialogs.block-info", "hash-front-label");
    auto hashBackLbl = S.label("dialogs.block-info", "hash-back-label");
    auto closeBtn = S.button("dialogs.block-info", "close-button");

    material::OverlayDialogSpec ov;
    ov.title = TR("block_info_title"); ov.p_open = &s_open;
    ov.style = material::OverlayStyle::BlurFloat;   // floating content on the blur, plain heading
    ov.cardWidth = win.width;                       // keep the authored width
    ov.cardBottomViewportRatio = 0.94f;
    if (material::BeginOverlayDialog(ov)) {
        auto* rpc = app->rpc();
        const auto& state = app->getWalletState();
        
        // Height input
        ImGui::Text("%s", TR("block_height"));
        ImGui::SetNextItemWidth(heightInput.width * Layout::dpiScale());
        ImGui::InputInt("##Height", &s_height);
        if (s_height < 1) s_height = 1;
        // Clamp to the chain tip so navigation/typing can't request a height
        // above the tip (which would only yield a raw RPC error).
        if (state.sync.blocks > 0 && s_height > state.sync.blocks) {
            s_height = state.sync.blocks;
        }
        
        ImGui::SameLine();
        
        // Current block info
        if (state.sync.blocks > 0) {
            ImGui::TextDisabled(TR("grpa_current_block_paren"), state.sync.blocks);
        }
        
        ImGui::SameLine();
        
        // Fetch button
        if (s_loading) {
            ImGui::BeginDisabled();
        }
        
        if (material::TactileButton(TR("block_get_info"), ImVec2(0,0), S.resolveFont(closeBtn.font))) {
            if (rpc && rpc->isConnected() && app->worker()) {
                s_loading = true;
                s_error.clear();
                s_has_data = false;
                s_pending_app = app;

                // Run the two chained RPCs (getblockhash → getblock) on the worker thread;
                // doing them inline froze the UI for two round-trips. Guard the hash type.
                int height = s_height;
                app->worker()->post([rpc, height]() -> rpc::RPCWorker::MainCb {
                    json block;
                    std::string error;
                    try {
                        rpc::RPCClient::TraceScope trace("Explorer / Block info");
                        auto hashResult = rpc->call("getblockhash", {height});
                        if (!hashResult.is_string()) {
                            error = TR("grpa_unexpected_getblockhash_result");
                        } else {
                            block = rpc->call("getblock", {hashResult.get<std::string>()});
                        }
                    } catch (const std::exception& e) {
                        error = e.what();
                    }
                    return [block, error]() { handleBlockResponseUnified(block, error); };
                });
            }
        }
        
        if (s_loading) {
            ImGui::EndDisabled();
            ImGui::SameLine();
            ImGui::TextDisabled("%s", TR("loading"));
        }
        
        ImGui::Spacing();
        ImGui::Separator();
        ImGui::Spacing();
        
        // Error display
        if (!s_error.empty()) {
            ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f));
            ImGui::TextWrapped("%s", s_error.c_str());
            ImGui::PopStyleColor();
        }
        
        // Block info display
        if (s_has_data) {
            // Block hash
            ImGui::Text("%s", TR("block_hash"));
            ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.8f, 1.0f, 1.0f));
            ImGui::TextWrapped("%s", s_block_hash.c_str());
            ImGui::PopStyleColor();
            
            if (ImGui::IsItemHovered()) {
                material::Tooltip("%s", TR("click_to_copy"));
            }
            if (ImGui::IsItemClicked()) {
                ImGui::SetClipboardText(s_block_hash.c_str());
                Notifications::instance().success(TR("block_hash_copied"));
            }
            
            ImGui::Spacing();
            
            // Timestamp
            ImGui::Text("%s", TR("block_timestamp"));
            ImGui::SameLine(lbl.position);
            if (s_block_time > 0) {
                ImGui::Text("%s", dragonx::util::formatClockDateTime(s_block_time, /*withSeconds=*/true).c_str());
            } else {
                ImGui::TextDisabled("%s", TR("unknown"));
            }
            
            // Confirmations
            ImGui::Text("%s", TR("confirmations"));
            ImGui::SameLine(lbl.position);
            ImGui::Text("%d", s_confirmations);
            
            // Transaction count
            ImGui::Text("%s", TR("block_transactions"));
            ImGui::SameLine(lbl.position);
            ImGui::Text("%d", s_tx_count);
            
            // Size
            ImGui::Text("%s", TR("block_size"));
            ImGui::SameLine(lbl.position);
            if (s_block_size > 1024 * 1024) {
                ImGui::Text("%.2f MB", s_block_size / (1024.0 * 1024.0));
            } else if (s_block_size > 1024) {
                ImGui::Text("%.2f KB", s_block_size / 1024.0);
            } else {
                ImGui::Text("%d bytes", s_block_size);
            }
            
            // Difficulty
            ImGui::Text("%s", TR("difficulty"));
            ImGui::SameLine(lbl.position);
            ImGui::Text("%.4f", s_difficulty);
            
            // Bits
            ImGui::Text("%s", TR("block_bits"));
            ImGui::SameLine(lbl.position);
            ImGui::Text("%s", s_bits.c_str());
            
            ImGui::Spacing();
            ImGui::Separator();
            ImGui::Spacing();
            
            // Merkle root
            ImGui::Text("%s", TR("block_merkle_root"));
            ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
            ImGui::TextWrapped("%s", s_merkle_root.c_str());
            ImGui::PopStyleColor();
            
            ImGui::Spacing();
            
            // Previous block
            if (!s_prev_hash.empty()) {
                ImGui::Text("%s", TR("block_previous"));
                ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.8f, 1.0f, 1.0f));
                
                // Truncate for display
                std::string prev_short = s_prev_hash;
                if (prev_short.length() > static_cast<size_t>(hashLbl.truncate)) {
                    prev_short = util::truncateMiddle(prev_short, hashFrontLbl.truncate, hashBackLbl.truncate);
                }
                ImGui::Text("%s", prev_short.c_str());
                ImGui::PopStyleColor();
                
                if (ImGui::IsItemHovered()) {
                    material::Tooltip("%s", TR("block_click_prev"));
                }
                if (ImGui::IsItemClicked() && s_height > 1) {
                    s_height--;
                    s_has_data = false;
                }
            }
            
            // Next block
            if (!s_next_hash.empty()) {
                ImGui::Text("%s", TR("block_next"));
                ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.8f, 1.0f, 1.0f));
                
                // Truncate for display
                std::string next_short = s_next_hash;
                if (next_short.length() > static_cast<size_t>(hashLbl.truncate)) {
                    next_short = util::truncateMiddle(next_short, hashFrontLbl.truncate, hashBackLbl.truncate);
                }
                ImGui::Text("%s", next_short.c_str());
                ImGui::PopStyleColor();
                
                if (ImGui::IsItemHovered()) {
                    material::Tooltip("%s", TR("block_click_next"));
                }
                if (ImGui::IsItemClicked() &&
                    (state.sync.blocks <= 0 || s_height < state.sync.blocks)) {
                    s_height++;
                    s_has_data = false;
                }
            }
        }
        
        ImGui::Spacing();
        ImGui::Spacing();
        
        // Navigation buttons
        if (s_has_data) {
            if (s_height > 1) {
                if (material::TactileButton(TR("block_nav_prev"), ImVec2(0,0), S.resolveFont(closeBtn.font))) {
                    s_height--;
                    s_has_data = false;
                    s_error.clear();
                }
                ImGui::SameLine();
            }
            
            // Only offer "Next" when below the chain tip (the tip block has no
            // nextblockhash, so this stays hidden there).
            if (!s_next_hash.empty() &&
                (state.sync.blocks <= 0 || s_height < state.sync.blocks)) {
                if (material::TactileButton(TR("block_nav_next"), ImVec2(0,0), S.resolveFont(closeBtn.font))) {
                    s_height++;
                    s_has_data = false;
                    s_error.clear();
                }
            }
        }
        
        // Close button at bottom — centered via the shared footer helper (no separator, matching
        // the prior hand-rolled placement).
        material::BeginOverlayDialogFooter(closeBtn.width, /*drawSeparator=*/false);
        if (material::TactileButton(TR("close"), ImVec2(closeBtn.width, 0), S.resolveFont(closeBtn.font))) {
            s_open = false;
        }
        material::EndOverlayDialog();
    }
}

} // namespace ui
} // namespace dragonx
