#include "console_input_model.h"

#include <algorithm>
#include <cctype>
#include <sstream>

namespace dragonx {
namespace ui {

const std::vector<std::string>& ConsoleRpcCommandNames()
{
    static const std::vector<std::string> commands = {
        "help", "getinfo", "stop",
        "getnetworkinfo", "getpeerinfo", "getconnectioncount",
        "addnode", "setban", "listbanned", "clearbanned", "ping",
        "getblockchaininfo", "getblockcount", "getbestblockhash",
        "getblock", "getblockhash", "getblockheader", "getdifficulty",
        "getrawmempool", "gettxout", "coinsupply", "getchaintips",
        "getmininginfo", "setgenerate", "getgenerate",
        "getnetworkhashps", "getblocksubsidy",
        "getbalance", "z_gettotalbalance", "z_getbalances",
        "getnewaddress", "z_getnewaddress",
        "listaddresses", "z_listaddresses",
        "sendtoaddress", "z_sendmany",
        "listtransactions", "listunspent", "z_listunspent",
        "z_getoperationstatus", "z_getoperationresult",
        "getwalletinfo", "backupwallet",
        "dumpprivkey", "importprivkey",
        "z_exportkey", "z_importkey",
        "signmessage", "settxfee",
        "createrawtransaction", "decoderawtransaction",
        "getrawtransaction", "sendrawtransaction", "signrawtransaction",
        "validateaddress", "z_validateaddress", "estimatefee",
        "clear"
    };
    return commands;
}

void AppendConsoleHistory(std::vector<std::string>& history,
                          const std::string& command,
                          std::size_t maxEntries)
{
    if (command.empty()) return;
    if (history.empty() || history.back() != command) {
        history.push_back(command);
        while (history.size() > maxEntries) {
            history.erase(history.begin());
        }
    }
}

int NavigateConsoleHistoryIndex(int currentIndex, std::size_t historySize, bool up)
{
    if (historySize == 0) return -1;
    if (up) {
        if (currentIndex < 0) return static_cast<int>(historySize) - 1;
        if (currentIndex > 0) return currentIndex - 1;
        return currentIndex;
    }
    if (currentIndex >= 0) {
        int next = currentIndex + 1;
        if (next >= static_cast<int>(historySize)) return -1;
        return next;
    }
    return currentIndex;
}

std::string ConsoleHistoryEntry(const std::vector<std::string>& history, int historyIndex)
{
    if (historyIndex < 0 || historyIndex >= static_cast<int>(history.size())) return {};
    return history[static_cast<std::size_t>(historyIndex)];
}

ConsoleCompletionResult CompleteConsoleCommand(const std::string& input)
{
    ConsoleCompletionResult result;
    if (input.empty()) return result;

    for (const auto& command : ConsoleRpcCommandNames()) {
        if (command.compare(0, input.size(), input) == 0) {
            result.matches.push_back(command);
        }
    }

    if (result.matches.empty()) return result;
    result.commonPrefix = result.matches.front();
    for (std::size_t matchIndex = 1; matchIndex < result.matches.size(); ++matchIndex) {
        const auto& match = result.matches[matchIndex];
        std::size_t prefixLength = 0;
        while (prefixLength < result.commonPrefix.size() &&
               prefixLength < match.size() &&
               result.commonPrefix[prefixLength] == match[prefixLength]) {
            ++prefixLength;
        }
        result.commonPrefix.resize(prefixLength);
    }
    return result;
}

std::vector<std::string> FormatConsoleCompletionLines(const std::vector<std::string>& matches,
                                                      std::size_t maxLineLength)
{
    std::vector<std::string> lines;
    std::string line = "  ";
    for (std::size_t matchIndex = 0; matchIndex < matches.size(); ++matchIndex) {
        if (matchIndex > 0) line += "  ";
        line += matches[matchIndex];
        if (line.length() > maxLineLength) {
            lines.push_back(line);
            line = "  ";
        }
    }
    if (line.length() > 2) {
        lines.push_back(line);
    }
    return lines;
}

std::vector<std::string> ParseConsoleCommandArgs(const std::string& command)
{
    std::vector<std::string> args;
    std::size_t index = 0;
    while (index < command.size()) {
        while (index < command.size() && (command[index] == ' ' || command[index] == '\t')) {
            ++index;
        }
        if (index >= command.size()) break;

        std::string token;
        if (command[index] == '"' || command[index] == '\'') {
            char quote = command[index++];
            while (index < command.size() && command[index] != quote) {
                token += command[index++];
            }
            if (index < command.size()) ++index;
        } else if (command[index] == '[' || command[index] == '{') {
            char open = command[index];
            char close = (open == '[') ? ']' : '}';
            int depth = 0;
            while (index < command.size()) {
                if (command[index] == open) ++depth;
                else if (command[index] == close) --depth;
                token += command[index++];
                if (depth == 0) break;
            }
        } else {
            while (index < command.size() && command[index] != ' ' && command[index] != '\t') {
                token += command[index++];
            }
        }
        if (!token.empty()) args.push_back(token);
    }
    return args;
}

ConsoleRpcCall BuildConsoleRpcCall(const std::string& command)
{
    auto args = ParseConsoleCommandArgs(command);
    ConsoleRpcCall call;
    if (args.empty()) return call;

    call.valid = true;
    call.method = args.front();

    for (std::size_t argIndex = 1; argIndex < args.size(); ++argIndex) {
        const std::string& arg = args[argIndex];
        if (!arg.empty() && (arg[0] == '{' || arg[0] == '[')) {
            auto parsed = nlohmann::json::parse(arg, nullptr, false);
            if (!parsed.is_discarded()) {
                call.params.push_back(parsed);
                continue;
            }
        }

        if (arg == "true") {
            call.params.push_back(true);
        } else if (arg == "false") {
            call.params.push_back(false);
        } else {
            try {
                if (arg.find('.') != std::string::npos) {
                    call.params.push_back(std::stod(arg));
                } else {
                    call.params.push_back(std::stoll(arg));
                }
            } catch (...) {
                call.params.push_back(arg);
            }
        }
    }

    return call;
}

std::vector<ConsoleResultLine> FormatConsoleRpcResultLines(const std::string& result,
                                                           bool isError)
{
    if (isError) {
        return {{"Error: " + result, ConsoleResultLineRole::Error}};
    }

    std::vector<ConsoleResultLine> lines;
    std::string normalized = (result == "null") ? "(no result)" : result;
    bool isJson = !normalized.empty() && (normalized[0] == '{' || normalized[0] == '[');
    std::istringstream stream(normalized);
    std::string line;
    while (std::getline(stream, line)) {
        ConsoleResultLineRole role = ConsoleResultLineRole::Result;
        if (isJson && !line.empty()) {
            std::string trimmed = line;
            std::size_t first = trimmed.find_first_not_of(" \t");
            if (first != std::string::npos) trimmed = trimmed.substr(first);
            if (!trimmed.empty()) {
                unsigned char firstChar = static_cast<unsigned char>(trimmed[0]);
                if (trimmed[0] == '{' || trimmed[0] == '}' ||
                    trimmed[0] == '[' || trimmed[0] == ']') {
                    role = ConsoleResultLineRole::JsonBrace;
                } else if (trimmed[0] == '"') {
                    if (trimmed.find("\": ") != std::string::npos ||
                        trimmed.find("\":") != std::string::npos) {
                        role = ConsoleResultLineRole::JsonKey;
                    } else {
                        role = ConsoleResultLineRole::JsonString;
                    }
                } else if (std::isdigit(firstChar) || trimmed[0] == '-') {
                    role = ConsoleResultLineRole::JsonNumber;
                } else if (trimmed == "true," || trimmed == "false," ||
                           trimmed == "true" || trimmed == "false" ||
                           trimmed == "null," || trimmed == "null") {
                    role = ConsoleResultLineRole::JsonNumber;
                }
            }
        }
        lines.push_back({line, role});
    }
    return lines;
}

} // namespace ui
} // namespace dragonx
