/*******************************************************************************
* Copyright (c) 2014, Jan Koester jan.koester@gmx.net
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
*      notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
*      notice, this list of conditions and the following disclaimer in the
*      documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
*      names of its contributors may be used to endorse or promote products
*      derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#pragma once

#include <array>
#include <iostream>
#include <atomic>
#include <mutex>
#include <string>
#include <memory>
#include <deque>
#include <map>
#include <set>
#include <cstring>
#include <algorithm>
#include <functional>

#include "config.h"
#include "crypto/aes.h"
#include "crypto/x509.h"
#include "crypto/sha.h"
#include "crypto/tls.h"

#ifndef Windows
	typedef int SOCKET;
	typedef unsigned long ULONG_PTR;
	#include <sys/socket.h>
	#include <sys/types.h>
	#include <netdb.h>
	#include <arpa/inet.h>
	#include <netinet/in.h>
	#include <sys/un.h>
	#include <unistd.h>
	#include <fcntl.h>
	#include <errno.h>
#else
	#define WIN32_LEAN_AND_MEAN
	#ifndef NOMINMAX
	#define NOMINMAX
	#endif
	#include <WinSock2.h>
	#include <ws2tcpip.h>
	#include <mswsock.h>
	#include <BaseTsd.h>
	typedef SSIZE_T ssize_t;
	#ifndef MSG_DONTWAIT
	#define MSG_DONTWAIT 0
	#endif
#endif

namespace netplus {

	enum sockettype { TCP = 0, UDP = 1, SSL = 2, QUIC = 3 };

#ifdef Windows
	enum IO_OPERATION { OP_READ = 0, OP_WRITE = 1 , OP_ACCEPT = 2 };

	struct AcceptContext {
		WSAOVERLAPPED ov{};
		SOCKET acceptSock = INVALID_SOCKET;

		static constexpr size_t ADDR_BUF_SZ =
			2 * (sizeof(SOCKADDR_STORAGE) + 16);

		alignas(void*) char addrBuf[ADDR_BUF_SZ]{};
	};
#endif

	// ---------------------------------------------------
	// buffer bleibt wie gehabt (Windows + Linux getrennt)
	// ---------------------------------------------------
#ifdef Windows
	class buffer {
	public:
		buffer(size_t s = BLOCKSIZE) {
			size = s;
			data.buf = new char[size];
			data.len = (ULONG)size;
			std::memset(&overlapped, 0, sizeof(WSAOVERLAPPED));
			operation = OP_READ;
		}

		buffer(const char *pointer,size_t s) {
			size = s;
			data.buf = new char[size];
			data.len = (ULONG)size;
			std::copy(pointer, pointer + size, data.buf);
			std::memset(&overlapped, 0, sizeof(WSAOVERLAPPED));
			operation = OP_READ;
		}

		~buffer() { delete[] data.buf; }

		bool   ptr=false;
		size_t size;

		WSAOVERLAPPED overlapped;
		WSABUF        data;
		IO_OPERATION  operation;
		void* owner = nullptr;

		buffer(const buffer&) = delete;
		buffer& operator=(const buffer&) = delete;
	};
#else
	class buffer {
	public:
		buffer(size_t bsize) {
			data.buf = new char[bsize]();
			size = bsize;
		}

		buffer(const char* bptr, size_t bsize) {
			data.buf = nullptr;
			data.ptr = bptr;
			size = bsize;
			ptr = true;
		}

		~buffer() {
			if (!ptr)
				delete[] data.buf;
		}

		size_t size;
		bool ptr = false;
		union {
			char* buf;
			const char* ptr;
		} data;
	};
#endif

	// ===================================================
	// ✅ SOCKET BASE CLASS (refactored)
	// ===================================================
	class socket {
	public:
		socket();
		socket(socket& socket) = delete;
		socket& operator=(const socket& socket) = delete;

		virtual ~socket();

		void setFlag(int flag, int value);
		void setTimeout(int msec);

		virtual void accept(std::unique_ptr<socket>& csock,bool nonblock) = 0;
		virtual void handshake_after_accept(){};
		virtual void  handshake_after_connect(){};

#ifdef Windows
		virtual void accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) = 0;
		virtual void prime_read() = 0;
		std::unique_ptr<buffer> _ReadBuffer;
		std::unique_ptr<buffer> _SendBuffer;
#endif

		virtual void bind() = 0;
		virtual void listen() = 0;
		virtual void setNonBlock() = 0;
		virtual void setBlock()=0;

		virtual int getMaxconnections() = 0;

		virtual size_t sendData(buffer& data, int flags = 0) = 0;
		virtual size_t recvData(buffer& data, int flags = 0) = 0;

		// Send raw transport bytes (bypasses TLS encryption for handshake traffic)
		// Default implementation calls sendData; SSL overrides to use tcp::sendData
		// This makes TLS layer work with both TCP and UDP transports
		virtual size_t sendRaw(buffer& data, int flags = 0) { return sendData(data, flags); }

		// Read raw transport data and buffer it (for SSL/TLS pre-decryption)
		// Returns bytes read. For plain TCP, this is a no-op (data is read
		// directly in recvData). For SSL, reads raw TCP into internal buffer
		// for later decryption.
		virtual size_t prime_recv(buffer& data, int flags = 0) { (void)data; (void)flags; return 0; }

		virtual bool getHandshakeDone() { return true; }
		virtual bool isClient() const { return false; }
		virtual bool hasBufferedData() const { return false; }
		virtual int  getSocketType() const { return _Type; }
		virtual bool hasPendingWrite() const { return _pendingIocpWrite.load(); }
		virtual void setPendingWrite(bool pending) { _pendingIocpWrite.store(pending); }

		// Push received data directly into buffer (for IOCP)
		virtual void pushReceivedData(const uint8_t* data, size_t len) { (void)data; (void)len; }

		virtual void connect(const std::string& addr, int port, bool nonblock = false) = 0;
		virtual void getAddress(std::string& addr) = 0;

		SOCKET fd();

		virtual socket& operator=(SOCKET sock) = 0;
		virtual void close();
		virtual void flush_out() = 0;

		// Stream callback (for QUIC stream data handling)
		using StreamCallback = std::function<void(socket*, uint64_t, const std::vector<uint8_t>&, bool)>;
		virtual void setStreamCallback(StreamCallback cb) { (void)cb; }

		// ===================================================
		// ✅ NEW ADDRESS STORAGE (Linux + Windows also usable)
		// ===================================================
		sockaddr_storage _Addr{};
		socklen_t        _AddrLen = sizeof(sockaddr_storage);

		// Old fields (keep for Windows compatibility or legacy code)
		ULONG_PTR        _SocketInfo = 0;
		size_t           _SocketInfoLen = 0;

		SOCKET           _Socket;
		int              _Type;
		ULONG_PTR        _Extension;

		bool _Bound    = false;
		bool _Listening = false;

	protected:
		std::atomic<bool> _pendingIocpWrite{false};
		// ✅ legacy helper still available
		void copyAddrInfo(ULONG_PTR* dest, ULONG_PTR src, size_t srclen);

		// ✅ NEW helper: store from addrinfo into sockaddr_storage
		void setAddrFromAI(const addrinfo* ai) {
			if (!ai || !ai->ai_addr || ai->ai_addrlen <= 0) return;
			std::memset(&_Addr, 0, sizeof(_Addr));
			_AddrLen = (socklen_t)std::min<size_t>(sizeof(_Addr), (size_t)ai->ai_addrlen);
			std::memcpy(&_Addr, ai->ai_addr, _AddrLen);
		}
	};

	// ===================================================
	// TCP / UDP / SSL remain same declarations
	// ===================================================
	class tcp : public socket {
	public:
		tcp();
		tcp(SOCKET sock);

		tcp(const netplus::tcp& ctcp) = delete;
		tcp& operator=(const tcp& socket) = delete;

		tcp(const std::string& uxsocket, int maxconnections, int sockopts);
		tcp(const std::string& addr, int port, int maxconnections, int sockopts);
		~tcp();
		void flush_out() override;

#ifdef Windows
		void accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock);
		void prime_read();
#endif

		void accept(std::unique_ptr<socket>& csock,bool nonblock) override;
		void bind() override;
		void listen() override;
		void setNonBlock() override;
		void setBlock() override;
		netplus::tcp& operator=(SOCKET sock) override;

		int getMaxconnections() override;

		size_t sendData(buffer& data, int flags = 0) override;
		size_t recvData(buffer& data, int flags = 0) override;

		void connect(const std::string& addr, int port, bool nonblock = false) override;

		void getAddress(std::string& addr) override;

	private:
		int         _Maxconnections;
		std::string _UxPath;
	};

	class udp : public socket {
	public:
		udp();
		udp(SOCKET sock);
		udp(const udp& cudp) = delete;

		udp(const std::string& uxsocket, int maxconnections,int sockopts);
		udp(const std::string& addr, int port, int maxconnections,int sockopts);
		~udp();
		void flush_out() override;

		void accept(std::unique_ptr<socket>& csock,bool nonblock) override;

#ifdef Windows
		void accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock);
		void prime_read();
#endif

		void bind() override;
		void listen() override;
		void setNonBlock() override;
		void setBlock() override;

		udp& operator=(SOCKET socket) override;
		int getMaxconnections() override;

		size_t sendData(buffer& data, int flags = 0) override;
		size_t recvData(buffer& data, int flags = 0) override;

		// Send to stored _Addr destination (for unconnected sockets)
		size_t sendTo(buffer& data, int flags = 0);

		void connect(const std::string& addr, int port, bool nonblock = false) override;
		void getAddress(std::string& addr) override;

	private:
		int         _Maxconnections;
		std::string _UxPath;
	};

	class ssl : public tcp {
	public:
		// Use tls::CertificateBundle for compatibility
		using CertificateBundle = netplus::tls::CertificateBundle;

		using tcp::operator=;

		ssl(const std::map<std::string, CertificateBundle>& certs);
		ssl(const std::map<std::string, CertificateBundle>& certs, int sock);

		virtual ~ssl() = default;

		ssl(const std::map<std::string, CertificateBundle>& certs, const std::string& addr, int port, int maxconnections, int sockopts);

		void accept(std::unique_ptr<socket>& csock, bool nonblock) override;
		void handshake_after_accept() override { _tls.handshake_after_accept(); }
		void handshake_after_connect() override { _tls.handshake_after_connect(); }
		void connect(const std::string& addr, int port, bool nonblock = false) override;
		void close() override;

		size_t sendData(buffer& data, int flags = 0) override;
		size_t recvData(buffer& data, int flags = 0) override;
		
		// Send raw bytes without TLS encryption (for TLS handshake traffic)
		size_t sendRaw(buffer& data, int flags = 0) override { return tcp::sendData(data, flags); }

		// Read raw TCP data and buffer it for TLS processing
		size_t prime_recv(buffer& data, int flags = 0) override {
			size_t rcv = tcp::recvData(data, flags);
			if (rcv > 0) {
				_tls.pushReceivedData((const uint8_t*)data.data.buf, rcv);
			}
			return rcv;
		}

#ifdef Windows
		void accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) override;
		void prime_read();
#endif

		bool hasPendingWrite() const override {
			return _tls.hasPendingWrite() || _pendingIocpWrite.load();
		}

		void setPendingWrite(bool pending) override { _pendingIocpWrite.store(pending); }

		bool getHandshakeDone() override { return _tls.getHandshakeDone(); }
		bool isClient() const override { return _tls.is_client; }

		virtual void queueRaw(const std::vector<uint8_t>& v) { _tls.queueRaw(v); }
		virtual void queueRaw(std::vector<uint8_t>&& v) { _tls.queueRaw(std::move(v)); }
		virtual void queueRaw(const uint8_t* p, size_t n) { _tls.queueRaw(p, n); }

		void flush_out() override { _tls.flush_out(); }

		std::vector<uint8_t> readTlsRecordAsync() { return _tls.readTlsRecordAsync(); }

		void resetTLS() { _tls.resetTLS(); }

		// SNI accessor - get the requested hostname from client
		const std::string& getRequestedHostname() const { return _tls.getRequestedHostname(); }

		// Check if there's buffered data waiting to be processed
		bool hasBufferedData() const override { return _tls.hasBufferedData(); }

		// Push received data directly into the buffer (called by IOCP)
		void pushReceivedData(const uint8_t* data, size_t len) override {
			_tls.pushReceivedData(data, len);
		}

		// ALPN selection callback: given client-offered protocols, return the one to use.
		using AlpnSelectCallback = std::function<std::string(const std::vector<std::string>&)>;

		// Framing callback: given the negotiated ALPN, HTTP/2 stream ID (0 for HTTP/1.1),
		// response body, and content-type, produce the fully framed response.
		using FramingCallback = std::function<std::string(const std::string& alpn,
		                                                  uint32_t stream_id,
		                                                  const std::string& body,
		                                                  const std::string& content_type)>;

		void setAlpnSelectCallback(AlpnSelectCallback cb) {
			_tls.alpn_select_cb = std::move(cb);
		}

		void setFramingCallback(FramingCallback cb) {
			_framing_cb = std::move(cb);
		}

		const FramingCallback& getFramingCallback() const { return _framing_cb; }

		// Get the ALPN protocol negotiated during TLS handshake
		const std::string& getSelectedAlpn() const { return _tls.selected_alpn; }

		// Access to TLS state for advanced operations
		tls& getTls() { return _tls; }
		const tls& getTls() const { return _tls; }

	private:
		ssl(std::shared_ptr<const std::map<std::string, CertificateBundle>> shared_certs);
		tls _tls;
		FramingCallback _framing_cb;
		std::atomic<bool> _pendingIocpWrite{false};

#ifdef Windows
		AcceptContext _acpt;
		std::array<char, 2 * (sizeof(SOCKADDR_STORAGE) + 16)> _acceptBuf{};
		buffer* _iocpBuf = nullptr;
#endif

		friend class poll;
		friend class event;
		friend class EventWorker;
		friend class EventWorkerArgs;
		friend class TestSSL;
	};

	// ===================================================
	// QUIC Protocol Implementation (RFC 9000)
	// UDP-based transport with TLS 1.3 encryption
	// ===================================================
	class quic : public udp {
	public:
		// QUIC version constants
		static constexpr uint32_t QUIC_VERSION_1 = 0x00000001;
		static constexpr uint32_t QUIC_VERSION_2 = 0x6b3343cf;

		// Packet types (long header)
		enum class PacketType : uint8_t {
			Initial    = 0x00,
			ZeroRTT    = 0x01,
			Handshake  = 0x02,
			Retry      = 0x03
		};

		// Frame types
		enum class FrameType : uint64_t {
			PADDING             = 0x00,
			PING                = 0x01,
			ACK                 = 0x02,
			ACK_ECN             = 0x03,
			RESET_STREAM        = 0x04,
			STOP_SENDING        = 0x05,
			CRYPTO              = 0x06,
			NEW_TOKEN           = 0x07,
			STREAM              = 0x08, // 0x08-0x0f
			MAX_DATA            = 0x10,
			MAX_STREAM_DATA     = 0x11,
			MAX_STREAMS_BIDI    = 0x12,
			MAX_STREAMS_UNI     = 0x13,
			DATA_BLOCKED        = 0x14,
			STREAM_DATA_BLOCKED = 0x15,
			STREAMS_BLOCKED_BIDI= 0x16,
			STREAMS_BLOCKED_UNI = 0x17,
			NEW_CONNECTION_ID   = 0x18,
			RETIRE_CONNECTION_ID= 0x19,
			PATH_CHALLENGE      = 0x1a,
			PATH_RESPONSE       = 0x1b,
			CONNECTION_CLOSE    = 0x1c,
			CONNECTION_CLOSE_APP= 0x1d,
			HANDSHAKE_DONE      = 0x1e
		};

		// Connection state
		enum class ConnectionState {
			Idle,
			Handshaking,
			Connected,
			Draining,
			Closed
		};

		// Sent packet metadata for retransmission / loss detection
		struct SentPacket {
			uint64_t pn;             // packet number
			std::chrono::steady_clock::time_point sent_time;
			uint64_t stream_id;
			uint64_t stream_offset;
			size_t   data_length;
			bool     fin;            // carried FIN flag
			bool     ack_eliciting;  // true for STREAM frames
			std::vector<uint8_t> stream_data; // copy of payload for retransmit
		};

		// Stream state
		struct Stream {
			uint64_t stream_id;
			std::vector<uint8_t> recv_buffer;
			std::vector<uint8_t> send_buffer;
			uint64_t recv_offset = 0;
			uint64_t recv_contiguous = 0;  // bytes received contiguously from offset 0
			uint64_t recv_fin_offset = UINT64_MAX; // final stream size (set when FIN received)
			uint64_t send_offset = 0;
			uint64_t max_data_local = 16777216;   // 16MB
			uint64_t max_data_remote = 16777216;
			uint64_t max_data_local_committed = 16777216; // last advertised
			bool recv_fin = false;
			bool recv_dispatched = false;
			bool recv_complete = false; // all data up to FIN received
			bool send_fin = false;
			bool reset = false;
			// Track received byte ranges for gap detection
			std::set<std::pair<uint64_t,uint64_t>> recv_ranges;
		};

		// Constructors/Destructor
		quic();
		quic(SOCKET sock);
		quic(const std::string& addr, int port, int maxconnections, int sockopts);
		quic(const std::map<std::string, ssl::CertificateBundle>& certs,
		     const std::string& addr, int port, int maxconnections, int sockopts);
		~quic();

		// Bring base class operator= into scope to avoid hiding warning
		using udp::operator=;

		// Socket interface overrides
		void flush_out() override;
		void accept(std::unique_ptr<socket>& csock, bool nonblock) override;
#ifdef Windows
		void accept(LPFN_ACCEPTEX lpfnAcceptEx, std::unique_ptr<socket>& csock) override;
		void prime_read() override;
#endif
		void connect(const std::string& addr, int port, bool nonblock = false) override;
		size_t sendData(buffer& data, int flags = 0) override;
		size_t recvData(buffer& data, int flags = 0) override;
		void close() override;
		
		// Handshake handling (for event loop integration)
		void handshake_after_accept() override;
		void pushReceivedData(const uint8_t* data, size_t len) override;
		bool hasBufferedData() const override { return !_recv_buffer.empty(); }

		// Read raw UDP data and buffer it for QUIC processing
		size_t prime_recv(buffer& data, int flags = 0) override {
			size_t rcv = udp::recvData(data, flags);
			if (rcv > 0) {
				pushReceivedData((const uint8_t*)data.data.buf, rcv);
			}
			return rcv;
		}

		// QUIC-specific methods
		void setVersion(uint32_t version) { _version = version; }
		uint32_t getVersion() const { return _version; }

		ConnectionState getConnectionState() const { return _conn_state.load(); }

		// Stream management
		uint64_t openStream(bool bidirectional = true);
		void closeStream(uint64_t stream_id);
		size_t sendStreamData(uint64_t stream_id, const uint8_t* data, size_t len, bool fin = false);
		size_t recvStreamData(uint64_t stream_id, uint8_t* data, size_t max_len);
		bool hasStreamData(uint64_t stream_id) const;
		bool isStreamComplete(uint64_t stream_id) const;

		// Read raw UDP datagrams and process QUIC packets without consuming
		// stream data.  Use this in HTTP/3 client loops to pump the network
		// while reading specific streams via recvStreamData().
		void pumpNetwork(int flags = 0);

		// Connection ID management
		const std::vector<uint8_t>& getLocalConnectionId() const { return _local_cid; }
		const std::vector<uint8_t>& getRemoteConnectionId() const { return _remote_cid; }

		// Handshake status
		bool getHandshakeDone() override { return _handshake_complete; }

		// Certificate configuration (server mode)
		void setCertificates(const std::map<std::string, ssl::CertificateBundle>& certs);
		
		// SNI (Server Name Indication) support
		const std::string& getRequestedHostname() const { return _requested_hostname; }
		
		// Cipher suite information
		uint16_t getSelectedCipher() const { return _selected_cipher; }
		
		// Stream data callback (for HTTP/3 handling in application layer)
		// Override base class method - internally wraps to quic-specific callback
		void setStreamCallback(StreamCallback cb) override {
			_stream_callback = [cb](quic* q, uint64_t id, const std::vector<uint8_t>& data, bool fin) {
				cb(static_cast<socket*>(q), id, data, fin);
			};
		}
		
		// Send data on a stream (vector overload for application use)
		void sendStreamData(uint64_t stream_id, const std::vector<uint8_t>& data, bool fin);

		// Drain incoming UDP datagrams and process them (ACKs,
		// flow-control updates) so long-running stream callbacks
		// don't starve the QUIC connection.
		void pumpIncoming();

	private:
		// Packet processing
		void processIncomingPacket(const uint8_t* data, size_t len);
		void processLongHeaderPacket(const uint8_t* data, size_t len);
		void processShortHeaderPacket(const uint8_t* data, size_t len);
		void processInitialPacket(const uint8_t* data, size_t len);
		void processHandshakePacket(const uint8_t* data, size_t len);
		void processApplicationPacket(const uint8_t* data, size_t len);

		// Frame processing
		void processFrame(const uint8_t* data, size_t len, size_t& offset);
		void processCryptoFrame(const uint8_t* data, size_t len, size_t& offset);
		void processStreamFrame(const uint8_t* data, size_t len, size_t& offset);
		void processAckFrame(const uint8_t* data, size_t len, size_t& offset);

		// Loss detection and retransmission
		void checkLossAndRetransmit();
		void recordSentPacket(uint64_t pn, uint64_t stream_id, uint64_t stream_offset,
		                      const uint8_t* data, size_t len, bool fin);

		// Packet building
		std::vector<uint8_t> buildInitialPacket(const std::vector<uint8_t>& payload);
		std::vector<uint8_t> buildHandshakePacket(const std::vector<uint8_t>& payload);
		std::vector<uint8_t> buildShortHeaderPacket(const std::vector<uint8_t>& payload);

		// Frame building
		std::vector<uint8_t> buildCryptoFrame(const std::vector<uint8_t>& data, uint64_t offset);
		std::vector<uint8_t> buildStreamFrame(uint64_t stream_id, const std::vector<uint8_t>& data,
		                                      uint64_t offset, bool fin);
		std::vector<uint8_t> buildAckFrame(uint64_t largest_ack, uint64_t ack_delay,
		                                    const std::set<std::pair<uint64_t,uint64_t>>& recv_ranges);
		std::vector<uint8_t> buildConnectionCloseFrame(uint64_t error_code, const std::string& reason);

		// PN range tracking helper
		static void trackRecvPN(uint64_t pn, std::set<std::pair<uint64_t,uint64_t>>& ranges);

		// Crypto operations (TLS 1.3 based)
		void deriveInitialKeys(const std::vector<uint8_t>& dcid, bool is_server);
		void deriveHandshakeKeys(const std::vector<uint8_t>& shared_secret);
		void deriveApplicationKeys();

		std::vector<uint8_t> protectPacket(const std::vector<uint8_t>& header,
		                                   const std::vector<uint8_t>& payload,
		                                   uint64_t packet_number,
		                                   const uint8_t* key, const uint8_t* iv,
		                                   bool force_aes128 = false);
		bool unprotectPacket(const uint8_t* data, size_t len,
		                     std::vector<uint8_t>& header,
		                     std::vector<uint8_t>& payload,
		                     uint64_t& packet_number,
		                     const uint8_t* key, const uint8_t* iv,
		                     const uint8_t* hp_key, bool force_aes128 = false);

		// Header protection
		void applyHeaderProtection(std::vector<uint8_t>& packet, const uint8_t* hp_key, bool force_aes128 = false);
		void removeHeaderProtection(std::vector<uint8_t>& packet, const uint8_t* hp_key, bool force_aes128 = false);

		// Variable-length integer encoding/decoding (RFC 9000 Section 16)
		static size_t encodeVarInt(uint64_t value, uint8_t* out);
		static uint64_t decodeVarInt(const uint8_t* data, size_t& bytes_read);
		static size_t varIntSize(uint64_t value);

		// Connection ID generation
		void generateConnectionId(std::vector<uint8_t>& cid, size_t len = 8);
		
		// Send packet to peer (uses sendto for child connections)
		ssize_t sendPacket(const uint8_t* data, size_t len);

		// Retry token validation
		bool validateRetryToken(const std::vector<uint8_t>& token);

		// TLS 1.3 handshake integration
		void processClientHello(const std::vector<uint8_t>& data);
		void processServerHello(const std::vector<uint8_t>& data);
		void processEncryptedExtensions(const std::vector<uint8_t>& data);
		void parseTransportParameters(const uint8_t* data, size_t len);
		void sendClientHello();
		void sendServerHello();
		void sendServerHandshakeFlight();
		void generateECDHEKeyPair();
		void computeSharedSecret(const std::vector<uint8_t>& peer_public_key);
		std::vector<uint8_t> buildClientHello();
		std::vector<uint8_t> buildTransportParameters();
		void processTLSMessages();
		void processFinished(const std::vector<uint8_t>& msg);
		void sendClientFinished();
		void sendHandshakeDone();
		void sendHttp3ControlStreams();
		void completeHandshake();

		// Packet number handling
		uint64_t decodePacketNumber(uint32_t truncated_pn, size_t pn_len, uint64_t largest_pn);

		// State
		std::atomic<ConnectionState> _conn_state{ConnectionState::Idle};
		bool _is_server = false;
		bool _handshake_complete = false;
		bool _serverhello_sent = false;  // Track if ServerHello has been sent
		bool _h3_ctrl_sent = false;      // Track if HTTP/3 control streams have been sent
		uint32_t _version = QUIC_VERSION_1;

		// SNI (Server Name Indication)
		std::string _requested_hostname;
		std::string _selected_alpn;  // ALPN protocol selected (e.g., "h3")
		
		// Selected cipher suite (0x1301 = TLS_AES_128_GCM_SHA256, 0x1302 = TLS_AES_256_GCM_SHA384)
		uint16_t _selected_cipher = 0x1301;  // Default to AES-128-GCM
		// Signature algorithm support parsed from ClientHello
		bool _sigalg_rsa_pss_sha256 = true;  // Safe default if extension missing
		bool _sigalg_rsa_pss_sha384 = false;
		
		// Connection IDs
		std::vector<uint8_t> _local_cid;
		std::vector<uint8_t> _remote_cid;
		std::vector<uint8_t> _original_dcid;
		std::vector<uint8_t> _retry_scid;

		// Packet numbers (per packet number space)
		uint64_t _initial_pn_send = 0;
		uint64_t _initial_pn_recv = 0;
		uint64_t _handshake_pn_send = 0;
		uint64_t _handshake_pn_recv = 0;
		uint64_t _app_pn_send = 0;
		uint64_t _app_pn_recv = 0;

		// Received packet-number ranges (inclusive) for proper ACK encoding
		// Each pair is [lo, hi] representing contiguous received PNs.
		std::set<std::pair<uint64_t,uint64_t>> _initial_pn_recv_ranges;
		std::set<std::pair<uint64_t,uint64_t>> _handshake_pn_recv_ranges;
		std::set<std::pair<uint64_t,uint64_t>> _app_pn_recv_ranges;

		// Crypto keys (Initial keys - derived from connection ID, always AES-128)
		uint8_t _initial_key_client[16] = {0};
		uint8_t _initial_iv_client[12] = {0};
		uint8_t _initial_hp_client[16] = {0};
		uint8_t _initial_key_server[16] = {0};
		uint8_t _initial_iv_server[12] = {0};
		uint8_t _initial_hp_server[16] = {0};

		// Crypto keys (Handshake keys - size depends on cipher, max 32 for AES-256)
		uint8_t _hs_key_client[32] = {0};
		uint8_t _hs_iv_client[12] = {0};
		uint8_t _hs_hp_client[32] = {0};
		uint8_t _hs_key_server[32] = {0};
		uint8_t _hs_iv_server[12] = {0};
		uint8_t _hs_hp_server[32] = {0};

		// Crypto keys (Application keys - size depends on cipher, max 32 for AES-256)
		uint8_t _app_key_client[32] = {0};
		uint8_t _app_iv_client[12] = {0};
		uint8_t _app_hp_client[32] = {0};
		uint8_t _app_key_server[32] = {0};
		uint8_t _app_iv_server[12] = {0};
		uint8_t _app_hp_server[32] = {0};

		// TLS handshake traffic secrets (for computing Finished)
		std::vector<uint8_t> _c_hs_traffic_secret;
		std::vector<uint8_t> _s_hs_traffic_secret;

		// TLS handshake state
		std::vector<uint8_t> _tls_transcript;
		std::vector<uint8_t> _client_random;
		std::vector<uint8_t> _server_random;
		std::vector<uint8_t> _client_session_id;  // Session ID from ClientHello (for echo)
		std::vector<uint8_t> _ecdhe_private;
		std::vector<uint8_t> _ecdhe_public;
		std::vector<uint8_t> _ecdhe_shared;

		// Crypto buffers (per encryption level)
		std::vector<uint8_t> _crypto_recv_initial;
		std::vector<uint8_t> _crypto_recv_handshake;
		std::vector<uint8_t> _crypto_recv_app;
		std::vector<uint8_t> _crypto_send_initial;
		std::vector<uint8_t> _crypto_send_handshake;

		// CRYPTO stream reassembly - stores received ranges to handle out-of-order delivery
		struct CryptoRange {
			uint64_t offset;
			uint64_t length;
			std::vector<uint8_t> data;
		};
		std::vector<CryptoRange> _crypto_ranges_initial;
		std::vector<CryptoRange> _crypto_ranges_handshake;
		std::vector<CryptoRange> _crypto_ranges_app;
		uint64_t _crypto_offset_initial = 0;
		uint64_t _crypto_offset_handshake = 0;
		uint64_t _crypto_offset_app = 0;

		// Current encryption level for frame processing
		enum class EncryptionLevel { Initial, Handshake, Application };
		EncryptionLevel _current_enc_level = EncryptionLevel::Initial;

		// Streams
		std::map<uint64_t, Stream> _streams;
		uint64_t _next_stream_id_bidi = 0;
		uint64_t _next_stream_id_uni = 2;

		// Flow control (connection level)
		uint64_t _max_data_local = 67108864;    // 64MB default
		uint64_t _max_data_remote = 67108864;
		uint64_t _max_data_local_committed = 67108864; // last advertised
		uint64_t _data_sent = 0;
		uint64_t _data_recv = 0;

		// Stream count limits (MAX_STREAMS)
		uint64_t _max_streams_bidi_local = 10000000;   // we allow peer to open
		uint64_t _max_streams_bidi_remote = 10000000;  // peer allows us to open
		uint64_t _max_streams_uni_local = 10000000;
		uint64_t _max_streams_uni_remote = 10000000;
		uint64_t _streams_bidi_opened = 0;        // streams we opened
		uint64_t _streams_uni_opened = 0;
		uint64_t _peer_streams_bidi_opened = 0;   // streams peer opened
		uint64_t _peer_streams_uni_opened = 0;

		// Initial per-stream limits from peer transport parameters
		uint64_t _peer_initial_max_stream_data_bidi_local = 16777216;
		uint64_t _peer_initial_max_stream_data_bidi_remote = 16777216;
		uint64_t _peer_initial_max_stream_data_uni = 16777216;

		/// Send a MAX_STREAM_DATA frame for the given stream
		void sendMaxStreamData(uint64_t stream_id, uint64_t max_data);
		/// Send a MAX_DATA frame for the connection
		void sendMaxData(uint64_t max_data);
		/// Send a DATA_BLOCKED frame
		void sendDataBlocked(uint64_t limit);
		/// Send a STREAM_DATA_BLOCKED frame
		void sendStreamDataBlocked(uint64_t stream_id, uint64_t limit);
		/// Send an ACK frame for 1-RTT packets
		void sendAppAck();

		// Transport parameters
		uint64_t _max_idle_timeout = 30000;    // 30s
		uint64_t _max_udp_payload = 1350;      // Safe post-handshake default (Ethernet-safe)
		uint64_t _active_connection_id_limit = 2;

		// Receive/send queues
		std::vector<uint8_t> _recv_buffer;
		std::deque<std::vector<uint8_t>> _send_queue;

		// AES instances for each encryption level (GCM encrypt/decrypt)
		std::unique_ptr<aes> _aes_initial_send;
		std::unique_ptr<aes> _aes_initial_recv;
		std::unique_ptr<aes> _aes_hs_send;
		std::unique_ptr<aes> _aes_hs_recv;
		std::unique_ptr<aes> _aes_app_send;
		std::unique_ptr<aes> _aes_app_recv;

		// Cached AES instances for header protection (ECB, reused across packets)
		std::unique_ptr<aes> _aes_hp_initial_send;
		std::unique_ptr<aes> _aes_hp_initial_recv;
		std::unique_ptr<aes> _aes_hp_hs_send;
		std::unique_ptr<aes> _aes_hp_hs_recv;
		std::unique_ptr<aes> _aes_hp_app_send;
		std::unique_ptr<aes> _aes_hp_app_recv;

		// Certificate bundle for server mode
		std::map<std::string, ssl::CertificateBundle> _cert_map;
		ssl::CertificateBundle* _selected_cert = nullptr;
		
		// Connection tracking for server mode (maps DCID to child quic*)
		// _child_connections maps DCID to raw pointer for fast lookup
		// _child_owned_connections vector owns the lifetime via unique_ptr
		std::map<std::vector<uint8_t>, quic*> _child_connections;
		std::vector<std::unique_ptr<quic>> _child_owned_connections;  // Owns child connection lifetimes
		quic* _parent = nullptr;  // For child connections, pointer to parent listening socket

		// Stream data callback for application layer (quic-specific internal type)
		using QuicStreamCallback = std::function<void(quic*, uint64_t, const std::vector<uint8_t>&, bool)>;
		QuicStreamCallback _stream_callback;

		// Deferred stream dispatches — filled inside _quic_mutex,
		// invoked AFTER releasing it so callbacks can't block the
		// QUIC packet processing loop (prevents PING timeouts).
		struct PendingDispatch {
			uint64_t stream_id;
			std::vector<uint8_t> data;
			bool fin;
		};
		std::vector<PendingDispatch> _pending_dispatches;

		// Handshake timeout tracking
		// Detects when client sends incomplete ClientHello with large gaps (likely packet loss)
		std::chrono::steady_clock::time_point _handshake_start_time;
		bool _incomplete_ch_detected = false;
		bool _handshake_timeout_triggered = false;
		static const int HANDSHAKE_TIMEOUT_MS = 5000;  // 5 second timeout for handshake

		// PING keepalive: last activity timestamp & interval
		std::chrono::steady_clock::time_point _last_activity;
		static constexpr int KEEPALIVE_INTERVAL_MS = 15000;  // Send PING every 15s of inactivity

		// Handshake retransmission: store last handshake flight for retransmit
		std::vector<std::vector<uint8_t>> _hs_flight_packets;  // Stored for retransmit
		std::chrono::steady_clock::time_point _hs_last_send;
		int _hs_retransmit_count = 0;
		static constexpr int HS_RETRANSMIT_MAX = 5;
		static constexpr int HS_RETRANSMIT_INTERVAL_MS = 200;  // Initial retransmit timer

		// Application packet retransmission (loss detection + recovery)
		std::map<uint64_t, SentPacket> _sent_packets;  // pn → metadata
		uint64_t _largest_acked_pn = UINT64_MAX;       // largest packet number ACKed
		// RTT estimation (RFC 9002 §5)
		double _srtt = 0.0;           // smoothed RTT (seconds)
		double _rttvar = 0.0;         // RTT variance (seconds)
		double _rto = 0.5;            // retransmission timeout (seconds), initial 500ms
		bool   _rtt_initialized = false;
		// Loss detection
		static constexpr uint64_t PACKET_THRESHOLD = 3;     // RFC 9002 kPacketThreshold
		static constexpr double   TIME_THRESHOLD   = 9.0/8; // RFC 9002 kTimeThreshold

		// Flow control batching: avoid sending duplicate MAX_DATA/MAX_STREAM_DATA
		bool _pending_max_data = false;
		uint64_t _pending_max_data_value = 0;
		std::map<uint64_t, uint64_t> _pending_max_stream_data;

		// ACK tracking: need to send ACK for initial/handshake levels
		bool _need_initial_ack = false;
		bool _need_handshake_ack = false;

		// Persistent receive buffer for pumpNetwork() to avoid 65KB alloc/free per call
		buffer _pump_buf{65535};

		// Mutex for thread safety (recursive: musl returns EDEADLK on
		// double-lock by same thread, which happens when processFrame
		// callbacks re-enter locking functions like sendStreamData)
		mutable std::recursive_mutex _quic_mutex;

		// Children delegate to parent's mutex so there is only ONE lock
		// per QUIC listener — eliminates lock-ordering complexity.
		std::recursive_mutex& quic_mtx() const {
			return _parent ? _parent->_quic_mutex : _quic_mutex;
		}

		// Friend classes for event handling
		friend class poll;
		friend class event;
		friend class EventWorker;
		friend class EventWorkerArgs;
	};
} // namespace netplus
