#include <iostream>
#include <string>
#include <cassert>
#include <cstring>
#include <memory>
#include <vector>
#include <thread>
#include <atomic>
#include <map>
#include <mutex>

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

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

using namespace netplus;

// Global flag to signal shutdown
static std::atomic<bool> g_running(true);

// ============================================================================
// HTTP/2 Constants and Frame Helpers (RFC 9113)
// ============================================================================

static constexpr uint8_t H2_FRAME_DATA          = 0x00;
static constexpr uint8_t H2_FRAME_HEADERS       = 0x01;
static constexpr uint8_t H2_FRAME_SETTINGS      = 0x04;
static constexpr uint8_t H2_FRAME_PING          = 0x06;
static constexpr uint8_t H2_FRAME_GOAWAY        = 0x07;
static constexpr uint8_t H2_FRAME_WINDOW_UPDATE = 0x08;

static constexpr uint8_t H2_FLAG_END_STREAM     = 0x01;
static constexpr uint8_t H2_FLAG_ACK            = 0x01;
static constexpr uint8_t H2_FLAG_END_HEADERS    = 0x04;

static constexpr size_t  H2_FRAME_HEADER_LEN    = 9;
static const char        H2_CLIENT_PREFACE[]    = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
static constexpr size_t  H2_CLIENT_PREFACE_LEN  = 24;

// Build a raw HTTP/2 frame: 9-byte header + payload
static std::string h2BuildFrame(uint8_t type, uint8_t flags, uint32_t stream_id,
                                const std::string& payload) {
    std::string f(9 + payload.size(), '\0');
    uint32_t len = static_cast<uint32_t>(payload.size());
    f[0] = static_cast<char>((len >> 16) & 0xFF);
    f[1] = static_cast<char>((len >>  8) & 0xFF);
    f[2] = static_cast<char>( len        & 0xFF);
    f[3] = static_cast<char>(type);
    f[4] = static_cast<char>(flags);
    f[5] = static_cast<char>((stream_id >> 24) & 0x7F);
    f[6] = static_cast<char>((stream_id >> 16) & 0xFF);
    f[7] = static_cast<char>((stream_id >>  8) & 0xFF);
    f[8] = static_cast<char>( stream_id        & 0xFF);
    std::memcpy(&f[9], payload.data(), payload.size());
    return f;
}

// Server connection preface: SETTINGS with max_concurrent_streams=100
static std::string h2BuildSettings() {
    std::string p(6, '\0');
    p[0] = 0x00; p[1] = 0x03;  // SETTINGS_MAX_CONCURRENT_STREAMS
    p[2] = 0x00; p[3] = 0x00; p[4] = 0x00; p[5] = 0x64; // 100
    return h2BuildFrame(H2_FRAME_SETTINGS, 0, 0, p);
}

static std::string h2BuildSettingsAck() {
    return h2BuildFrame(H2_FRAME_SETTINGS, H2_FLAG_ACK, 0, "");
}

// Minimal HPACK encoder for response headers
// Uses HPACK static table (RFC 7541 Appendix A) where possible
static std::string h2BuildResponseHpack(const std::string& content_type, size_t content_length) {
    std::string h;

    // :status 200  -> indexed, static table index 8: 1000_1000 = 0x88
    h.push_back(static_cast<char>(0x88));

    // content-type -> literal with incremental indexing, name index 31
    //   01_011111 = 0x5f  (6-bit prefix, index 31 < 63 fits)
    h.push_back(static_cast<char>(0x5f));
    h.push_back(static_cast<char>(content_type.size())); // value length (no Huffman)
    h += content_type;

    // content-length -> literal without indexing, new name (0x00)
    h.push_back(0x00);
    const std::string cl_name = "content-length";
    h.push_back(static_cast<char>(cl_name.size()));
    h += cl_name;
    const std::string cl_val = std::to_string(content_length);
    h.push_back(static_cast<char>(cl_val.size()));
    h += cl_val;

    // alt-svc -> literal without indexing, new name
    // Tells browsers HTTP/3 is available on the same port via QUIC
    h.push_back(0x00);
    const std::string as_name = "alt-svc";
    h.push_back(static_cast<char>(as_name.size()));
    h += as_name;
    const std::string as_val = "h3=\":8443\"; ma=86400";
    h.push_back(static_cast<char>(as_val.size()));
    h += as_val;

    return h;
}

// Build full HTTP/2 HEADERS + DATA response frames for a given stream
static std::string h2BuildResponse(uint32_t stream_id,
                                    const std::string& body,
                                    const std::string& content_type) {
    std::string out;
    // HEADERS frame (END_HEADERS, no END_STREAM since DATA follows)
    std::string hpack = h2BuildResponseHpack(content_type, body.size());
    out += h2BuildFrame(H2_FRAME_HEADERS, H2_FLAG_END_HEADERS, stream_id, hpack);
    // DATA frame (END_STREAM)
    out += h2BuildFrame(H2_FRAME_DATA, H2_FLAG_END_STREAM, stream_id, body);
    return out;
}

// Forward declarations for SVG badge functions (defined below)
static std::string getSvgQuicSuccessBadge();
static std::string getSvgQuicErrorBadge();

// Check if HPACK/QPACK header data contains the path "/badge.svg"
// Handles both literal and Huffman-encoded representations.
static bool isBadgeSvgPath(const char* data, size_t len) {
    // Literal encoding
    if (std::string(data, len).find("/badge.svg") != std::string::npos)
        return true;
    // HPACK/QPACK Huffman encoding of "/badge.svg"
    // Captured from actual curl HPACK output: 62 31 c9 31 57 47 79 bf
    static const uint8_t huff[] = {0x62,0x31,0xC9,0x31,0x57,0x47,0x79,0xBF};
    for (size_t i = 0; i + sizeof(huff) <= len; ++i) {
        if (std::memcmp(data + i, huff, sizeof(huff)) == 0) return true;
    }
    return false;
}

// ============================================================================
// HTTP/2 per-connection state (needed for persistent connections)
// ============================================================================
struct H2ConnState {
    bool preface_sent = false;  // Have we sent our server connection preface?
};

static std::map<netplus::socket*, H2ConnState> g_h2_state;
static std::mutex g_h2_mutex;

// Parse HTTP/2 frames from raw data on a persistent connection.
// Only sends server connection preface (SETTINGS) on the first call.
// Returns bytes consumed from input, and appends output to 'out'.
static size_t h2ProcessFrames(H2ConnState& state,
                              const char* data, size_t len,
                              const netplus::ssl::FramingCallback& frameCb,
                              const std::string& body,
                              const std::string& content_type,
                              std::string& out) {
    size_t off = 0;

    // Server connection preface: MUST send own SETTINGS only ONCE per connection
    // (RFC 9113 Section 3.4)
    if (!state.preface_sent) {
        out += h2BuildSettings();
        state.preface_sent = true;
    }

    // Skip client connection preface magic if present at start of stream
    if (off + H2_CLIENT_PREFACE_LEN <= len &&
        std::memcmp(data + off, H2_CLIENT_PREFACE, H2_CLIENT_PREFACE_LEN) == 0) {
        off += H2_CLIENT_PREFACE_LEN;
    }

    while (off + H2_FRAME_HEADER_LEN <= len) {
        uint32_t flen = (static_cast<uint8_t>(data[off])   << 16) |
                        (static_cast<uint8_t>(data[off+1]) <<  8) |
                         static_cast<uint8_t>(data[off+2]);
        uint8_t  ftype  = static_cast<uint8_t>(data[off+3]);
        uint8_t  fflags = static_cast<uint8_t>(data[off+4]);
        uint32_t sid    = ((static_cast<uint8_t>(data[off+5]) & 0x7F) << 24) |
                          (static_cast<uint8_t>(data[off+6]) << 16) |
                          (static_cast<uint8_t>(data[off+7]) <<  8) |
                           static_cast<uint8_t>(data[off+8]);

        // Check if the full frame payload is available
        if (off + H2_FRAME_HEADER_LEN + flen > len) break; // incomplete frame, wait for more data

        off += H2_FRAME_HEADER_LEN;

        switch (ftype) {
        case H2_FRAME_SETTINGS:
            if (!(fflags & H2_FLAG_ACK)) {
                out += h2BuildSettingsAck();
                std::cerr << "[HTTP/2] <- SETTINGS (" << flen << " bytes), sent ACK" << std::endl;
            } else {
                std::cerr << "[HTTP/2] <- SETTINGS ACK" << std::endl;
            }
            break;
        case H2_FRAME_HEADERS: {
            std::cerr << "[HTTP/2] <- HEADERS stream=" << sid << std::endl;
            // Detect badge request by scanning HPACK header data
            bool is_badge = isBadgeSvgPath(data + off, flen);
            std::string resp_body = is_badge ? getSvgQuicErrorBadge() : body;
            std::string resp_ct = is_badge ? std::string("image/svg+xml") : content_type;
            if (frameCb) {
                out += frameCb("h2", sid, resp_body, resp_ct);
            } else {
                out += h2BuildResponse(sid, resp_body, resp_ct);
            }
            break;
        }
        case H2_FRAME_WINDOW_UPDATE:
            std::cerr << "[HTTP/2] <- WINDOW_UPDATE stream=" << sid << std::endl;
            break;
        case H2_FRAME_PING:
            // Respond with PING ACK (echo payload)
            out += h2BuildFrame(H2_FRAME_PING, H2_FLAG_ACK, 0,
                                std::string(data + off, flen));
            std::cerr << "[HTTP/2] <- PING, sent ACK" << std::endl;
            break;
        case H2_FRAME_GOAWAY:
            std::cerr << "[HTTP/2] <- GOAWAY" << std::endl;
            break;
        default:
            std::cerr << "[HTTP/2] <- frame type=" << (int)ftype
                      << " len=" << flen << std::endl;
            break;
        }
        off += flen;
    }
    return off; // bytes consumed
}

// ============================================================================
// SVG Status Badges
// ============================================================================

static std::string getSvgQuicSuccessBadge() {
    return R"SVG(
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="80" viewBox="0 0 300 80">
  <defs>
    <style>
      .badge-bg { fill: #22c55e; }
      .badge-text { font-family: 'Segoe UI', Arial, sans-serif; font-size: 32px; font-weight: bold; fill: white; text-anchor: middle; }
      .status-circle { fill: white; }
      .connection-label { font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px; fill: #15803d; text-anchor: middle; font-weight: 500; }
    </style>
  </defs>
  
  <!-- Success Badge (Green) -->
  <rect class="badge-bg" x="10" y="5" width="280" height="70" rx="12" />
  
  <!-- Status Circle -->
  <circle cx="50" cy="40" r="12" fill="#16a34a" />
  <circle class="status-circle" cx="50" cy="40" r="9" />
  
  <!-- Checkmark -->
  <g fill="none" stroke="#22c55e" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
    <polyline points="44,40 48,44 56,34" />
  </g>
  
  <!-- QUIC Text -->
  <text class="badge-text" x="150" y="52">QUIC</text>
  
  <!-- Label -->
  <text class="connection-label" x="150" y="72">Connected ✓</text>
</svg>
)SVG";
}

static std::string getSvgQuicErrorBadge() {
    return R"SVG(
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="80" viewBox="0 0 300 80">
  <defs>
    <style>
      .badge-bg-error { fill: #ef4444; }
      .badge-text { font-family: 'Segoe UI', Arial, sans-serif; font-size: 32px; font-weight: bold; fill: white; text-anchor: middle; }
      .status-circle { fill: white; }
      .connection-label { font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px; fill: #b91c1c; text-anchor: middle; font-weight: 500; }
    </style>
  </defs>
  
  <!-- Error Badge (Red) -->
  <rect class="badge-bg-error" x="10" y="5" width="280" height="70" rx="12" />
  
  <!-- Status Circle -->
  <circle cx="50" cy="40" r="12" fill="#b91c1c" />
  <circle class="status-circle" cx="50" cy="40" r="9" />
  
  <!-- Error X -->
  <g fill="none" stroke="#ef4444" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
    <line x1="44" y1="34" x2="56" y2="46" />
    <line x1="56" y1="34" x2="44" y2="46" />
  </g>
  
  <!-- QUIC Text -->
  <text class="badge-text" x="150" y="52">QUIC</text>
  
  <!-- Label -->
  <text class="connection-label" x="150" y="72">Failed ✗</text>
</svg>
)SVG";
}

// Variable-length integer encoding (RFC 9000)
static size_t encodeVarInt(uint64_t value, uint8_t* out) {
    if (value < 64) {
        out[0] = static_cast<uint8_t>(value);
        return 1;
    } else if (value < 16384) {
        out[0] = static_cast<uint8_t>(0x40 | (value >> 8));
        out[1] = static_cast<uint8_t>(value & 0xFF);
        return 2;
    } else if (value < 1073741824) {
        out[0] = static_cast<uint8_t>(0x80 | (value >> 24));
        out[1] = static_cast<uint8_t>((value >> 16) & 0xFF);
        out[2] = static_cast<uint8_t>((value >> 8) & 0xFF);
        out[3] = static_cast<uint8_t>(value & 0xFF);
        return 4;
    } else {
        out[0] = static_cast<uint8_t>(0xC0 | (value >> 56));
        out[1] = static_cast<uint8_t>((value >> 48) & 0xFF);
        out[2] = static_cast<uint8_t>((value >> 40) & 0xFF);
        out[3] = static_cast<uint8_t>((value >> 32) & 0xFF);
        out[4] = static_cast<uint8_t>((value >> 24) & 0xFF);
        out[5] = static_cast<uint8_t>((value >> 16) & 0xFF);
        out[6] = static_cast<uint8_t>((value >> 8) & 0xFF);
        out[7] = static_cast<uint8_t>(value & 0xFF);
        return 8;
    }
}

static uint64_t decodeVarInt(const uint8_t* data, size_t& bytes_read) {
    uint8_t prefix = data[0] >> 6;
    uint64_t value = data[0] & 0x3F;
    
    switch (prefix) {
        case 0: bytes_read = 1; break;
        case 1: bytes_read = 2; value = (value << 8) | data[1]; break;
        case 2: bytes_read = 4; 
            value = (value << 8) | data[1];
            value = (value << 8) | data[2];
            value = (value << 8) | data[3];
            break;
        case 3: bytes_read = 8;
            for (int i = 1; i < 8; ++i)
                value = (value << 8) | data[i];
            break;
    }
    return value;
}

// Build HTTP/3 response with minimal QPACK encoding
static std::vector<uint8_t> buildHttp3Response(uint16_t status_code, const std::string& body,
                                                const std::string& content_type = "text/plain") {
    std::vector<uint8_t> response;
    std::vector<uint8_t> headers_payload;
    
    // QPACK: Required Insert Count = 0, Base Delta = 0
    headers_payload.push_back(0x00);
    headers_payload.push_back(0x00);
    
    // :status header
    if (status_code == 200) {
        headers_payload.push_back(0xd9); // Static table index 25 (:status 200)
    } else {
        headers_payload.push_back(0x5f); // Literal with static name ref
        headers_payload.push_back(0x00); // Index 24 (:status)
        std::string status_str = std::to_string(status_code);
        headers_payload.push_back(static_cast<uint8_t>(status_str.size()));
        headers_payload.insert(headers_payload.end(), status_str.begin(), status_str.end());
    }
    
    // content-type (QPACK literal with static name reference)
    headers_payload.push_back(0x5f); // Literal with static name ref
    headers_payload.push_back(0x1d); // Index 29 (content-type)
    headers_payload.push_back(static_cast<uint8_t>(content_type.size()));
    headers_payload.insert(headers_payload.end(), content_type.begin(), content_type.end());
    
    // HEADERS frame (type 0x01)
    response.push_back(0x01);
    uint8_t len_buf[8];
    size_t len_bytes = encodeVarInt(headers_payload.size(), len_buf);
    response.insert(response.end(), len_buf, len_buf + len_bytes);
    response.insert(response.end(), headers_payload.begin(), headers_payload.end());
    
    // DATA frame (type 0x00)
    if (!body.empty()) {
        response.push_back(0x00);
        len_bytes = encodeVarInt(body.size(), len_buf);
        response.insert(response.end(), len_buf, len_buf + len_bytes);
        response.insert(response.end(), body.begin(), body.end());
    }
    
    return response;
}

// HTTP/3 stream callback - handles incoming requests
static void http3StreamCallback(netplus::socket* sock, uint64_t stream_id, 
                                 const std::vector<uint8_t>& data, bool fin) {
    quic* q = static_cast<quic*>(sock);
    
    std::cerr << "[HTTP/3] Stream callback: stream_id=" << stream_id 
              << ", data.size()=" << data.size() 
              << ", fin=" << fin << std::endl;
    
    if (data.empty()) {
        std::cerr << "[HTTP/3] Empty data, returning" << std::endl;
        return;
    }
    
    // Determine stream type
    // stream_id % 4 == 0: client-initiated bidirectional (requests)
    // stream_id % 4 == 2: client-initiated unidirectional (control/QPACK)
    bool is_unidirectional = (stream_id % 4 == 2) || (stream_id % 4 == 3);
    
    std::cerr << "[HTTP/3] Stream " << stream_id 
              << " (uni=" << is_unidirectional << "), size=" << data.size() 
              << ", fin=" << fin << std::endl;
    
    // For unidirectional streams, just log them (control/QPACK setup)
    if (is_unidirectional) {
        if (!data.empty()) {
            // First byte of unidirectional stream indicates type
            // 0x00 = Control stream, 0x01 = Push stream, 0x02 = QPACK encoder, 0x03 = QPACK decoder
            uint8_t stream_type = data[0];
            std::cerr << "[HTTP/3] Unidirectional stream type=" << (int)stream_type << std::endl;
        }
        return;  // Don't respond to control streams
    }
    
    // Only process bidirectional streams when FIN is received (complete request)
    if (!fin) {
        std::cerr << "[HTTP/3] Bidirectional stream waiting for FIN" << std::endl;
        return;
    }
    
    // Parse HTTP/3 frames on bidirectional request streams
    size_t offset = 0;
    while (offset < data.size()) {
        size_t bytes;
        uint64_t frame_type = decodeVarInt(&data[offset], bytes);
        offset += bytes;
        
        if (offset >= data.size()) break;
        
        uint64_t frame_length = decodeVarInt(&data[offset], bytes);
        offset += bytes;
        
        std::cerr << "[HTTP/3] Frame type=" << frame_type 
                  << ", length=" << frame_length << std::endl;
        
        if (frame_type == 0x01) { // HEADERS
            std::cerr << "[HTTP/3] Received HEADERS frame, sending response" << std::endl;
            
            // Detect request path by scanning QPACK-encoded header data
            bool is_badge = false;
            if (offset + frame_length <= data.size()) {
                is_badge = isBadgeSvgPath(
                    reinterpret_cast<const char*>(&data[offset]), frame_length);
            }
            
            std::vector<uint8_t> response;
            if (is_badge) {
                // Serve QUIC success badge SVG as external resource
                response = buildHttp3Response(200, getSvgQuicSuccessBadge(), "image/svg+xml");
            } else {
                // Serve HTML page with inline success badge (avoids sub-resource going over HTTP/2)
                std::string html = R"HTML(<!DOCTYPE html><html><head><title>QUIC HTTP/3 Server</title>
<style>body{font-family:sans-serif;margin:40px;background:#f5f5f5}
.c{max-width:600px;margin:0 auto;background:#fff;padding:30px;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,.1)}
h1{color:#22c55e}p{line-height:1.6}.info{background:#f0fdf4;border-left:4px solid #22c55e;padding:15px;margin:20px 0}
.badge{text-align:center;margin:20px 0}</style></head><body><div class="c">
<h1>QUIC HTTP/3 Connection Successful!</h1>
<div class="badge">)HTML";
                html += getSvgQuicSuccessBadge();
                html += R"HTML(</div>
<div class="info"><p><strong>Congratulations!</strong> You are using HTTP/3 with QUIC protocol!</p></div>
<p>Protocol: QUIC (UDP) | Version: HTTP/3 | Encryption: TLS 1.3</p>
</div></body></html>)HTML";
                response = buildHttp3Response(200, html, "text/html; charset=utf-8");
            }
            
            std::cerr << "[HTTP/3] Sending response: stream_id=" << stream_id 
                      << ", response.size()=" << response.size()
                      << ", badge=" << is_badge << std::endl;
            
            try {
                q->sendStreamData(stream_id, response, true);
                std::cerr << "[HTTP/3] Response sent successfully" << std::endl;
            } catch (const std::exception& e) {
                std::cerr << "[HTTP/3] Error sending response: " << e.what() << std::endl;
            }
        }
        
        // Skip frame data
        offset += frame_length;
    }
}

class QuicServer : public event {
  public:
    QuicServer(std::vector<netplus::socket*> serversockets, int timeout=1000)
          : event(serversockets, timeout) {
    }
    
    void RequestEvent(con &curcon, const int tid, ULONG_PTR args) override {
        // Check if this is a QUIC socket
        netplus::quic* quicsock = dynamic_cast<netplus::quic*>(curcon.slots[0].csock.get());
        
        if (quicsock) {
            // Build HTML response with embedded SVG badge
            std::string html = R"HTML(<!DOCTYPE html>
<html>
<head>
    <title>QUIC HTTP/3 Server</title>
    <style>
        body { font-family: 'Segoe UI', Arial, sans-serif; margin: 40px; background: #f5f5f5; }
        .container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
        h1 { color: #22c55e; margin-top: 0; }
        .badge-container { text-align: center; margin: 20px 0; }
        .info { background: #f0fdf4; border-left: 4px solid #22c55e; padding: 15px; margin: 20px 0; }
        .connection-details { font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 4px; font-size: 12px; }
        .success { color: #16a34a; font-weight: bold; }
    </style>
</head>
<body>
    <div class="container">
        <h1>🚀 QUIC HTTP/3 Connection Successful!</h1>
        
        <div class="badge-container">
)HTML";
            
            html += getSvgQuicSuccessBadge();
            html += R"HTML(
        </div>
        
        <div class="info">
            <p><strong>Congratulations!</strong> You are using HTTP/3 with QUIC protocol!</p>
        </div>
        
        <div class="connection-details">
            <strong>Connection Information:</strong><br>
            Protocol: QUIC (UDP-based)<br>
            Version: HTTP/3<br>
            Encryption: TLS 1.3<br>
            Status: <span class="success">✓ Connected</span>
        </div>
        
        <p style="margin-top: 30px; color: #666; font-size: 12px;">
            This server demonstrates RFC 9000 (QUIC), RFC 9001 (QUIC-TLS), and RFC 9114 (HTTP/3) compliance.
        </p>
    </div>
</body>
</html>
)HTML";
            
            // Build HTTP/3 response with QPACK headers
            std::vector<uint8_t> response = buildHttp3Response(200, html);
            
            curcon.SendData.append(reinterpret_cast<const char*>(response.data()), response.size());
        } else {
            // Not QUIC - return HTML with red error badge
            std::string html = R"HTML(<!DOCTYPE html>
<html>
<head>
    <title>QUIC HTTP/3 Not Available</title>
    <style>
        body { font-family: 'Segoe UI', Arial, sans-serif; margin: 40px; background: #fef2f2; }
        .container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); border-left: 4px solid #ef4444; }
        h1 { color: #dc2626; margin-top: 0; }
        .badge-container { text-align: center; margin: 20px 0; }
        .warning { background: #fee2e2; border-left: 4px solid #ef4444; padding: 15px; margin: 20px 0; }
        .connection-details { font-family: monospace; background: #f5f5f5; padding: 10px; border-radius: 4px; font-size: 12px; }
        .error { color: #b91c1c; font-weight: bold; }
    </style>
</head>
<body>
    <div class="container">
        <h1>⚠️ HTTP/1.1 Connection (Not HTTP/3)</h1>
        
        <div class="badge-container">
)HTML";
            
            html += getSvgQuicErrorBadge();
            
            html += R"HTML(
        </div>
        
        <div class="warning">
            <p><strong>Fallback Connection:</strong> You are using HTTPS/TLS 1.3 via TCP, not HTTP/3 with QUIC.</p>
        </div>
        
        <div class="connection-details">
            <strong>Connection Information:</strong><br>
            Protocol: <span class="error">TCP (not QUIC)</span><br>
            Version: <span class="error">HTTP/1.1 (not HTTP/3)</span><br>
            Encryption: TLS 1.3<br>
            Status: <span class="error">✗ Fallback Mode</span>
        </div>
        
        <p style="margin-top: 30px; color: #666; font-size: 12px;">
            To use HTTP/3 with QUIC, ensure your client supports HTTP/3 and connect via UDP port 8443.
            <br>Use curl with: <code>curl --http3-only https://localhost:8443/</code>
        </p>
    </div>
</body>
</html>
)HTML";
            
            // Build HTTP/2 response with error badge
            std::string hpack = h2BuildResponseHpack("text/html; charset=utf-8", html.size());
            std::string resp;
            resp += h2BuildSettings();
            resp += h2BuildFrame(H2_FRAME_HEADERS, H2_FLAG_END_HEADERS, 1, hpack);
            resp += h2BuildFrame(H2_FRAME_DATA, H2_FLAG_END_STREAM, 1, html);
            
            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 {
        netplus::quic* quicsock = dynamic_cast<netplus::quic*>(curcon.slots[0].csock.get());
        std::cout << "New QUIC connection from peer";
        if (quicsock) {
            std::cout << " (State: " << static_cast<int>(quicsock->getConnectionState()) << ")";
            std::cout << " (SNI: " << quicsock->getRequestedHostname() << ")";
            std::cout << " (Cipher: 0x" << std::hex << quicsock->getSelectedCipher() << std::dec << ")";
        }
        std::cout << std::endl;
    }

    void DisconnectEvent(con &curcon, const int tid, ULONG_PTR args) override {
        std::cout << "QUIC connection closed" << std::endl;
    }

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

// ============================================================================
// TCP/HTTPS Fallback Server - advertises HTTP/3 via Alt-Svc header
// ============================================================================

class HttpsFallbackServer : public event {
  public:
    HttpsFallbackServer(std::vector<netplus::socket*> serversockets, int timeout=1000)
          : event(serversockets, timeout) {
    }
    
    void RequestEvent(con &curcon, const int tid, ULONG_PTR args) override {
        netplus::ssl* sslsock = dynamic_cast<netplus::ssl*>(curcon.slots[0].csock.get());
        std::string hostname = "unknown";
        std::string alpn;
        if (sslsock) {
            hostname = sslsock->getRequestedHostname();
            alpn = sslsock->getSelectedAlpn();
        }

        // HTML body with inline error badge (avoids sub-resource protocol mismatch)
        // Includes one-time JS reload: after receiving Alt-Svc, the browser
        // should upgrade to QUIC on the second load.
        std::string body = R"HTML(<!DOCTYPE html>
<html><head><title>HTTPS Fallback</title>
<style>body{font-family:sans-serif;margin:40px;background:#f5f5f5}
.c{max-width:600px;margin:0 auto;background:#fff;padding:30px;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,.1)}
h1{color:#2563eb}p{line-height:1.6}.info{background:#eff6ff;border-left:4px solid #2563eb;padding:15px;margin:20px 0}
.badge{text-align:center;margin:20px 0}</style>
<script>if(!location.search.includes('noreload')){setTimeout(function(){location.replace(location.pathname+'?noreload')},1000)}</script>
</head>
<body><div class="c"><h1>HTTPS Fallback Server</h1>
<div class="badge">)HTML";
        body += getSvgQuicErrorBadge();
        body += R"HTML(</div>
<div class="info"><p>HTTP/3 is available on port 8443 via QUIC.</p>
<p>Your browser should automatically upgrade via Alt-Svc.</p></div>
<p>Protocol: HTTPS (TLS 1.3 over TCP)</p>
</div></body></html>)HTML";

        if (alpn == "h2") {
            // ---- HTTP/2: parse binary frames on persistent connection ----
            std::lock_guard<std::mutex> lk(g_h2_mutex);
            H2ConnState& h2 = g_h2_state[curcon.slots[0].csock.get()];
            std::string resp;
            size_t consumed = h2ProcessFrames(
                h2,
                curcon.RecvData.data(), curcon.RecvData.size(),
                sslsock ? sslsock->getFramingCallback() : nullptr,
                body, "text/html; charset=utf-8",
                resp);
            if (!resp.empty())
                curcon.SendData.append(resp.data(), resp.size());
            // Consume processed bytes from RecvData
            if (consumed > 0)
                curcon.RecvData.erase(curcon.RecvData.begin(), curcon.RecvData.begin() + consumed);
            std::cout << "[HTTPS Fallback] HTTP/2 response to " << hostname
                      << " (" << resp.size() << " bytes)" << std::endl;
        } else {
            // ---- HTTP/1.1 fallback (wrk, curl without --http2, etc.) ----
            // Check we have a complete HTTP/1.1 request (ends with \r\n\r\n)
            std::string req(curcon.RecvData.begin(), curcon.RecvData.end());
            size_t hdr_end = req.find("\r\n\r\n");
            if (hdr_end == std::string::npos)
                return; // incomplete request, wait for more data

            size_t consumed = hdr_end + 4;
            std::string resp;
            resp += "HTTP/1.1 200 OK\r\n";
            resp += "Content-Type: text/html; charset=utf-8\r\n";
            resp += "Content-Length: " + std::to_string(body.size()) + "\r\n";
            resp += "Alt-Svc: h3=\":8443\"; ma=86400\r\n";
            resp += "Connection: close\r\n";
            resp += "\r\n";
            resp += body;
            curcon.SendData.append(resp.data(), resp.size());
            curcon.RecvData.erase(curcon.RecvData.begin(), curcon.RecvData.begin() + consumed);
            std::cout << "[HTTPS Fallback] HTTP/1.1 response to " << hostname
                      << " (" << resp.size() << " bytes)" << std::endl;
        }
    }

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

    void ConnectEvent(con &curcon, const int tid, ULONG_PTR args) override {
        netplus::ssl* sslsock = dynamic_cast<netplus::ssl*>(curcon.slots[0].csock.get());
        std::string hostname = "unknown";
        if (sslsock) {
            hostname = sslsock->getRequestedHostname();
        }
        // Initialize per-connection HTTP/2 state
        {
            std::lock_guard<std::mutex> lk(g_h2_mutex);
            g_h2_state[curcon.slots[0].csock.get()] = H2ConnState{};
        }
        std::cout << "[HTTPS Fallback] New connection (SNI: " << hostname << ")" << std::endl;
    }

    void DisconnectEvent(con &curcon, const int tid, ULONG_PTR args) override {
        // Clean up per-connection state
        {
            std::lock_guard<std::mutex> lk(g_h2_mutex);
            g_h2_state.erase(curcon.slots[0].csock.get());
        }
        std::cout << "[HTTPS Fallback] Connection closed" << std::endl;
    }

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

// Thread function to run HTTPS fallback server
static void runHttpsFallbackServer(std::map<std::string, netplus::ssl::CertificateBundle>& certs) {
    try {
        std::cout << "[HTTPS Fallback] Starting on [::]:8443 (TCP, dual-stack)..." << std::endl;
        
        // Create SSL/TLS socket on port 8443 (TCP) - same port as QUIC (UDP)
        // Bind to :: for dual-stack IPv4+IPv6 (Firefox needs IPv6 support)
        ssl fallbackSock(certs, "::", 8443, 1024, -1);
        std::cout << "[HTTPS Fallback] Socket created on port 8443 (TCP, dual-stack)" << std::endl;
        
        // Configure ALPN: only accept h2 (HTTP/2)
        fallbackSock.setAlpnSelectCallback([](const std::vector<std::string>& client_protos) -> std::string {
            for (const auto& p : client_protos) {
                if (p == "h2") return p;
            }
            return "h2"; // Force h2 even if client didn't offer it
        });

        // Configure framing callback: always HTTP/2
        // stream_id == 0 → epoll post-handshake probe, return empty
        //                   (SETTINGS preface is sent by h2ProcessFrames)
        // stream_id >  0 → produce response for that HTTP/2 stream
        fallbackSock.setFramingCallback([](const std::string& alpn,
                                           uint32_t stream_id,
                                           const std::string& body,
                                           const std::string& content_type) -> std::string {
            if (stream_id == 0) {
                return "";
            }
            return h2BuildResponse(stream_id, body, content_type);
        });

        HttpsFallbackServer srv({&fallbackSock});
        std::cout << "[HTTPS Fallback] Server ready, entering event loop" << std::endl;
        
        srv.runEventloop();
        
    } catch (netplus::NetException& e) {
        std::cerr << "[HTTPS Fallback] NetException: " << e.what() << std::endl;
    } catch (std::exception& e) {
        std::cerr << "[HTTPS Fallback] Exception: " << e.what() << std::endl;
    }
}

int main() {
    std::cout << "[START]" << std::endl;
    std::cerr << "[STDERR]" << std::endl;
    
    try {
        std::cout << "=== QUIC 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 QUIC socket listening on 127.0.0.1:8443 with SNI support
        std::cout << "Creating QUIC socket..." << std::endl;
        std::cout.flush();
        
        // 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();
        
        // Set CA chain on the bundle (mkcert root CA for browser trust)
        bundle1.chain.push_back(std::vector<uint8_t>(MKCERT_ROOT_CA_DER,
            MKCERT_ROOT_CA_DER + MKCERT_ROOT_CA_DER_LEN));
        
        // Note: test2_key_der is in PKCS#8 format which the RSA parser doesn't support yet.
        // For now, use bundle1 for all domains.
        
        // Registriere Test-Domains mit ihren jeweiligen Zertifikaten
        certs["test1.localhost"] = bundle1;
        certs["test2.localhost"] = bundle1;  // Use bundle1 until PKCS#8 support is added
        certs["localhost"] = bundle1;
        certs["127.0.0.1"] = bundle1;
        certs["::1"] = bundle1;
        
        // Create QUIC server socket (UDP-based with TLS 1.3)
        // Bind to :: for dual-stack IPv4+IPv6 (Firefox needs IPv6 support)
        quic serverSock(certs, "::", 8443, 1024, -1);
        std::cout << "QUIC socket created successfully (cert chain per-SNI bundle)" << std::endl;
        std::cout.flush();
        
        // Set HTTP/3 stream callback
        serverSock.setStreamCallback(http3StreamCallback);

        // Start HTTPS fallback server in a separate thread
        std::cout << "Starting HTTPS fallback server thread..." << std::endl;
        std::thread fallbackThread(runHttpsFallbackServer, std::ref(certs));
        fallbackThread.detach();  // Let it run independently

        std::cout << "Starting QUIC server on [::]:8443 (dual-stack)..." << std::endl;
        std::cout << "Socket FD before event: " << serverSock.fd() << std::endl;
        std::cout.flush();
        
        std::cout << "Creating QuicServer instance (will call bind/listen)..." << std::endl;
        std::cout.flush();
        QuicServer 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;
}
