#pragma once

#include <cstdint>
#include <cstddef>
#include <vector>
#include <string>

namespace paritypp {

// Cluster node health status
enum class node_status : uint8_t {
    OK       = 0, // All blocks intact, node healthy
    DEGRADED = 1, // Some peers unreachable, still operational
    REBUILD  = 2, // Data is being reconstructed
};

// Per-node status info returned by GET_STATUS
struct status_info {
    node_status status = node_status::OK;
    uint32_t    block_count = 0;    // Total blocks stored on this node
    uint32_t    group_count = 0;    // Number of distinct groups
    uint32_t    peers_total = 0;    // Expected peer count
    uint32_t    peers_online = 0;   // Reachable peers
};

// Wire protocol for parity block distribution over QUIC
//
// Message format:
//   [1 byte: message type]
//   [4 bytes: payload length, big-endian]
//   [payload...]

enum class msg_type : uint8_t {
    // Client -> Server
    STORE_BLOCK    = 0x01, // Store a block: [8B block_group_id][4B block_index][data...]
    FETCH_BLOCK    = 0x02, // Fetch a block: [8B block_group_id][4B block_index]
    DELETE_GROUP   = 0x03, // Delete block group: [8B block_group_id]
    LIST_BLOCKS    = 0x04, // List blocks in group: [8B block_group_id]
    GET_STATUS     = 0x05, // Get node status
    LIST_GROUPS    = 0x06, // List all groups: no payload
    VACUUM         = 0x07, // Compact block store: no payload
    AUTH_REQUEST   = 0x10, // Auth request: [1B name_len][name_bytes]
    AUTH_RESPONSE  = 0x11, // Auth response: [32B HMAC-SHA256(key, nonce)]

    // Server -> Client
    BLOCK_DATA     = 0x81, // Block data response: [8B block_group_id][4B block_index][data...]
    BLOCK_NOT_FOUND= 0x82, // Block not found: [8B block_group_id][4B block_index]
    BLOCK_LIST     = 0x83, // Block list: [8B block_group_id][4B count][4B index...]
    STATUS_RESP    = 0x84, // Status response: [1B status][4B block_count][4B group_count][4B peers_total][4B peers_online]
    GROUP_LIST     = 0x85, // Group list: [4B count][8B group_id...]
    OK             = 0x86, // Generic OK
    AUTH_CHALLENGE = 0x87, // Auth challenge: [32B nonce]
    AUTH_OK        = 0x88, // Auth accepted
    AUTH_DENIED    = 0x89, // Auth denied: [string reason]
    PROTOCOL_ERROR = 0xFF, // Error: [2B error_code][string message]
};

struct protocol {
    // Encode a message header + payload
    static std::vector<uint8_t> encode_message(msg_type type,
                                                const uint8_t* payload,
                                                size_t payload_len);

    static std::vector<uint8_t> encode_message(msg_type type,
                                                const std::vector<uint8_t>& payload);

    // Decode message type from header
    static bool decode_header(const uint8_t* data, size_t len,
                              msg_type& type, uint32_t& payload_len);

    // Helper: encode 64-bit big-endian
    static void encode_u64(uint8_t* dst, uint64_t val);
    static uint64_t decode_u64(const uint8_t* src);

    // Helper: encode 32-bit big-endian
    static void encode_u32(uint8_t* dst, uint32_t val);
    static uint32_t decode_u32(const uint8_t* src);

    // Build store_block payload
    static std::vector<uint8_t> make_store_block(uint64_t group_id,
                                                  uint32_t block_index,
                                                  const uint8_t* data,
                                                  size_t data_len);

    // Build fetch_block payload
    static std::vector<uint8_t> make_fetch_block(uint64_t group_id,
                                                  uint32_t block_index);

    // Build block_data response payload
    static std::vector<uint8_t> make_block_data(uint64_t group_id,
                                                 uint32_t block_index,
                                                 const uint8_t* data,
                                                 size_t data_len);

    // Parse store_block payload
    static bool parse_store_block(const uint8_t* payload, size_t len,
                                   uint64_t& group_id, uint32_t& block_index,
                                   const uint8_t*& data, size_t& data_len);

    // Parse fetch_block payload
    static bool parse_fetch_block(const uint8_t* payload, size_t len,
                                   uint64_t& group_id, uint32_t& block_index);

    // Parse block_data response payload
    static bool parse_block_data(const uint8_t* payload, size_t len,
                                  uint64_t& group_id, uint32_t& block_index,
                                  const uint8_t*& data, size_t& data_len);

    // Build auth_request payload: [1B name_len][name]
    static std::vector<uint8_t> make_auth_request(const std::string& client_name);

    // Build auth_response payload: [32B hmac]
    static std::vector<uint8_t> make_auth_response(const std::vector<uint8_t>& hmac);

    // Build auth_challenge payload: [32B nonce]
    static std::vector<uint8_t> make_auth_challenge(const std::vector<uint8_t>& nonce);

    // Parse auth_request: extract client_name
    static bool parse_auth_request(const uint8_t* payload, size_t len,
                                    std::string& client_name);

    // Parse auth_response: extract hmac (32 bytes)
    static bool parse_auth_response(const uint8_t* payload, size_t len,
                                     std::vector<uint8_t>& hmac);

    // Parse auth_challenge: extract nonce (32 bytes)
    static bool parse_auth_challenge(const uint8_t* payload, size_t len,
                                      std::vector<uint8_t>& nonce);

    // Build status_resp payload
    static std::vector<uint8_t> make_status_resp(const status_info& info);

    // Parse status_resp payload
    static bool parse_status_resp(const uint8_t* payload, size_t len,
                                   status_info& info);

    // Build group_list response payload
    static std::vector<uint8_t> make_group_list(const std::vector<uint64_t>& group_ids);

    // Parse group_list response payload
    static bool parse_group_list(const uint8_t* payload, size_t len,
                                  std::vector<uint64_t>& group_ids);

    static constexpr size_t HEADER_SIZE = 5; // 1B type + 4B length
};

} // namespace paritypp
