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

#pragma once

#include "console_channel.h"
#include "console_model.h"
#include "console_scroll_controller.h"
#include "console_selection_controller.h"
#include "console_text_layout.h"
#include "../layout.h"
#include "../../daemon/embedded_daemon.h"
#include "../../daemon/xmrig_manager.h"
#include "../../rpc/rpc_client.h"
#include "../../rpc/rpc_worker.h"

#include <cstdint>
#include <string>
#include <vector>
#include <deque>
#include <mutex>

namespace dragonx {
namespace ui {

class ConsoleCommandExecutor;
struct ConsoleLogFilterCaps;
struct ConsoleCommandEntry;

/**
 * @brief Console tab — a rich terminal shared by the full-node and lite variants.
 *
 * The command execution + log sources are provided by a ConsoleCommandExecutor, so this
 * one widget serves both dragonxd (RPC) and the lite backend.
 */
class ConsoleTab {
public:
    ConsoleTab();
    ~ConsoleTab();

    /**
     * @brief Render the console tab against a backend executor.
     */
    void render(ConsoleCommandExecutor& exec);

    /**
     * @brief Render the command-reference popup at top-level scope.
     * Must be called outside any child window so the modal blocks all input. `exec` supplies the
     * reference table (full-node RPC vs lite backend verbs); may be null (no console yet) -> no-op.
     */
    void renderCommandsPopupModal(ConsoleCommandExecutor* exec);

    // Debug/UI-sweep hook: force the RPC command-reference popup open/closed so the full UI sweep
    // can capture it (the popup is otherwise opened only by a toolbar button).
    void sweepSetCommandsPopup(bool open) { show_commands_popup_ = open; }

    /**
     * @brief Add a line to the console output
     */
    void addLine(const std::string& line, ConsoleChannel channel = ConsoleChannel::None);
    void addRpcTraceLine(const std::string& source, const std::string& method);
    
    /**
     * @brief Clear console output
     */
    void clear();

    // Ask the console to place the keyboard focus in the command input on the next render (consumed once).
    // Called when the user switches to the Console tab, gated by the console_auto_focus setting.
    void requestInputFocus() { focus_input_pending_ = true; }

    // Scanline effect toggle (set from settings)
    static bool s_scanline_enabled;

    // Console output zoom factor (1.0 = default caption font size)
    static float s_console_zoom;

    // Draw the per-line left color accent bars (channel-colored). Toggled from the toolbar.
    static bool s_line_accents_enabled;

    // Color output text per channel. When false the console is monochrome text. Toolbar-toggled.
    static bool s_line_text_color_enabled;

    // Show/hide daemon output messages
    static bool s_daemon_messages_enabled;

    // Show only error messages (filter toggle)
    static bool s_errors_only_enabled;

    // Show app RPC calls made through RPCClient (method/source only)
    static bool s_rpc_trace_enabled;

    // Show "[app] ..." wallet log lines
    static bool s_app_messages_enabled;

    /// Refresh console text colors for current theme (call after theme switch)
    static void refreshColors();
    
    // Colors — follow active theme (public for use by notification forwarding)
    static ImU32 COLOR_COMMAND;
    static ImU32 COLOR_RESULT;
    static ImU32 COLOR_ERROR;
    static ImU32 COLOR_DAEMON;
    static ImU32 COLOR_INFO;
    static ImU32 COLOR_RPC;
    
private:
    // Text color for a channel, resolved from the current theme (COLOR_* + material). Each
    // line stores only its semantic ConsoleChannel (in ConsoleModel); the text color and
    // the left accent-bar color are both derived from it at draw time.
    ImU32 channelTextColor(ConsoleChannel channel) const;
    // Saturated accent color for a channel — the left accent-bar color, also used to tint the
    // matching toolbar filter toggle. 0 for channels that get no accent (result/JSON/None).
    static ImU32 channelAccentColor(ConsoleChannel channel);

    // Drain the thread-safe ingest queue into the visible model (once per frame, main
    // thread), applying the line cap + selection/scroll bookkeeping for the new lines.
    void drainModel();

    // Format a completed command result (JSON role -> channel) into console lines.
    void addFormattedResult(const std::string& result, bool is_error);
    void renderStatusHeader(ConsoleCommandExecutor& exec);
    void renderCommandsPopup(ConsoleCommandExecutor& exec);
    // right pane; jsonArgs=true (full-node RPC) auto-quotes string args, false (lite) inserts bare
    void renderCommandDetail(const ConsoleCommandEntry& cmd, const char* catLabel, bool jsonArgs);
    void insertCommandToInput(const ConsoleCommandEntry& cmd);  // fill input + close the modal

    // renderToolbar() draws the top bar; these are its sub-steps:
    void renderToolbar(ConsoleCommandExecutor& exec);
    void drawToolbarStatus(ConsoleCommandExecutor& exec);  // backend status dot + label
    void drawLogFilterToggles(const ConsoleLogFilterCaps& caps);  // color-coded filter checkboxes
    void drawFilterInput();                                // text filter + match count
    void drawZoomControls();                               // zoom -/+ buttons

    // renderInput() draws the command line; dispatch is factored out:
    void renderInput(ConsoleCommandExecutor& exec);
    // Echo + handle a submitted command line (built-ins + submit). True if non-empty/handled.
    bool submitConsoleCommand(ConsoleCommandExecutor& exec, const std::string& cmd);

    // renderOutput() orchestrates the scrollable output area; these are its sub-steps:
    void renderOutput();
    void computeVisibleLines(bool& hasTextFilter, std::string& filterLower);  // -> visible_indices_
    void handleOutputInteraction(ImVec2 mousePos, bool mouseInOutput);        // wheel/selection/keys
    void drawVisibleLines(float padX, float lineHeight, bool hasTextFilter,
                          const std::string& filterLower);                    // bars/guides/highlight/text
    void drawNewOutputIndicator();                                            // jump-to-bottom pill
    void drawOutputContextMenu();                                             // right-click menu
    
    // Map a screen point to a raw {line, col} position via this frame's wrapped layout.
    // (View-side because it needs layout_ + output_origin_; feeds the selection controller.)
    ConsoleTextPos screenToTextPos(ImVec2 screen_pos) const;

    // The current selection's text (via selection_ over the visible model). "" if inactive.
    std::string selectedText() const;

    // Thread-safe line store: producers on any thread call model_.ingest(); the main
    // thread drains it once per frame in render(). The visible deque is main-thread-only.
    ConsoleModel model_;
    std::vector<std::string> command_history_;
    int history_index_ = -1;
    char input_buffer_[4096] = {0};
    bool stop_confirm_pending_ = false;   // 'stop' typed once, awaiting a confirming second 'stop'
    bool focus_input_pending_ = false;    // one-shot: focus the command input next render (tab-open auto-focus)
    // (log-ingestion cursors + result queue moved to the ConsoleCommandExecutor)

    // Auto-scroll state machine (pin-to-bottom, wheel-up cooldown, new-line backlog count).
    ConsoleScrollController scroll_;

    // Text-selection state + logic (anchor/caret, ordering, extraction, eviction shift).
    ConsoleSelectionController selection_;
    float output_scroll_y_ = 0.0f;    // Track scroll position for selection
    ImVec2 output_origin_ = {0, 0};   // Top-left of output area
    float output_line_height_ = 0.0f; // Text line height (for scanline alignment)

    struct ScanlineRow {
        float yTop = 0.0f;
        float yBot = 0.0f;
        int rowIndex = 0;
    };
    std::vector<ScanlineRow> scanline_rows_;

    // Output filter
    char filter_text_[128] = {0};
    mutable int filter_match_count_ = 0;        // lines matching the text filter (for the toolbar)
    std::string context_token_;                 // hash/address under the cursor at right-click
    mutable std::vector<int> visible_indices_;  // Cached for selection mapping
    bool folding_active_ = true;                // fold UI shown only in the unfiltered (foldable) view
    bool has_text_filter_ = false;              // computed once per frame (before the toolbar draws it)
    std::string filter_lower_;                  // lowercased filter needle for match highlighting

    // Memoization keys so the two expensive per-frame passes rebuild only on change (not every frame):
    // computeVisibleLines (filter scan over the whole model) is keyed on the model revision + filter
    // state; BuildConsoleLayout (glyph-by-glyph text shaping of every visible line) is keyed on the
    // resulting visible-set generation + wrap width + line height/zoom.
    std::uint64_t vis_key_ = ~0ull;         // key of the last computeVisibleLines
    std::uint64_t vis_generation_ = 0;      // bumped whenever visible_indices_ is rebuilt
    std::uint64_t layout_key_ = ~0ull;      // key of the last BuildConsoleLayout

    // Wrap layout for the visible lines (segments + per-line heights + cumulative Y),
    // recomputed each frame by the pure BuildConsoleLayout (console_text_layout.h) and
    // consumed by the renderer + hit-testing.
    mutable ConsoleLayout layout_;

    // Commands popup (command explorer) — indexes into the active executor's commandReference()
    // table (full-node RPC commands or the lite backend verbs), not always the full-node one.
    bool show_commands_popup_ = false;
    char command_search_[128] = {0};   // reference search filter (cleared when the modal opens)
    int  cmd_sel_cat_ = -1;            // detail-pane selection: category index into that table
    int  cmd_sel_idx_ = -1;            // detail-pane selection: command index within that category
    std::string pending_submit_;      // command to run next frame (deferred so the modal needs no executor)
    const ConsoleCommandEntry* run_confirm_cmd_ = nullptr;  // destructive "Insert & run" awaiting confirmation
    char cmd_param_bufs_[6][256] = {{0}};                   // parameter-builder input fields
    const ConsoleCommandEntry* cmd_param_owner_ = nullptr;  // command the param buffers currently hold
};

} // namespace ui
} // namespace dragonx
