#pragma once

#include <cstdint>
#include <optional>
#include <string>
#include <vector>

#include <nlohmann/json.hpp>

namespace dragonx::wallet {

enum class LiteResultCommand {
    Info,
    Height,
    Balance,
    Addresses,
    List,
    Notes,
    SyncStatus,
};

enum class LiteResultParserError {
    None,
    InvalidJson,
    ExpectedObject,
    ExpectedArray,
    MissingField,
    InvalidFieldType,
    InvalidFieldValue,
};

enum class LiteSpendableOutputKind {
    UnspentNote,
    Utxo,
    PendingNote,
    PendingUtxo,
};

enum class LiteTransactionDirection {
    Unknown,
    Send,
    Receive,
};

struct LiteResultParserIssue {
    std::string path;
    std::string message;
};

struct LiteParseResultBase {
    bool ok = false;
    LiteResultCommand command = LiteResultCommand::Info;
    LiteResultParserError error = LiteResultParserError::None;
    std::string errorPath;
    std::string errorMessage;
    std::vector<LiteResultParserIssue> issues;
};

struct LiteInfoResponse {
    std::optional<std::string> chainName;
    std::optional<std::string> version;
    std::optional<std::string> vendor;
    std::optional<std::int64_t> latestBlockHeight;
    std::optional<std::int64_t> difficulty;
    std::optional<std::int64_t> longestChain;
    std::optional<std::int64_t> notarized;
};

struct LiteHeightResponse {
    std::int64_t height = 0;
};

struct LiteBalanceResponse {
    std::uint64_t transparentBalance = 0;
    std::uint64_t shieldedBalance = 0;
    std::uint64_t unconfirmedBalance = 0;
    std::uint64_t verifiedShieldedBalance = 0;
    std::uint64_t spendableShieldedBalance = 0;
};

struct LiteAddressesResponse {
    std::vector<std::string> zAddresses;
    std::vector<std::string> tAddresses;
};

struct LiteSpendableOutput {
    LiteSpendableOutputKind kind = LiteSpendableOutputKind::UnspentNote;
    std::string address;
    std::string createdInTxid;
    std::optional<std::int64_t> createdInBlock;
    std::uint64_t value = 0;
    bool spent = false;
    bool unconfirmedSpent = false;
    bool pending = false;
    bool spendable = false;
};

struct LiteNotesResponse {
    std::vector<LiteSpendableOutput> unspentNotes;
    std::vector<LiteSpendableOutput> utxos;
    std::vector<LiteSpendableOutput> pendingNotes;
    std::vector<LiteSpendableOutput> pendingUtxos;
};

struct LiteTransactionOutput {
    std::string address;
    std::int64_t value = 0;
    std::string memo;
};

struct LiteTransactionRecord {
    std::string txid;
    std::int64_t datetime = 0;
    std::optional<std::int64_t> blockHeight;
    bool unconfirmed = false;
    LiteTransactionDirection direction = LiteTransactionDirection::Unknown;
    std::string address;
    std::int64_t amount = 0;
    std::string memo;
    std::optional<std::int64_t> position;
    std::vector<LiteTransactionOutput> outgoingMetadata;
};

struct LiteTransactionsResponse {
    std::vector<LiteTransactionRecord> transactions;
};

struct LiteSyncStatusResponse {
    std::uint64_t syncedBlocks = 0;
    std::uint64_t totalBlocks = 0;
    double progress = 0.0;
    bool complete = false;
};

struct LiteInfoParseResult : LiteParseResultBase { LiteInfoResponse info; };
struct LiteHeightParseResult : LiteParseResultBase { LiteHeightResponse height; };
struct LiteBalanceParseResult : LiteParseResultBase { LiteBalanceResponse balance; };
struct LiteAddressesParseResult : LiteParseResultBase { LiteAddressesResponse addresses; };
struct LiteNotesParseResult : LiteParseResultBase { LiteNotesResponse notes; };
struct LiteTransactionsParseResult : LiteParseResultBase { LiteTransactionsResponse transactions; };
struct LiteSyncStatusParseResult : LiteParseResultBase { LiteSyncStatusResponse syncStatus; };

const char* liteResultCommandName(LiteResultCommand command);
const char* liteResultParserErrorName(LiteResultParserError error);
const char* liteSpendableOutputKindName(LiteSpendableOutputKind kind);
const char* liteTransactionDirectionName(LiteTransactionDirection direction);

LiteInfoParseResult parseLiteInfoResponse(const std::string& jsonText);
LiteInfoParseResult parseLiteInfoResponse(const nlohmann::json& value);

LiteHeightParseResult parseLiteHeightResponse(const std::string& jsonText);
LiteHeightParseResult parseLiteHeightResponse(const nlohmann::json& value);

LiteBalanceParseResult parseLiteBalanceResponse(const std::string& jsonText);
LiteBalanceParseResult parseLiteBalanceResponse(const nlohmann::json& value);

LiteAddressesParseResult parseLiteAddressesResponse(const std::string& jsonText);
LiteAddressesParseResult parseLiteAddressesResponse(const nlohmann::json& value);

LiteNotesParseResult parseLiteNotesResponse(const std::string& jsonText);
LiteNotesParseResult parseLiteNotesResponse(const nlohmann::json& value);

LiteTransactionsParseResult parseLiteTransactionsResponse(const std::string& jsonText);
LiteTransactionsParseResult parseLiteTransactionsResponse(const nlohmann::json& value);

LiteSyncStatusParseResult parseLiteSyncStatusResponse(const std::string& jsonText);
LiteSyncStatusParseResult parseLiteSyncStatusResponse(const nlohmann::json& value);

} // namespace dragonx::wallet