/**
 * @file rpc.hpp
 * @brief Synor RPC SDK for C++
 *
 * Modern C++17 SDK for blockchain queries and real-time subscriptions.
 */

#pragma once

#include <string>
#include <vector>
#include <optional>
#include <future>
#include <memory>
#include <functional>
#include <cstdint>
#include "wallet.hpp"  // For Network, Priority, SynorException

namespace synor {
namespace rpc {

/// Transaction status
enum class TransactionStatus {
    Pending,
    Confirmed,
    Failed
};

/// RPC configuration
struct Config {
    std::string api_key;
    std::string endpoint = "https://rpc.synor.io/v1";
    std::string ws_endpoint = "wss://rpc.synor.io/v1/ws";
    Network network = Network::Mainnet;
    uint32_t timeout_ms = 30000;
    uint32_t retries = 3;
    bool debug = false;
};

/// Block header
struct BlockHeader {
    std::string hash;
    int64_t height;
    std::string previous_hash;
    int64_t timestamp;
    int32_t version;
    std::string merkle_root;
    int64_t nonce;
    double difficulty;
};

/// Script public key
struct ScriptPubKey {
    std::string asm_code;
    std::string hex;
    std::string type;
    std::optional<std::string> address;
};

/// Transaction input
struct TransactionInput {
    std::string txid;
    int32_t vout;
    std::optional<std::string> script_sig;
    int64_t sequence;
    std::vector<std::string> witness;
};

/// Transaction output
struct TransactionOutput {
    int64_t value;
    int32_t n;
    ScriptPubKey script_pubkey;
};

/// Transaction
struct Transaction {
    std::string txid;
    std::string hash;
    int32_t version;
    int32_t size;
    int32_t weight;
    int64_t lock_time;
    std::vector<TransactionInput> inputs;
    std::vector<TransactionOutput> outputs;
    int64_t fee;
    int32_t confirmations;
    std::optional<std::string> block_hash;
    std::optional<int64_t> block_height;
    std::optional<int64_t> timestamp;
    TransactionStatus status = TransactionStatus::Pending;
};

/// Block
struct Block {
    std::string hash;
    int64_t height;
    std::string previous_hash;
    int64_t timestamp;
    int32_t version;
    std::string merkle_root;
    int64_t nonce;
    double difficulty;
    std::vector<Transaction> transactions;
    int32_t size;
    int32_t weight;
};

/// Chain information
struct ChainInfo {
    std::string chain;
    int64_t blocks;
    int64_t headers;
    std::string best_block_hash;
    double difficulty;
    int64_t median_time;
    double verification_progress;
    std::string chain_work;
    bool pruned;
};

/// Mempool information
struct MempoolInfo {
    int32_t size;
    int64_t bytes;
    int64_t usage;
    int64_t max_mempool;
    double mempool_min_fee;
    double min_relay_tx_fee;
};

/// Fee estimate
struct FeeEstimate {
    Priority priority;
    int64_t fee_rate;
    int32_t estimated_blocks;
};

/// Transaction submission result
struct SubmitResult {
    std::string txid;
    bool accepted;
    std::optional<std::string> reason;
};

/// Subscription handle
class Subscription {
public:
    Subscription(const std::string& id, const std::string& channel,
                 std::function<void()> on_cancel);
    ~Subscription();

    Subscription(const Subscription&) = delete;
    Subscription& operator=(const Subscription&) = delete;
    Subscription(Subscription&&) noexcept;
    Subscription& operator=(Subscription&&) noexcept;

    const std::string& id() const { return id_; }
    const std::string& channel() const { return channel_; }

    void cancel();

private:
    std::string id_;
    std::string channel_;
    std::function<void()> on_cancel_;
};

/// RPC client
class Client {
public:
    explicit Client(const Config& config);
    ~Client();

    Client(const Client&) = delete;
    Client& operator=(const Client&) = delete;
    Client(Client&&) noexcept;
    Client& operator=(Client&&) noexcept;

    // Block operations
    Block get_latest_block();
    std::future<Block> get_latest_block_async();
    Block get_block(const std::string& hash_or_height);
    BlockHeader get_block_header(const std::string& hash_or_height);
    std::vector<Block> get_blocks(int64_t start_height, int64_t end_height);

    // Transaction operations
    Transaction get_transaction(const std::string& txid);
    std::string get_raw_transaction(const std::string& txid);
    SubmitResult send_raw_transaction(const std::string& hex);
    Transaction decode_raw_transaction(const std::string& hex);
    std::vector<Transaction> get_address_transactions(
        const std::string& address, int32_t limit = 50, int32_t offset = 0);

    // Fee estimation
    FeeEstimate estimate_fee(Priority priority = Priority::Medium);
    std::map<Priority, FeeEstimate> get_all_fee_estimates();

    // Chain information
    ChainInfo get_chain_info();
    MempoolInfo get_mempool_info();
    std::vector<std::string> get_mempool_transactions(int32_t limit = 100);

    // Subscriptions
    std::unique_ptr<Subscription> subscribe_blocks(
        std::function<void(const Block&)> callback);
    std::unique_ptr<Subscription> subscribe_address(
        const std::string& address,
        std::function<void(const Transaction&)> callback);
    std::unique_ptr<Subscription> subscribe_mempool(
        std::function<void(const Transaction&)> callback);

private:
    class Impl;
    std::unique_ptr<Impl> impl_;
};

} // namespace rpc
} // namespace synor
