// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
//
// ConsoleCommandExecutor — the pluggable backend behind the shared ConsoleTab UI. It
// abstracts the only real differences between the full-node console (dragonxd log + RPC
// command execution) and the lite console (lite diagnostics ring + backend command),
// so both variants use one rich terminal widget instead of two implementations.

#pragma once

#include "console_channel.h"
#include "imgui.h"

#include <atomic>
#include <cstdint>
#include <deque>
#include <functional>
#include <mutex>
#include <string>
#include <utility>
#include <vector>

namespace dragonx {
class App;
namespace ui {

struct ConsoleCommandCategory;  // console_command_reference.h — command-reference table

using ConsoleAddLineFn = std::function<void(const std::string&, ConsoleChannel)>;

struct ConsoleStatusLine {
    std::string text;
    ImU32       color = IM_COL32(200, 200, 200, 255);
    bool        pulse = false;   // animate the toolbar dot (starting/connecting)
};

// Which log-filter toggles the toolbar should show for a given backend. The full node has a
// daemon/xmrig log + an RPC trace; the lite backend has neither (its diagnostics ring maps to
// the App/Error channels), so it only wants the errors-only + app-messages toggles.
struct ConsoleLogFilterCaps {
    bool daemon      = false;  // daemon + xmrig output toggle
    bool errorsOnly  = false;  // errors-only toggle
    bool rpcTrace    = false;  // RPC method/source trace toggle
    bool appMessages = false;  // app / diagnostics log toggle
    bool any() const { return daemon || errorsOnly || rpcTrace || appMessages; }
};

class ConsoleCommandExecutor {
public:
    virtual ~ConsoleCommandExecutor() = default;

    // True when commands can be run (RPC connected / lite wallet open).
    virtual bool isReady() const = 0;
    // A command is currently in flight (input is disabled while true).
    virtual bool busy() const { return false; }
    // Submit a user command for async execution. The UI has already echoed "> cmd" and
    // handled the built-ins (clear/help/quit). Results arrive later via pollResult().
    virtual void submit(const std::string& cmd) = 0;
    // Pop one completed command result (raw text + error flag) if available; the UI
    // formats/colors it via FormatConsoleRpcResultLines. Returns false if none pending.
    virtual bool pollResult(std::string& result, bool& isError) = 0;

    // Pull any new passive log lines (daemon/xmrig output, or the lite diagnostics ring)
    // and hand each to `add`. Called once per frame.
    virtual void pollLogLines(const ConsoleAddLineFn& add) { (void)add; }

    // UI-chrome capabilities.
    // True when this backend speaks JSON-RPC to a daemon (the full node). Gates daemon-specific
    // console behavior (the 'stop' shutdown confirm, "not connected to daemon" wording) and the
    // command-reference modal's JSON string-arg quoting — the lite backend takes bare tokens.
    virtual bool hasRpcReference() const { return false; }
    // The command-reference table this backend offers (browsed by the console's reference modal),
    // or nullptr for none. Full node = the JSON-RPC reference; lite = its own backend verbs.
    virtual const std::vector<ConsoleCommandCategory>* commandReference() const { return nullptr; }
    // Which log-filter toggles the toolbar should show (default: none).
    virtual ConsoleLogFilterCaps logFilterCaps() const { return {}; }

    // Print the backend-appropriate `help` output via `add`.
    virtual void printHelp(const ConsoleAddLineFn& add) = 0;

    // Extra status lines drawn above the toolbar (lite shows sync + last error).
    virtual std::vector<ConsoleStatusLine> statusLines() const { return {}; }
    // Short status shown in the toolbar (daemon state / lite connection). Empty text =>
    // the toolbar shows its generic "no daemon" label.
    virtual ConsoleStatusLine toolbarStatus() const { return {}; }
};

// ── Full-node: dragonxd log ingestion + RPC command execution ────────────────
class FullNodeConsoleExecutor : public ConsoleCommandExecutor {
public:
    explicit FullNodeConsoleExecutor(App* app) : app_(app) {}

    bool isReady() const override;
    void submit(const std::string& cmd) override;
    bool pollResult(std::string& result, bool& isError) override;
    void pollLogLines(const ConsoleAddLineFn& add) override;
    bool hasRpcReference() const override { return true; }
    const std::vector<ConsoleCommandCategory>* commandReference() const override;
    // Full node: daemon/xmrig log, errors-only, RPC trace, and app messages.
    ConsoleLogFilterCaps logFilterCaps() const override { return {true, true, true, true}; }
    void printHelp(const ConsoleAddLineFn& add) override;
    ConsoleStatusLine toolbarStatus() const override;
    // A command is in flight on the RPC worker — lets the UI disable the input (no queued pile-up).
    bool busy() const override { return in_flight_.load() > 0; }

private:
    App*   app_;
    std::atomic<int> in_flight_{0};
    size_t last_daemon_output_size_ = 0;
    size_t last_xmrig_output_size_  = 0;
    int    last_daemon_state_       = -1;  // daemon::EmbeddedDaemon::State as int
    bool   last_rpc_connected_      = false;
    std::deque<std::pair<std::string, bool>> results_;  // {text, isError}
    std::mutex results_mutex_;
    // Chat diagnostics ring cursor: chat code logs via wallet::liteLog on BOTH variants, so the full-node
    // console surfaces those lines (as App messages) too — mirrors LiteConsoleExecutor's diag drain.
    std::uint64_t diag_gen_ = static_cast<std::uint64_t>(-1);
};

// ── Lite: diagnostics ring + backend console command ─────────────────────────
class LiteConsoleExecutor : public ConsoleCommandExecutor {
public:
    explicit LiteConsoleExecutor(App* app) : app_(app) {}

    bool isReady() const override;
    bool busy() const override;
    void submit(const std::string& cmd) override;
    bool pollResult(std::string& result, bool& isError) override;
    void pollLogLines(const ConsoleAddLineFn& add) override;
    // Lite: no daemon log / RPC trace — its diagnostics ring maps to the App + Error
    // channels, so offer errors-only + app-messages (plus the always-shown text filter).
    ConsoleLogFilterCaps logFilterCaps() const override { return {false, true, false, true}; }
    const std::vector<ConsoleCommandCategory>* commandReference() const override;
    void printHelp(const ConsoleAddLineFn& add) override;
    std::vector<ConsoleStatusLine> statusLines() const override;
    ConsoleStatusLine toolbarStatus() const override;

private:
    App*          app_;
    std::uint64_t diag_gen_ = static_cast<std::uint64_t>(-1);  // last consumed diagnostics generation
};

} // namespace ui
} // namespace dragonx
