#include <iostream>
#include <string>
#include <cassert>
#include <cstring>
#include <memory>
#include <vector>
#include <fstream>
#include <cstdio>
#include <map>
#include <iomanip>
#include <array>

#include "connection.h"
#include "eventapi.h"
#include "socket.h"
#include "exception.h"

// Include certs from generated header
#include "https_certs.h" 

using namespace netplus;

// ============================================================================
// HTTP/2 Implementation (RFC 7540)
// ============================================================================

// HTTP/2 Frame Types
enum class H2FrameType : uint8_t {
    DATA          = 0x00,
    HEADERS       = 0x01,
    PRIORITY      = 0x02,
    RST_STREAM    = 0x03,
    SETTINGS      = 0x04,
    PUSH_PROMISE  = 0x05,
    PING          = 0x06,
    GOAWAY        = 0x07,
    WINDOW_UPDATE = 0x08,
    CONTINUATION  = 0x09
};

// HTTP/2 Settings Identifiers
enum class H2Settings : uint16_t {
    HEADER_TABLE_SIZE      = 0x01,
    ENABLE_PUSH            = 0x02,
    MAX_CONCURRENT_STREAMS = 0x03,
    INITIAL_WINDOW_SIZE    = 0x04,
    MAX_FRAME_SIZE         = 0x05,
    MAX_HEADER_LIST_SIZE   = 0x06
};

// HTTP/2 Flags
enum H2Flags : uint8_t {
    FLAG_END_STREAM  = 0x01,
    FLAG_ACK         = 0x01,  // For SETTINGS and PING
    FLAG_END_HEADERS = 0x04,
    FLAG_PADDED      = 0x08,
    FLAG_PRIORITY    = 0x20
};

// HTTP/2 Error Codes
enum class H2Error : uint32_t {
    H2_NO_ERROR         = 0x00,
    PROTOCOL_ERROR      = 0x01,
    INTERNAL_ERROR      = 0x02,
    FLOW_CONTROL_ERROR  = 0x03,
    SETTINGS_TIMEOUT    = 0x04,
    STREAM_CLOSED       = 0x05,
    FRAME_SIZE_ERROR    = 0x06,
    REFUSED_STREAM      = 0x07,
    CANCEL              = 0x08,
    COMPRESSION_ERROR   = 0x09,
    CONNECT_ERROR       = 0x0a,
    ENHANCE_YOUR_CALM   = 0x0b,
    INADEQUATE_SECURITY = 0x0c,
    HTTP_1_1_REQUIRED   = 0x0d
};

// HPACK Static Table (RFC 7541 Appendix A) - Partial
struct HPACKStaticEntry {
    const char* name;
    const char* value;
};

static const HPACKStaticEntry HPACK_STATIC_TABLE[] = {
    {nullptr, nullptr},                      // 0 - unused
    {":authority", ""},                       // 1
    {":method", "GET"},                       // 2
    {":method", "POST"},                      // 3
    {":path", "/"},                           // 4
    {":path", "/index.html"},                 // 5
    {":scheme", "http"},                      // 6
    {":scheme", "https"},                     // 7
    {":status", "200"},                       // 8
    {":status", "204"},                       // 9
    {":status", "206"},                       // 10
    {":status", "304"},                       // 11
    {":status", "400"},                       // 12
    {":status", "404"},                       // 13
    {":status", "500"},                       // 14
    {"accept-charset", ""},                   // 15
    {"accept-encoding", "gzip, deflate"},     // 16
    {"accept-language", ""},                  // 17
    {"accept-ranges", ""},                    // 18
    {"accept", ""},                           // 19
    {"access-control-allow-origin", ""},      // 20
    {"age", ""},                              // 21
    {"allow", ""},                            // 22
    {"authorization", ""},                    // 23
    {"cache-control", ""},                    // 24
    {"content-disposition", ""},              // 25
    {"content-encoding", ""},                 // 26
    {"content-language", ""},                 // 27
    {"content-length", ""},                   // 28
    {"content-location", ""},                 // 29
    {"content-range", ""},                    // 30
    {"content-type", ""},                     // 31
    {"cookie", ""},                           // 32
    {"date", ""},                             // 33
    {"etag", ""},                             // 34
    {"expect", ""},                           // 35
    {"expires", ""},                          // 36
    {"from", ""},                             // 37
    {"host", ""},                             // 38
    {"if-match", ""},                         // 39
    {"if-modified-since", ""},                // 40
    {"if-none-match", ""},                    // 41
    {"if-range", ""},                         // 42
    {"if-unmodified-since", ""},              // 43
    {"last-modified", ""},                    // 44
    {"link", ""},                             // 45
    {"location", ""},                         // 46
    {"max-forwards", ""},                     // 47
    {"proxy-authenticate", ""},               // 48
    {"proxy-authorization", ""},              // 49
    {"range", ""},                            // 50
    {"referer", ""},                          // 51
    {"refresh", ""},                          // 52
    {"retry-after", ""},                      // 53
    {"server", ""},                           // 54
    {"set-cookie", ""},                       // 55
    {"strict-transport-security", ""},        // 56
    {"transfer-encoding", ""},                // 57
    {"user-agent", ""},                       // 58
    {"vary", ""},                             // 59
    {"via", ""},                              // 60
    {"www-authenticate", ""}                  // 61
};
static constexpr size_t HPACK_STATIC_TABLE_SIZE = sizeof(HPACK_STATIC_TABLE) / sizeof(HPACK_STATIC_TABLE[0]);

// HTTP/2 connection preface (RFC 7540 Section 3.5)
static const char H2_CONNECTION_PREFACE[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
static constexpr size_t H2_PREFACE_LEN = 24;

// HTTP/2 Frame structure
struct H2Frame {
    uint32_t length;      // 24 bits
    uint8_t type;
    uint8_t flags;
    uint32_t stream_id;   // 31 bits (R bit is reserved)
    const uint8_t* payload = nullptr; // Points into receive buffer — no copy
};

// ============================================================================
// HPACK Huffman Decoding Table (RFC 7541 Appendix B)
// ============================================================================
struct HuffmanEntry {
    uint32_t code;
    uint8_t  bits;
    uint8_t  sym;
};

static const HuffmanEntry HUFFMAN_TABLE[] = {
    {0x1ff8,     13, 0},   {0x7fffd8,   23, 1},   {0xfffffe2,  28, 2},   {0xfffffe3,  28, 3},
    {0xfffffe4,  28, 4},   {0xfffffe5,  28, 5},   {0xfffffe6,  28, 6},   {0xfffffe7,  28, 7},
    {0xfffffe8,  28, 8},   {0xffffea,   24, 9},   {0x3ffffffc, 30, 10},  {0xfffffe9,  28, 11},
    {0xfffffea,  28, 12},  {0x3ffffffd, 30, 13},  {0xfffffeb,  28, 14},  {0xfffffec,  28, 15},
    {0xfffffed,  28, 16},  {0xfffffee,  28, 17},  {0xfffffef,  28, 18},  {0xffffff0,  28, 19},
    {0xffffff1,  28, 20},  {0xffffff2,  28, 21},  {0x3ffffffe, 30, 22},  {0xffffff3,  28, 23},
    {0xffffff4,  28, 24},  {0xffffff5,  28, 25},  {0xffffff6,  28, 26},  {0xffffff7,  28, 27},
    {0xffffff8,  28, 28},  {0xffffff9,  28, 29},  {0xffffffa,  28, 30},  {0xffffffb,  28, 31},
    {0x14,        6, 32},  {0x3f8,      10, 33},  {0x3f9,      10, 34},  {0xffa,      12, 35},
    {0x1ff9,     13, 36},  {0x15,        6, 37},  {0xf8,        8, 38},  {0x7fa,      11, 39},
    {0x3fa,      10, 40},  {0x3fb,      10, 41},  {0xf9,        8, 42},  {0x7fb,      11, 43},
    {0xfa,        8, 44},  {0x16,        6, 45},  {0x17,        6, 46},  {0x18,        6, 47},
    {0x0,         5, 48},  {0x1,         5, 49},  {0x2,         5, 50},  {0x19,        6, 51},
    {0x1a,        6, 52},  {0x1b,        6, 53},  {0x1c,        6, 54},  {0x1d,        6, 55},
    {0x1e,        6, 56},  {0x1f,        6, 57},  {0x5c,        7, 58},  {0xfb,        8, 59},
    {0x7ffc,     15, 60},  {0x20,        6, 61},  {0xffb,      12, 62},  {0x3fc,      10, 63},
    {0x1ffa,     13, 64},  {0x21,        6, 65},  {0x5d,        7, 66},  {0x5e,        7, 67},
    {0x5f,        7, 68},  {0x60,        7, 69},  {0x61,        7, 70},  {0x62,        7, 71},
    {0x63,        7, 72},  {0x64,        7, 73},  {0x65,        7, 74},  {0x66,        7, 75},
    {0x67,        7, 76},  {0x68,        7, 77},  {0x69,        7, 78},  {0x6a,        7, 79},
    {0x6b,        7, 80},  {0x6c,        7, 81},  {0x6d,        7, 82},  {0x6e,        7, 83},
    {0x6f,        7, 84},  {0x70,        7, 85},  {0x71,        7, 86},  {0x72,        7, 87},
    {0xfc,        8, 88},  {0x73,        7, 89},  {0xfd,        8, 90},  {0x1ffb,     13, 91},
    {0x7fff0,    19, 92},  {0x1ffc,     13, 93},  {0x3ffc,     14, 94},  {0x22,        6, 95},
    {0x7ffd,     15, 96},  {0x3,         5, 97},  {0x23,        6, 98},  {0x4,         5, 99},
    {0x24,        6, 100}, {0x5,         5, 101}, {0x25,        6, 102}, {0x26,        6, 103},
    {0x27,        6, 104}, {0x6,         5, 105}, {0x74,        7, 106}, {0x75,        7, 107},
    {0x28,        6, 108}, {0x29,        6, 109}, {0x2a,        6, 110}, {0x7,         5, 111},
    {0x2b,        6, 112}, {0x76,        7, 113}, {0x2c,        6, 114}, {0x8,         5, 115},
    {0x9,         5, 116}, {0x2d,        6, 117}, {0x77,        7, 118}, {0x78,        7, 119},
    {0x79,        7, 120}, {0x7a,        7, 121}, {0x7b,        7, 122}, {0x7ffe,     15, 123},
    {0x7fc,      11, 124}, {0x3ffd,     14, 125}, {0x1ffd,     13, 126}, {0xffffffc,  28, 127},
    {0xfffe6,    20, 128}, {0x3fffd2,   22, 129}, {0xfffe7,    20, 130}, {0xfffe8,    20, 131},
    {0x3fffd3,   22, 132}, {0x3fffd4,   22, 133}, {0x3fffd5,   22, 134}, {0x7fffd9,   23, 135},
    {0x3fffd6,   22, 136}, {0x7fffda,   23, 137}, {0x7fffdb,   23, 138}, {0x7fffdc,   23, 139},
    {0x7fffdd,   23, 140}, {0x7fffde,   23, 141}, {0xffffeb,   24, 142}, {0x7fffdf,   23, 143},
    {0xffffec,   24, 144}, {0xffffed,   24, 145}, {0x3fffd7,   22, 146}, {0x7fffe0,   23, 147},
    {0xffffee,   24, 148}, {0x7fffe1,   23, 149}, {0x7fffe2,   23, 150}, {0x7fffe3,   23, 151},
    {0x7fffe4,   23, 152}, {0x1fffdc,   21, 153}, {0x3fffd8,   22, 154}, {0x7fffe5,   23, 155},
    {0x3fffd9,   22, 156}, {0x7fffe6,   23, 157}, {0x7fffe7,   23, 158}, {0xffffef,   24, 159},
    {0x3fffda,   22, 160}, {0x1fffdd,   21, 161}, {0xfffe9,    20, 162}, {0x3fffdb,   22, 163},
    {0x3fffdc,   22, 164}, {0x7fffe8,   23, 165}, {0x7fffe9,   23, 166}, {0x1fffde,   21, 167},
    {0x7fffea,   23, 168}, {0x3fffdd,   22, 169}, {0x3fffde,   22, 170}, {0xfffff0,   24, 171},
    {0x1fffdf,   21, 172}, {0x3fffdf,   22, 173}, {0x7fffeb,   23, 174}, {0x7fffec,   23, 175},
    {0x1fffe0,   21, 176}, {0x1fffe1,   21, 177}, {0x3fffe0,   22, 178}, {0x1fffe2,   21, 179},
    {0x7fffed,   23, 180}, {0x3fffe1,   22, 181}, {0x7fffee,   23, 182}, {0x7fffef,   23, 183},
    {0xfffea,    20, 184}, {0x3fffe2,   22, 185}, {0x3fffe3,   22, 186}, {0x3fffe4,   22, 187},
    {0x7ffff0,   23, 188}, {0x3fffe5,   22, 189}, {0x3fffe6,   22, 190}, {0x7ffff1,   23, 191},
    {0x3ffffe0,  26, 192}, {0x3ffffe1,  26, 193}, {0xfffeb,    20, 194}, {0x7fff1,    19, 195},
    {0x3fffe7,   22, 196}, {0x7ffff2,   23, 197}, {0x3fffe8,   22, 198}, {0x1ffffec,  25, 199},
    {0x3ffffe2,  26, 200}, {0x3ffffe3,  26, 201}, {0x3ffffe4,  26, 202}, {0x7ffffde,  27, 203},
    {0x7ffffdf,  27, 204}, {0x3ffffe5,  26, 205}, {0xfffff1,   24, 206}, {0x1ffffed,  25, 207},
    {0x7fff2,    19, 208}, {0x1fffe3,   21, 209}, {0x3ffffe6,  26, 210}, {0x7ffffe0,  27, 211},
    {0x7ffffe1,  27, 212}, {0x3ffffe7,  26, 213}, {0x7ffffe2,  27, 214}, {0xfffff2,   24, 215},
    {0x1fffe4,   21, 216}, {0x1fffe5,   21, 217}, {0x3ffffe8,  26, 218}, {0x3ffffe9,  26, 219},
    {0xffffffd,  28, 220}, {0x7ffffe3,  27, 221}, {0x7ffffe4,  27, 222}, {0x7ffffe5,  27, 223},
    {0xfffec,    20, 224}, {0xfffff3,   24, 225}, {0xfffed,    20, 226}, {0x1fffe6,   21, 227},
    {0x3fffe9,   22, 228}, {0x1fffe7,   21, 229}, {0x1fffe8,   21, 230}, {0x7ffff3,   23, 231},
    {0x3fffea,   22, 232}, {0x3fffeb,   22, 233}, {0x1ffffee,  25, 234}, {0x1ffffef,  25, 235},
    {0xfffff4,   24, 236}, {0xfffff5,   24, 237}, {0x3ffffea,  26, 238}, {0x7ffff4,   23, 239},
    {0x3ffffeb,  26, 240}, {0x7ffffe6,  27, 241}, {0x3ffffec,  26, 242}, {0x3ffffed,  26, 243},
    {0x7ffffe7,  27, 244}, {0x7ffffe8,  27, 245}, {0x7ffffe9,  27, 246}, {0x7ffffea,  27, 247},
    {0x7ffffeb,  27, 248}, {0xffffffe,  28, 249}, {0x7ffffec,  27, 250}, {0x7ffffed,  27, 251},
    {0x7ffffee,  27, 252}, {0x7ffffef,  27, 253}, {0x7fffff0,  27, 254}, {0x3ffffee,  26, 255},
};

// Build a lookup table for fast Huffman decoding (indexed by first 8 bits)
// For codes <= 8 bits this gives direct decode; for longer codes we fall back.
static std::string huffmanDecode(const uint8_t* data, size_t len) {
    std::string result;
    result.reserve(len * 2);  // Huffman typically expands
    uint64_t bits = 0;
    int nbits = 0;

    for (size_t i = 0; i < len; ++i) {
        bits = (bits << 8) | data[i];
        nbits += 8;

        while (nbits >= 5) {  // shortest Huffman code is 5 bits
            bool found = false;
            for (size_t t = 0; t < 256; ++t) {
                const auto& e = HUFFMAN_TABLE[t];
                if (e.bits <= (uint8_t)nbits) {
                    uint32_t candidate = (uint32_t)(bits >> (nbits - e.bits));
                    if (candidate == e.code) {
                        result.push_back((char)e.sym);
                        nbits -= e.bits;
                        bits &= ((uint64_t)1 << nbits) - 1;
                        found = true;
                        break;
                    }
                }
            }
            if (!found) break;  // remaining bits are padding
        }
    }
    return result;
}

// HTTP/2 Connection State
struct H2Connection {
    bool preface_received = false;
    bool settings_sent = false;
    bool settings_acked = false;
    uint32_t initial_window_size = 65535;
    uint32_t max_frame_size = 16384;
    uint32_t max_concurrent_streams = 100;
    uint32_t header_table_size = 4096;
    uint32_t local_window = 65535;
    uint32_t remote_window = 65535;
    uint32_t last_stream_id = 0;
    std::map<uint32_t, int32_t> stream_windows;  // Per-stream flow control
};

// Write HTTP/2 frame header (9 bytes) directly into buffer
static void writeH2FrameHeader(uint8_t* out, uint32_t length, uint8_t type, uint8_t flags, uint32_t stream_id) {
    out[0] = (length >> 16) & 0xFF;
    out[1] = (length >> 8) & 0xFF;
    out[2] = length & 0xFF;
    out[3] = type;
    out[4] = flags;
    out[5] = (stream_id >> 24) & 0x7F;
    out[6] = (stream_id >> 16) & 0xFF;
    out[7] = (stream_id >> 8) & 0xFF;
    out[8] = stream_id & 0xFF;
}

// Append HTTP/2 SETTINGS frame directly into SendData
static void appendH2SettingsFrame(condata<char>& out, bool ack = false) {
    if (ack) {
        uint8_t buf[9];
        writeH2FrameHeader(buf, 0, static_cast<uint8_t>(H2FrameType::SETTINGS), FLAG_ACK, 0);
        out.append(reinterpret_cast<const char*>(buf), 9);
        return;
    }
    // 3 settings * 6 bytes = 18 bytes payload
    uint8_t buf[9 + 18];
    writeH2FrameHeader(buf, 18, static_cast<uint8_t>(H2FrameType::SETTINGS), 0, 0);
    uint8_t* p = buf + 9;
    // MAX_CONCURRENT_STREAMS = 100
    p[0]=0; p[1]=0x03; p[2]=0; p[3]=0; p[4]=0; p[5]=0x64; p += 6;
    // INITIAL_WINDOW_SIZE = 65535
    p[0]=0; p[1]=0x04; p[2]=0; p[3]=0; p[4]=0xFF; p[5]=0xFF; p += 6;
    // MAX_FRAME_SIZE = 16384
    p[0]=0; p[1]=0x05; p[2]=0; p[3]=0; p[4]=0x40; p[5]=0x00;
    out.append(reinterpret_cast<const char*>(buf), sizeof(buf));
}

// Append HTTP/2 WINDOW_UPDATE frame directly into SendData
static void appendH2WindowUpdate(condata<char>& out, uint32_t stream_id, uint32_t increment) {
    uint8_t buf[13];
    writeH2FrameHeader(buf, 4, static_cast<uint8_t>(H2FrameType::WINDOW_UPDATE), 0, stream_id);
    buf[9]  = (increment >> 24) & 0x7F;
    buf[10] = (increment >> 16) & 0xFF;
    buf[11] = (increment >> 8) & 0xFF;
    buf[12] = increment & 0xFF;
    out.append(reinterpret_cast<const char*>(buf), 13);
}

// HPACK integer encoding (RFC 7541 Section 5.1)
static void hpackEncodeInteger(std::vector<uint8_t>& out, uint64_t value, uint8_t prefix_bits, uint8_t prefix_mask) {
    uint8_t max_prefix = (1 << prefix_bits) - 1;
    
    if (value < max_prefix) {
        out.push_back(prefix_mask | static_cast<uint8_t>(value));
    } else {
        out.push_back(prefix_mask | max_prefix);
        value -= max_prefix;
        while (value >= 128) {
            out.push_back(static_cast<uint8_t>((value & 0x7F) | 0x80));
            value >>= 7;
        }
        out.push_back(static_cast<uint8_t>(value));
    }
}

// HPACK integer decoding (RFC 7541 Section 5.1)
static uint64_t hpackDecodeInteger(const uint8_t* data, size_t len, size_t& pos, uint8_t prefix_bits) {
    if (pos >= len) return 0;
    
    uint8_t max_prefix = (1 << prefix_bits) - 1;
    uint64_t value = data[pos] & max_prefix;
    pos++;
    
    if (value < max_prefix) {
        return value;
    }
    
    uint64_t m = 0;
    while (pos < len) {
        uint8_t b = data[pos++];
        value += (b & 0x7F) << m;
        m += 7;
        if (!(b & 0x80)) break;
    }
    return value;
}

// HPACK string encoding (RFC 7541 Section 5.2) - no Huffman for simplicity
static void hpackEncodeString(std::vector<uint8_t>& out, const std::string& str) {
    // No Huffman encoding (bit 7 = 0)
    hpackEncodeInteger(out, str.size(), 7, 0x00);
    out.insert(out.end(), str.begin(), str.end());
}

// HPACK string decoding (with Huffman support)
static std::string hpackDecodeString(const uint8_t* data, size_t len, size_t& pos) {
    if (pos >= len) return "";
    
    bool huffman = (data[pos] & 0x80) != 0;
    uint64_t str_len = hpackDecodeInteger(data, len, pos, 7);
    
    if (pos + str_len > len) return "";
    
    std::string result;
    if (huffman) {
        result = huffmanDecode(data + pos, str_len);
    } else {
        result.assign(reinterpret_cast<const char*>(data + pos), str_len);
    }
    pos += str_len;
    return result;
}

// HPACK encode header with literal name (indexed or not)
static void hpackEncodeLiteralHeader(std::vector<uint8_t>& out, uint8_t static_idx, const std::string& value) {
    // Literal Header Field with Incremental Indexing - Indexed Name
    // 01xxxxxx where xxxxxx is the index
    hpackEncodeInteger(out, static_idx, 6, 0x40);
    hpackEncodeString(out, value);
}

// HPACK encode header with new name
static void hpackEncodeLiteralHeaderNewName(std::vector<uint8_t>& out, const std::string& name, const std::string& value) {
    // Literal Header Field with Incremental Indexing - New Name
    // 01000000 followed by name and value
    out.push_back(0x40);
    hpackEncodeString(out, name);
    hpackEncodeString(out, value);
}

// HPACK encode indexed header (fully in static table)
static void hpackEncodeIndexed(std::vector<uint8_t>& out, uint8_t index) {
    // Indexed Header Field: 1xxxxxxx
    hpackEncodeInteger(out, index, 7, 0x80);
}

// Build HPACK-encoded headers for response
static std::vector<uint8_t> buildHpackHeaders(uint16_t status, const std::string& content_type, size_t content_length) {
    std::vector<uint8_t> headers;
    
    // :status header
    if (status == 200) {
        hpackEncodeIndexed(headers, 8);  // :status: 200
    } else if (status == 204) {
        hpackEncodeIndexed(headers, 9);
    } else if (status == 404) {
        hpackEncodeIndexed(headers, 13);
    } else if (status == 500) {
        hpackEncodeIndexed(headers, 14);
    } else {
        // Literal with indexed name
        hpackEncodeLiteralHeader(headers, 8, std::to_string(status));
    }
    
    // content-type header (index 31)
    hpackEncodeLiteralHeader(headers, 31, content_type);
    
    // content-length header (index 28)
    hpackEncodeLiteralHeader(headers, 28, std::to_string(content_length));
    
    // server header (index 54)
    hpackEncodeLiteralHeader(headers, 54, "libnetplus/1.0");
    
    return headers;
}

// Append HTTP/2 HEADERS frame directly into SendData
static void appendH2HeadersFrame(condata<char>& out, uint32_t stream_id,
                                  const std::vector<uint8_t>& hpack_headers,
                                  bool end_stream = false) {
    uint8_t hdr[9];
    uint8_t flags = FLAG_END_HEADERS;
    if (end_stream) flags |= FLAG_END_STREAM;
    writeH2FrameHeader(hdr, hpack_headers.size(), static_cast<uint8_t>(H2FrameType::HEADERS), flags, stream_id);
    out.append(reinterpret_cast<const char*>(hdr), 9);
    out.append(reinterpret_cast<const char*>(hpack_headers.data()), hpack_headers.size());
}

// Append HTTP/2 DATA frame directly into SendData
static void appendH2DataFrame(condata<char>& out, uint32_t stream_id,
                               const std::string& body, bool end_stream = true) {
    uint8_t hdr[9];
    uint8_t flags = end_stream ? FLAG_END_STREAM : 0;
    writeH2FrameHeader(hdr, body.size(), static_cast<uint8_t>(H2FrameType::DATA), flags, stream_id);
    out.append(reinterpret_cast<const char*>(hdr), 9);
    out.append(body.data(), body.size());
}

// Append HTTP/2 GOAWAY frame directly into SendData
static void appendH2GoawayFrame(condata<char>& out, uint32_t last_stream_id,
                                 H2Error error_code) {
    uint8_t buf[9 + 8];
    writeH2FrameHeader(buf, 8, static_cast<uint8_t>(H2FrameType::GOAWAY), 0, 0);
    uint32_t err = static_cast<uint32_t>(error_code);
    buf[9]  = (last_stream_id >> 24) & 0x7F;
    buf[10] = (last_stream_id >> 16) & 0xFF;
    buf[11] = (last_stream_id >> 8) & 0xFF;
    buf[12] = last_stream_id & 0xFF;
    buf[13] = (err >> 24) & 0xFF;
    buf[14] = (err >> 16) & 0xFF;
    buf[15] = (err >> 8) & 0xFF;
    buf[16] = err & 0xFF;
    out.append(reinterpret_cast<const char*>(buf), sizeof(buf));
}

// Parse HTTP/2 frame header
static bool parseH2FrameHeader(const uint8_t* data, size_t len, H2Frame& frame) {
    if (len < 9) return false;
    
    frame.length = (uint32_t(data[0]) << 16) | (uint32_t(data[1]) << 8) | data[2];
    frame.type = data[3];
    frame.flags = data[4];
    frame.stream_id = ((uint32_t(data[5]) & 0x7F) << 24) | (uint32_t(data[6]) << 16) |
                      (uint32_t(data[7]) << 8) | data[8];
    return true;
}

// Parse HPACK headers from HEADERS frame (simplified)
static std::map<std::string, std::string> parseHpackHeaders(const uint8_t* data, size_t len) {
    std::map<std::string, std::string> headers;
    size_t pos = 0;
    
    while (pos < len) {
        uint8_t first_byte = data[pos];
        
        if (first_byte & 0x80) {
            // Indexed Header Field
            uint64_t index = hpackDecodeInteger(data, len, pos, 7);
            if (index > 0 && index < HPACK_STATIC_TABLE_SIZE) {
                headers[HPACK_STATIC_TABLE[index].name] = HPACK_STATIC_TABLE[index].value;
            }
        } else if ((first_byte & 0xC0) == 0x40) {
            // Literal Header Field with Incremental Indexing
            uint64_t index = hpackDecodeInteger(data, len, pos, 6);
            std::string name, value;
            if (index > 0 && index < HPACK_STATIC_TABLE_SIZE) {
                name = HPACK_STATIC_TABLE[index].name;
            } else {
                name = hpackDecodeString(data, len, pos);
            }
            value = hpackDecodeString(data, len, pos);
            headers[name] = value;
        } else if ((first_byte & 0xF0) == 0x00) {
            // Literal Header Field without Indexing
            uint64_t index = hpackDecodeInteger(data, len, pos, 4);
            std::string name, value;
            if (index > 0 && index < HPACK_STATIC_TABLE_SIZE) {
                name = HPACK_STATIC_TABLE[index].name;
            } else {
                name = hpackDecodeString(data, len, pos);
            }
            value = hpackDecodeString(data, len, pos);
            headers[name] = value;
        } else if ((first_byte & 0xF0) == 0x10) {
            // Literal Header Field Never Indexed
            uint64_t index = hpackDecodeInteger(data, len, pos, 4);
            std::string name, value;
            if (index > 0 && index < HPACK_STATIC_TABLE_SIZE) {
                name = HPACK_STATIC_TABLE[index].name;
            } else {
                name = hpackDecodeString(data, len, pos);
            }
            value = hpackDecodeString(data, len, pos);
            headers[name] = value;
        } else if ((first_byte & 0xE0) == 0x20) {
            // Dynamic Table Size Update
            hpackDecodeInteger(data, len, pos, 5);
            // Ignore for now
        } else {
            // Unknown, skip
            pos++;
        }
    }
    
    return headers;
}

// HTTP/2 connection state per connection
static std::map<netplus::socket*, H2Connection> h2_connections;

// Process HTTP/2 frames and generate response
static void processH2Frames(con& curcon, netplus::ssl* sslsock, const std::string& hostname) {
    auto* sock = curcon.slots[0].csock.get();
    H2Connection& h2 = h2_connections[sock];
    
    const uint8_t* data = reinterpret_cast<const uint8_t*>(curcon.RecvData.data());
    size_t len = curcon.RecvData.size();
    size_t offset = 0;
    
    // Check for connection preface first
    if (!h2.preface_received) {
        if (len >= H2_PREFACE_LEN && memcmp(data, H2_CONNECTION_PREFACE, H2_PREFACE_LEN) == 0) {
            h2.preface_received = true;
            offset = H2_PREFACE_LEN;
            
            // Send server SETTINGS
            if (!h2.settings_sent) {
                appendH2SettingsFrame(curcon.SendData, false);
                h2.settings_sent = true;
            }
        } else {
            return;
        }
    }
    
    // Process frames — payload is accessed via pointer into RecvData (no copy)
    while (offset + 9 <= len) {
        H2Frame frame;
        if (!parseH2FrameHeader(data + offset, len - offset, frame)) {
            break;
        }
        
        if (offset + 9 + frame.length > len) {
            break;  // Incomplete frame, wait for more data
        }
        
        frame.payload = data + offset + 9;
        
        switch (static_cast<H2FrameType>(frame.type)) {
            case H2FrameType::SETTINGS: {
                if (frame.flags & FLAG_ACK) {
                    h2.settings_acked = true;
                } else {
                    for (size_t i = 0; i + 6 <= frame.length; i += 6) {
                        uint16_t id = (frame.payload[i] << 8) | frame.payload[i+1];
                        uint32_t val = (frame.payload[i+2] << 24) | (frame.payload[i+3] << 16) |
                                      (frame.payload[i+4] << 8) | frame.payload[i+5];
                        switch (static_cast<H2Settings>(id)) {
                            case H2Settings::HEADER_TABLE_SIZE:
                                h2.header_table_size = val; break;
                            case H2Settings::INITIAL_WINDOW_SIZE:
                                h2.initial_window_size = val; break;
                            case H2Settings::MAX_FRAME_SIZE:
                                h2.max_frame_size = val; break;
                            case H2Settings::MAX_CONCURRENT_STREAMS:
                                h2.max_concurrent_streams = val; break;
                            default: break;
                        }
                    }
                    appendH2SettingsFrame(curcon.SendData, true);
                }
                break;
            }
            
            case H2FrameType::WINDOW_UPDATE: {
                if (frame.length >= 4) {
                    uint32_t increment = ((frame.payload[0] & 0x7F) << 24) |
                                        (frame.payload[1] << 16) |
                                        (frame.payload[2] << 8) |
                                        frame.payload[3];
                    if (frame.stream_id == 0) {
                        h2.remote_window += increment;
                    } else {
                        h2.stream_windows[frame.stream_id] += increment;
                    }
                }
                break;
            }
            
            case H2FrameType::HEADERS: {
                h2.last_stream_id = std::max(h2.last_stream_id, frame.stream_id);
                
                auto headers = parseHpackHeaders(frame.payload, frame.length);
                
                std::string body;
                if (hostname == "test1.localhost") {
                    body = "Hello from test1.localhost (HTTPS/HTTP2)\n";
                } else if (hostname == "test2.localhost") {
                    body = "Hello from test2.localhost (HTTPS/HTTP2)\n";
                } else {
                    body = "Hello world (HTTPS/HTTP2)\n";
                }
                
                auto hpack_headers = buildHpackHeaders(200, "text/plain", body.size());
                appendH2HeadersFrame(curcon.SendData, frame.stream_id, hpack_headers, false);
                appendH2DataFrame(curcon.SendData, frame.stream_id, body, true);
                break;
            }
            
            case H2FrameType::DATA: {
                if (frame.length > 0) {
                    appendH2WindowUpdate(curcon.SendData, 0, frame.length);
                    appendH2WindowUpdate(curcon.SendData, frame.stream_id, frame.length);
                }
                break;
            }
            
            case H2FrameType::PING: {
                if (!(frame.flags & FLAG_ACK)) {
                    uint8_t hdr[9];
                    writeH2FrameHeader(hdr, 8, static_cast<uint8_t>(H2FrameType::PING), FLAG_ACK, 0);
                    curcon.SendData.append(reinterpret_cast<const char*>(hdr), 9);
                    if (frame.length >= 8) {
                        curcon.SendData.append(reinterpret_cast<const char*>(frame.payload), 8);
                    } else {
                        uint8_t zeros[8] = {};
                        curcon.SendData.append(reinterpret_cast<const char*>(zeros), 8);
                    }
                }
                break;
            }
            
            case H2FrameType::GOAWAY:
            case H2FrameType::RST_STREAM:
            case H2FrameType::PRIORITY:
            default:
                break;
        }
        
        offset += 9 + frame.length;
    }
    
    // Remove processed data — use pos-tracking to avoid O(n) erase when possible
    if (offset > 0) {
        if (offset >= len) {
            curcon.RecvData.clear();
        } else {
            curcon.RecvData.erase(curcon.RecvData.begin(), curcon.RecvData.begin() + offset);
        }
    }
}

class MyServer : public event {
  public:
    MyServer(std::vector<netplus::socket*> serversockets, int timeout=1000)
          : event(serversockets, timeout) {
    }
    
    void RequestEvent(con &curcon, const int tid, ULONG_PTR args) override {
        // Get the SNI hostname and ALPN from the SSL socket
        netplus::ssl* sslsock = dynamic_cast<netplus::ssl*>(curcon.slots[0].csock.get());
        std::string hostname = "unknown";
        std::string alpn = "";
        
        if (sslsock) {
            hostname = sslsock->getRequestedHostname();
            // Get ALPN from TLS layer
            alpn = sslsock->getTls().selected_alpn;
        }
        
        // Check if this is HTTP/2
        if (alpn == "h2") {
            // HTTP/2 protocol
            processH2Frames(curcon, sslsock, hostname);
            return;
        }
        
        // HTTP/1.1 fallback
        std::string body;
        if (hostname == "test1.localhost") {
            body = "Hello from test1.localhost (HTTPS)\n";
        } else if (hostname == "test2.localhost") {
            body = "Hello from test2.localhost (HTTPS)\n";
        } else {
            body = "Hello world (HTTPS)\n";
        }
        
        std::string resp;
        resp += "HTTP/1.1 200 OK\r\n";
        resp += "Content-Type: text/plain\r\n";
        resp += "Content-Length: " + std::to_string(body.size()) + "\r\n";
        resp += "Connection: close\r\n";
        resp += "\r\n";
        resp += body;

        curcon.SendData.append(resp.data(), resp.size());
    }

    void ResponseEvent(con &curcon, const int tid, ULONG_PTR args) override {
    }

    void ConnectEvent(con &curcon, const int tid, ULONG_PTR args) override {
        // Initialize HTTP/2 connection state
        h2_connections[curcon.slots[0].csock.get()] = H2Connection{};
    }

    void DisconnectEvent(con &curcon, const int tid, ULONG_PTR args) override {
        // Clean up HTTP/2 state
        h2_connections.erase(curcon.slots[0].csock.get());
    }

    void CreateConnection(std::shared_ptr<con> &res) override {
        res = std::make_shared<con>(this);
    }
};

int main() {
    std::cout << "[START]" << std::endl;
    std::cerr << "[STDERR]" << std::endl;
    
    try {
        std::cout << "=== HTTPS Test Starting ===" << std::endl;
        std::cout.flush();
        
        // Load first certificate
        x509cert cert;
        if (!cert.loadFromBuffer(test_cert_der)) {
             std::cerr << "Failed to load cert from memory!" << std::endl;
             std::cerr.flush();
             return 1;
        }
        std::cout << "Certificate 1 loaded successfully" << std::endl;
        std::cout.flush();

        // Load second certificate for test2.localhost
        x509cert cert2;
        if (!cert2.loadFromBuffer(test2_cert_der)) {
             std::cerr << "Failed to load cert2 from memory!" << std::endl;
             std::cerr.flush();
             return 1;
        }
        std::cout << "Certificate 2 loaded successfully" << std::endl;
        std::cout.flush();

        // Create SSL socket listening on 127.0.0.1:8443 with SNI support
        std::cout << "Creating SSL socket..." << std::endl;
        std::cout.flush();
        
        // ✅ BETTER STRUCT APPROACH: Pre-load RSA keys into certificate bundles
        std::map<std::string, netplus::ssl::CertificateBundle> certs;
        
        // Bundle 1 für test1.localhost, localhost, 127.0.0.1
        netplus::ssl::CertificateBundle bundle1;
        bundle1.cert = cert;
        bundle1.privateKeyDer = std::vector<uint8_t>(test_key_der.begin(), test_key_der.end());
        // Pre-load RSA key into bundle using vector-based constructor
        try {
            bundle1.rsa_key = netplus::rsa(bundle1.privateKeyDer);
        } catch (const std::exception& e) {
             std::cerr << "Failed to load RSA key 1: " << e.what() << std::endl;
             std::cerr.flush();
             return 1;
        }
        std::cout << "Bundle 1 RSA key loaded successfully" << std::endl;
        std::cout.flush();
        
        // Bundle 2 für test2.localhost mit anderem Zertifikat und privatem Schlüssel
        netplus::ssl::CertificateBundle bundle2;
        bundle2.cert = cert2;
        bundle2.privateKeyDer = std::vector<uint8_t>(test2_key_der.begin(), test2_key_der.end());
        // Pre-load RSA key into bundle using vector-based constructor
        try {
            bundle2.rsa_key = netplus::rsa(bundle2.privateKeyDer);
        } catch (const std::exception& e) {
             std::cerr << "Failed to load RSA key 2: " << e.what() << std::endl;
             std::cerr.flush();
             return 1;
        }
        std::cout << "Bundle 2 RSA key loaded successfully" << std::endl;
        std::cout.flush();
        
        // Registriere Test-Domains mit ihren jeweiligen Zertifikaten
        certs["test1.localhost"] = bundle1;
        certs["test2.localhost"] = bundle2;
        certs["localhost"] = bundle1;
        certs["127.0.0.1"] = bundle1;
        
        ssl serverSock(certs, "127.0.0.1", 8443, 1024, -1);
        std::cout << "SSL socket created successfully" << std::endl;
        std::cout.flush();

        std::cout << "Starting HTTPS server on 127.0.0.1:8443..." << std::endl;
        std::cout << "Socket FD before event: " << serverSock.fd() << std::endl;
        std::cout.flush();
        
        std::cout << "Creating MyServer instance (will call bind/listen)..." << std::endl;
        std::cout.flush();
        MyServer srv({&serverSock});
        std::cout << "Server object created (bind/listen done)" << std::endl;
        std::cout << "Socket FD after event: " << serverSock.fd() << std::endl;
        std::cout.flush();
        
        std::cout << "Starting event loop..." << std::endl;
        std::cout.flush();
        srv.runEventloop();
        std::cout << "Event loop exited" << std::endl;
        std::cout.flush();

    } catch (netplus::NetException& e) {
        std::cerr << "CAUGHT NetException: " << e.what() << std::endl;
        std::cerr.flush();
        return 1;
    } catch (std::exception& e) {
        std::cerr << "CAUGHT std::exception: " << e.what() << std::endl;
        std::cerr.flush();
        return 1;
    } catch (...) {
        std::cerr << "CAUGHT unknown exception!" << std::endl;
        std::cerr.flush();
        return 1;
    }
    return 0;
}
