package dht

import (
	"crypto/rand"
	"crypto/sha1"
	"database/sql"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"log"
	"net"
	"sync"
	"time"

	"torrentGateway/internal/config"
	"github.com/anacrolix/torrent/bencode"
)

const (
	// Node ID length in bytes (160 bits for SHA-1)
	NodeIDLength = 20
	// K-bucket size
	BucketSize = 8
	// Number of buckets (160 bits = 160 buckets max)
	NumBuckets = 160
	// DHT protocol constants
	Alpha = 3 // Parallelism parameter
	
	// BEP-5 DHT protocol constants
	TransactionIDLength = 2
	TokenLength = 8
	MaxNodesReturn = 8
	
	// Message types
	QueryMessage    = "q"
	ResponseMessage = "r"
	ErrorMessage    = "e"
	
	// Storage limits
	MaxPeersPerTorrent = 500
	PeerExpiryMinutes  = 30
	TokenRotationMinutes = 5
	MaxResponsePeers   = 50
	MaxResponseNodes   = 8
	
	// Rate limiting
	MaxAnnouncesPerIPPerHour = 100
	MaxStoragePerIP = 50
	BlocklistDurationMinutes = 60
	
	// Peer verification
	PeerVerificationTimeout = 5 * time.Second
	MinReliabilityScore = 20
	MaxReliabilityScore = 100
)

// NodeID represents a 160-bit node identifier
type NodeID [NodeIDLength]byte

// NodeHealth represents the health state of a node
type NodeHealth int

const (
	NodeGood NodeHealth = iota     // Responded recently
	NodeQuestionable              // Old, needs verification
	NodeBad                       // Failed to respond, should be removed
)

// Node represents a DHT node
type Node struct {
	ID           NodeID
	Addr         *net.UDPAddr
	LastSeen     time.Time
	LastQuery    time.Time    // When we last queried this node
	Health       NodeHealth
	FailedQueries int         // Count of consecutive failed queries
	RTT          time.Duration // Round trip time for last successful query
}

// Bucket represents a k-bucket in the routing table
type Bucket struct {
	mu           sync.RWMutex
	nodes        []*Node
	lastChanged  time.Time    // When bucket was last modified
	lastRefresh  time.Time    // When bucket was last refreshed
}

// RoutingTable implements Kademlia routing table
type RoutingTable struct {
	mu      sync.RWMutex
	selfID  NodeID
	buckets [NumBuckets]*Bucket
}

// PendingQuery tracks outgoing queries
type PendingQuery struct {
	TransactionID string
	QueryType     string
	Target        *net.UDPAddr
	SentTime      time.Time
	Retries       int
	ResponseChan  chan *DHTMessage
}

// RateLimiter tracks announce rates per IP
type RateLimiter struct {
	mu       sync.RWMutex
	announces map[string][]time.Time // IP -> announce times
	storage   map[string]int         // IP -> peer count
	blocklist map[string]time.Time   // IP -> unblock time
}

// DHT represents a DHT node with enhanced peer storage
type DHT struct {
	config      *config.DHTConfig
	nodeID      NodeID
	routingTable *RoutingTable
	conn        *net.UDPConn
	storage     map[string][]byte // Simple in-memory storage for demo
	storageMu   sync.RWMutex
	peerStorage *PeerStorage      // Enhanced peer storage with database backing
	
	// Channels for communication
	stopCh   chan struct{}
	announceQueue chan AnnounceRequest
	verificationQueue chan *PeerVerificationRequest
	
	// Transaction tracking
	pendingQueries map[string]*PendingQuery
	pendingMu      sync.RWMutex
	transactionCounter uint16
	
	// Token generation with rotation
	tokenSecret     []byte
	previousSecret  []byte
	lastSecretRotation time.Time
	tokenMu         sync.RWMutex
	
	// Rate limiting and security
	rateLimiter    *RateLimiter
	peerVerification bool // Whether to verify peers
	
	// Statistics
	stats Stats
	statsMu sync.RWMutex
}

// PeerVerificationRequest represents a peer verification request
type PeerVerificationRequest struct {
	InfoHash string
	Peer     *Peer
	Retries  int
}

// Stats tracks DHT statistics
type Stats struct {
	PacketsSent     int64
	PacketsReceived int64
	NodesInTable    int
	StoredItems     int
}

// AnnounceRequest represents a request to announce a torrent
type AnnounceRequest struct {
	InfoHash string
	Port     int
}

// Peer represents a BitTorrent peer with enhanced metadata
type Peer struct {
	IP            net.IP
	Port          int
	PeerID        string    // Peer ID if available
	Added         time.Time // When this peer was added
	LastAnnounced time.Time // When this peer last announced
	Source        string    // How we got this peer ("announce", "dht", etc.)
	ReliabilityScore int    // Reliability score (0-100)
	AnnounceCount    int    // Number of successful announces
	Verified         bool   // Whether peer has been verified
	LastVerified     time.Time // When peer was last verified
}

// PeerList holds peers for a specific infohash
type PeerList struct {
	mu    sync.RWMutex
	peers map[string]*Peer // Key is "IP:Port"
}

// DatabaseInterface defines the database operations for peer storage
type DatabaseInterface interface {
	StorePeer(infohash, ip string, port int, peerID string, lastAnnounced time.Time) error
	GetPeersForTorrent(infohash string) ([]*StoredPeer, error)
	CleanExpiredPeers() error
	GetPeerCount(infohash string) (int, error)
	UpdatePeerReliability(infohash, ip string, port int, reliabilityScore int, verified bool, lastVerified time.Time) error
}

// StoredPeer represents a peer stored in the database
type StoredPeer struct {
	InfoHash         string
	IP               string
	Port             int
	PeerID           string
	LastAnnounced    time.Time
	ReliabilityScore int
	AnnounceCount    int
	Verified         bool
	LastVerified     time.Time
}

// PeerStorage manages peer storage with expiration and database backing
type PeerStorage struct {
	mu        sync.RWMutex
	infohashes map[string]*PeerList // Key is infohash (in-memory cache)
	db        DatabaseInterface     // Database backing
	stats     PeerStorageStats
}

// PeerStorageStats tracks storage statistics
type PeerStorageStats struct {
	TotalPeers     int64
	TotalTorrents  int64
	CacheHits      int64
	CacheMisses    int64
	DatabaseWrites int64
	DatabaseReads  int64
}

// DHT message types

// DHTMessage represents the base structure of a DHT message
type DHTMessage struct {
	TransactionID string                 `bencode:"t"`
	MessageType   string                 `bencode:"y"`
	QueryType     string                 `bencode:"q,omitempty"`
	Response      map[string]interface{} `bencode:"r,omitempty"`
	Arguments     map[string]interface{} `bencode:"a,omitempty"`
	Error         []interface{}          `bencode:"e,omitempty"`
}

// PingQuery represents a ping query
type PingQuery struct {
	ID string `bencode:"id"`
}

// PingResponse represents a ping response
type PingResponse struct {
	ID string `bencode:"id"`
}

// FindNodeQuery represents a find_node query
type FindNodeQuery struct {
	ID     string `bencode:"id"`
	Target string `bencode:"target"`
}

// FindNodeResponse represents a find_node response
type FindNodeResponse struct {
	ID    string `bencode:"id"`
	Nodes string `bencode:"nodes"`
}

// GetPeersQuery represents a get_peers query
type GetPeersQuery struct {
	ID       string `bencode:"id"`
	InfoHash string `bencode:"info_hash"`
}

// GetPeersResponse represents a get_peers response
type GetPeersResponse struct {
	ID     string      `bencode:"id"`
	Token  string      `bencode:"token"`
	Values []string    `bencode:"values,omitempty"`
	Nodes  string      `bencode:"nodes,omitempty"`
}

// AnnouncePeerQuery represents an announce_peer query
type AnnouncePeerQuery struct {
	ID       string `bencode:"id"`
	InfoHash string `bencode:"info_hash"`
	Port     int    `bencode:"port"`
	Token    string `bencode:"token"`
}

// AnnouncePeerResponse represents an announce_peer response
type AnnouncePeerResponse struct {
	ID string `bencode:"id"`
}
const (
	MsgQuery    = "q"
	MsgResponse = "r"
	MsgError    = "e"
)

// NewDHT creates a new DHT node
func NewDHT(config *config.DHTConfig) (*DHT, error) {
	// Generate random node ID
	var nodeID NodeID
	if _, err := rand.Read(nodeID[:]); err != nil {
		return nil, fmt.Errorf("failed to generate node ID: %w", err)
	}

	// Generate token secret
	tokenSecret := make([]byte, 32)
	if _, err := rand.Read(tokenSecret); err != nil {
		return nil, fmt.Errorf("failed to generate token secret: %w", err)
	}

	dht := &DHT{
		config:            config,
		nodeID:            nodeID,
		routingTable:      NewRoutingTable(nodeID),
		storage:           make(map[string][]byte),
		stopCh:            make(chan struct{}),
		announceQueue:     make(chan AnnounceRequest, 100),
		verificationQueue: make(chan *PeerVerificationRequest, 100),
		pendingQueries:    make(map[string]*PendingQuery),
		tokenSecret:       tokenSecret,
		previousSecret:    make([]byte, 32), // Initialize empty previous secret
		lastSecretRotation: time.Now(),
		peerStorage:       NewPeerStorage(nil), // Database will be set later
		rateLimiter:       NewRateLimiter(),
		peerVerification:  true, // Enable peer verification by default
	}

	return dht, nil
}

// NewRateLimiter creates a new rate limiter
func NewRateLimiter() *RateLimiter {
	return &RateLimiter{
		announces: make(map[string][]time.Time),
		storage:   make(map[string]int),
		blocklist: make(map[string]time.Time),
	}
}

// IsBlocked checks if an IP is currently blocked
func (rl *RateLimiter) IsBlocked(ip string) bool {
	rl.mu.RLock()
	defer rl.mu.RUnlock()
	
	if unblockTime, exists := rl.blocklist[ip]; exists {
		if time.Now().Before(unblockTime) {
			return true
		}
		// Unblock expired entries
		rl.mu.RUnlock()
		rl.mu.Lock()
		delete(rl.blocklist, ip)
		rl.mu.Unlock()
		rl.mu.RLock()
	}
	return false
}

// CheckAnnounceRate checks if an IP can announce (not rate limited)
func (rl *RateLimiter) CheckAnnounceRate(ip string) bool {
	rl.mu.Lock()
	defer rl.mu.Unlock()
	
	now := time.Now()
	cutoff := now.Add(-time.Hour)
	
	// Clean old announces
	if announces, exists := rl.announces[ip]; exists {
		var recent []time.Time
		for _, t := range announces {
			if t.After(cutoff) {
				recent = append(recent, t)
			}
		}
		rl.announces[ip] = recent
		
		// Check rate limit
		if len(recent) >= MaxAnnouncesPerIPPerHour {
			// Block this IP
			rl.blocklist[ip] = now.Add(BlocklistDurationMinutes * time.Minute)
			log.Printf("DHT: Rate limited IP %s (%d announces in last hour)", ip, len(recent))
			return false
		}
	}
	
	// Record this announce
	rl.announces[ip] = append(rl.announces[ip], now)
	return true
}

// CheckStorageLimit checks if an IP can store more peers
func (rl *RateLimiter) CheckStorageLimit(ip string) bool {
	rl.mu.RLock()
	defer rl.mu.RUnlock()
	
	if count, exists := rl.storage[ip]; exists {
		return count < MaxStoragePerIP
	}
	return true
}

// IncrementStorage increments the storage count for an IP
func (rl *RateLimiter) IncrementStorage(ip string) {
	rl.mu.Lock()
	defer rl.mu.Unlock()
	rl.storage[ip]++
}

// NewPeerStorage creates a new peer storage with optional database backing
func NewPeerStorage(db DatabaseInterface) *PeerStorage {
	return &PeerStorage{
		infohashes: make(map[string]*PeerList),
		db:         db,
		stats:      PeerStorageStats{},
	}
}

// validateTokenForTimeWindow validates a token for a specific time window and secret
func (d *DHT) validateTokenForTimeWindow(token string, addr *net.UDPAddr, timeWindow int64, secret []byte) bool {
	h := sha1.New()
	h.Write(secret)
	h.Write(addr.IP)
	binary.Write(h, binary.BigEndian, uint16(addr.Port))
	binary.Write(h, binary.BigEndian, timeWindow)
	expected := fmt.Sprintf("%x", h.Sum(nil)[:TokenLength])
	return token == expected
}

// rotateTokenSecretIfNeeded rotates the token secret every 5 minutes
func (d *DHT) rotateTokenSecretIfNeeded() {
	if time.Since(d.lastSecretRotation) >= TokenRotationMinutes*time.Minute {
		// Move current secret to previous
		copy(d.previousSecret, d.tokenSecret)
		
		// Generate new secret
		if _, err := rand.Read(d.tokenSecret); err != nil {
			log.Printf("DHT: Failed to rotate token secret: %v", err)
			return
		}
		
		d.lastSecretRotation = time.Now()
		log.Printf("DHT: Token secret rotated")
	}
}

// SetDatabase sets the database interface for peer storage
func (d *DHT) SetDatabase(db DatabaseInterface) {
	d.peerStorage.db = db
	log.Printf("DHT: Database interface configured")
}

// AddPeer adds a peer to the storage with database persistence
func (ps *PeerStorage) AddPeer(infohash string, peer *Peer) error {
	ps.mu.Lock()
	defer ps.mu.Unlock()
	
	// Check if we're at the limit for this torrent
	if ps.db != nil {
		count, err := ps.db.GetPeerCount(infohash)
		if err == nil && count >= MaxPeersPerTorrent {
			log.Printf("DHT: Max peers reached for torrent %s (%d peers)", infohash[:8], count)
			return fmt.Errorf("max peers reached for torrent")
		}
	}
	
	// Update in-memory cache
	peerList, exists := ps.infohashes[infohash]
	if !exists {
		peerList = &PeerList{
			peers: make(map[string]*Peer),
		}
		ps.infohashes[infohash] = peerList
	}
	
	peerList.mu.Lock()
	key := fmt.Sprintf("%s:%d", peer.IP.String(), peer.Port)
	now := time.Now()
	peer.Added = now
	peer.LastAnnounced = now
	peerList.peers[key] = peer
	peerList.mu.Unlock()
	
	// Store in database if available
	if ps.db != nil {
		err := ps.db.StorePeer(infohash, peer.IP.String(), peer.Port, peer.PeerID, peer.LastAnnounced)
		if err != nil {
			log.Printf("DHT: Failed to store peer in database: %v", err)
			return err
		}
		ps.stats.DatabaseWrites++
	}
	
	ps.stats.TotalPeers++
	return nil
}

// GetPeers gets peers for an infohash with database fallback
func (ps *PeerStorage) GetPeers(infohash string) []*Peer {
	ps.mu.RLock()
	peerList, exists := ps.infohashes[infohash]
	ps.mu.RUnlock()
	
	// Try cache first
	if exists {
		peerList.mu.RLock()
		if len(peerList.peers) > 0 {
			var peers []*Peer
			for _, peer := range peerList.peers {
				// Filter expired peers
				if time.Since(peer.LastAnnounced) <= PeerExpiryMinutes*time.Minute {
					peers = append(peers, peer)
				}
			}
			peerList.mu.RUnlock()
			ps.stats.CacheHits++
			return peers
		}
		peerList.mu.RUnlock()
	}
	
	// Cache miss - try database
	if ps.db != nil {
		storedPeers, err := ps.db.GetPeersForTorrent(infohash)
		if err != nil {
			log.Printf("DHT: Failed to get peers from database: %v", err)
			return nil
		}
		
		// Convert stored peers to Peer objects
		var peers []*Peer
		for _, sp := range storedPeers {
			peer := &Peer{
				IP:            net.ParseIP(sp.IP),
				Port:          sp.Port,
				PeerID:        sp.PeerID,
				Added:         sp.LastAnnounced,
				LastAnnounced: sp.LastAnnounced,
				Source:        "database",
			}
			peers = append(peers, peer)
		}
		
		ps.stats.DatabaseReads++
		ps.stats.CacheMisses++
		return peers
	}
	
	ps.stats.CacheMisses++
	return nil
}

// CleanExpiredPeers removes peers older than configured expiry time
func (ps *PeerStorage) CleanExpiredPeers() {
	cutoff := time.Now().Add(-PeerExpiryMinutes * time.Minute)
	
	// Clean database first if available
	if ps.db != nil {
		if err := ps.db.CleanExpiredPeers(); err != nil {
			log.Printf("DHT: Failed to clean expired peers from database: %v", err)
		}
	}
	
	// Clean in-memory cache
	ps.mu.Lock()
	defer ps.mu.Unlock()
	
	expiredCount := 0
	for infohash, peerList := range ps.infohashes {
		peerList.mu.Lock()
		
		activePeers := make(map[string]*Peer)
		for key, peer := range peerList.peers {
			if peer.LastAnnounced.After(cutoff) {
				activePeers[key] = peer
			} else {
				expiredCount++
			}
		}
		
		peerList.peers = activePeers
		peerList.mu.Unlock()
		
		// Remove empty peer lists
		if len(activePeers) == 0 {
			delete(ps.infohashes, infohash)
		}
	}
	
	if expiredCount > 0 {
		log.Printf("DHT: Cleaned %d expired peers from cache", expiredCount)
		ps.stats.TotalPeers -= int64(expiredCount)
	}
}

// NewRoutingTable creates a new routing table
func NewRoutingTable(selfID NodeID) *RoutingTable {
	rt := &RoutingTable{
		selfID: selfID,
	}
	
	// Initialize buckets
	now := time.Now()
	for i := range rt.buckets {
		rt.buckets[i] = &Bucket{
			nodes:       make([]*Node, 0, BucketSize),
			lastChanged: now,
			lastRefresh: now,
		}
	}
	
	return rt
}

// Start starts the DHT node
func (d *DHT) Start() error {
	// Listen on UDP port
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", d.config.Port))
	if err != nil {
		return fmt.Errorf("failed to resolve UDP address: %w", err)
	}

	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		return fmt.Errorf("failed to listen on UDP: %w", err)
	}

	d.conn = conn
	log.Printf("DHT node started on port %d with ID %x", d.config.Port, d.nodeID)

	// Start goroutines
	go d.handlePackets()
	go d.bootstrap()
	go d.maintenance()
	go d.handleAnnouncements()
	go d.handlePeerVerification()

	return nil
}

// Stop stops the DHT node
func (d *DHT) Stop() error {
	close(d.stopCh)
	if d.conn != nil {
		return d.conn.Close()
	}
	return nil
}

// handlePackets handles incoming UDP packets
func (d *DHT) handlePackets() {
	buffer := make([]byte, 2048)
	
	for {
		select {
		case <-d.stopCh:
			return
		default:
		}

		d.conn.SetReadDeadline(time.Now().Add(1 * time.Second))
		n, addr, err := d.conn.ReadFromUDP(buffer)
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
				continue
			}
			log.Printf("Error reading UDP packet: %v", err)
			continue
		}

		d.statsMu.Lock()
		d.stats.PacketsReceived++
		d.statsMu.Unlock()

		// Parse and handle DHT message
		go d.handlePacket(buffer[:n], addr)
	}
}

// handlePacket processes a single packet
func (d *DHT) handlePacket(data []byte, addr *net.UDPAddr) {
	var msg DHTMessage
	if err := bencode.Unmarshal(data, &msg); err != nil {
		log.Printf("Failed to unmarshal DHT message from %s: %v", addr, err)
		return
	}
	
	switch msg.MessageType {
	case QueryMessage:
		d.handleQuery(&msg, addr)
	case ResponseMessage:
		d.handleResponse(&msg, addr)
	case ErrorMessage:
		d.handleError(&msg, addr)
	default:
		log.Printf("Unknown message type '%s' from %s", msg.MessageType, addr)
	}
}

// handleQuery handles incoming query messages
func (d *DHT) handleQuery(msg *DHTMessage, addr *net.UDPAddr) {
	switch msg.QueryType {
	case "ping":
		d.handlePingQuery(msg, addr)
	case "find_node":
		d.handleFindNodeQuery(msg, addr)
	case "get_peers":
		d.handleGetPeersQuery(msg, addr)
	case "announce_peer":
		d.handleAnnouncePeerQuery(msg, addr)
	default:
		d.sendError(msg.TransactionID, addr, 204, "Method Unknown")
	}
}

// handleResponse handles incoming response messages
func (d *DHT) handleResponse(msg *DHTMessage, addr *net.UDPAddr) {
	d.pendingMu.RLock()
	pendingQuery, exists := d.pendingQueries[msg.TransactionID]
	d.pendingMu.RUnlock()
	
	if !exists {
		log.Printf("Received response for unknown transaction %s from %s", msg.TransactionID, addr)
		return
	}
	
	// Send response to waiting goroutine
	select {
	case pendingQuery.ResponseChan <- msg:
	default:
		// Channel might be closed
	}
	
	// Update routing table with responding node
	if nodeIDBytes, ok := msg.Response["id"].(string); ok {
		if len(nodeIDBytes) == NodeIDLength {
			var nodeID NodeID
			copy(nodeID[:], nodeIDBytes)
			node := &Node{
				ID:       nodeID,
				Addr:     addr,
				LastSeen: time.Now(),
				Health:   NodeGood,
			}
			d.routingTable.AddNode(node)
		}
	}
}

// handleError handles incoming error messages
func (d *DHT) handleError(msg *DHTMessage, addr *net.UDPAddr) {
	log.Printf("Received error from %s: %v", addr, msg.Error)
	
	d.pendingMu.RLock()
	pendingQuery, exists := d.pendingQueries[msg.TransactionID]
	d.pendingMu.RUnlock()
	
	if exists {
		select {
		case pendingQuery.ResponseChan <- msg:
		default:
			// Channel might be closed
		}
	}
}

// bootstrap connects to bootstrap nodes and builds routing table
func (d *DHT) bootstrap() {
	log.Printf("Starting DHT bootstrap with %d nodes", len(d.config.BootstrapNodes))
	
	// First ping all bootstrap nodes to establish initial connections
	var activeBootstrapNodes []*net.UDPAddr
	for _, bootstrapAddr := range d.config.BootstrapNodes {
		addr, err := net.ResolveUDPAddr("udp", bootstrapAddr)
		if err != nil {
			log.Printf("Failed to resolve bootstrap node %s: %v", bootstrapAddr, err)
			continue
		}

		// Ping bootstrap node
		if _, err := d.Ping(addr); err == nil {
			activeBootstrapNodes = append(activeBootstrapNodes, addr)
			log.Printf("Connected to bootstrap node: %s", addr)
		} else {
			log.Printf("Failed to ping bootstrap node %s: %v", addr, err)
		}
		time.Sleep(500 * time.Millisecond)
	}
	
	if len(activeBootstrapNodes) == 0 {
		log.Printf("Warning: No bootstrap nodes responded")
		return
	}
	
	// Perform iterative node lookup for our own ID to find neighbors
	log.Printf("Performing bootstrap lookup for own node ID")
	closestNodes := d.iterativeFindNode(d.nodeID, activeBootstrapNodes)
	
	// Add all discovered nodes to routing table
	for _, node := range closestNodes {
		d.routingTable.AddNode(node)
	}
	
	log.Printf("Bootstrap completed: discovered %d nodes", len(closestNodes))
}

// generateTransactionID generates a transaction ID for DHT messages
func (d *DHT) generateTransactionID() string {
	d.transactionCounter++
	tid := make([]byte, TransactionIDLength)
	binary.BigEndian.PutUint16(tid, d.transactionCounter)
	return string(tid)
}

// sendQuery sends a DHT query and tracks the transaction
func (d *DHT) sendQuery(queryType string, args map[string]interface{}, target *net.UDPAddr) (*DHTMessage, error) {
	tid := d.generateTransactionID()
	
	msg := map[string]interface{}{
		"t": tid,
		"y": QueryMessage,
		"q": queryType,
		"a": args,
	}
	
	data, err := bencode.Marshal(msg)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal query: %w", err)
	}
	
	// Create pending query
	responseChan := make(chan *DHTMessage, 1)
	pendingQuery := &PendingQuery{
		TransactionID: tid,
		QueryType:     queryType,
		Target:        target,
		SentTime:      time.Now(),
		Retries:       0,
		ResponseChan:  responseChan,
	}
	
	d.pendingMu.Lock()
	d.pendingQueries[tid] = pendingQuery
	d.pendingMu.Unlock()
	
	// Send the query
	_, err = d.conn.WriteToUDP(data, target)
	if err != nil {
		d.pendingMu.Lock()
		delete(d.pendingQueries, tid)
		d.pendingMu.Unlock()
		return nil, fmt.Errorf("failed to send query: %w", err)
	}
	
	d.statsMu.Lock()
	d.stats.PacketsSent++
	d.statsMu.Unlock()
	
	// Wait for response with timeout
	timer := time.NewTimer(5 * time.Second)
	defer timer.Stop()
	
	select {
	case response := <-responseChan:
		d.pendingMu.Lock()
		delete(d.pendingQueries, tid)
		d.pendingMu.Unlock()
		return response, nil
	case <-timer.C:
		d.pendingMu.Lock()
		delete(d.pendingQueries, tid)
		d.pendingMu.Unlock()
		return nil, fmt.Errorf("query timeout")
	case <-d.stopCh:
		d.pendingMu.Lock()
		delete(d.pendingQueries, tid)
		d.pendingMu.Unlock()
		return nil, fmt.Errorf("DHT stopping")
	}
}

// sendError sends an error response
func (d *DHT) sendError(transactionID string, addr *net.UDPAddr, code int, message string) {
	errorMsg := map[string]interface{}{
		"t": transactionID,
		"y": ErrorMessage,
		"e": []interface{}{code, message},
	}
	
	data, err := bencode.Marshal(errorMsg)
	if err != nil {
		log.Printf("Failed to marshal error message: %v", err)
		return
	}
	
	d.conn.WriteToUDP(data, addr)
}

// sendResponse sends a response message
func (d *DHT) sendResponse(transactionID string, addr *net.UDPAddr, response map[string]interface{}) {
	response["id"] = string(d.nodeID[:])
	
	respMsg := map[string]interface{}{
		"t": transactionID,
		"y": ResponseMessage,
		"r": response,
	}
	
	data, err := bencode.Marshal(respMsg)
	if err != nil {
		log.Printf("Failed to marshal response message: %v", err)
		return
	}
	
	d.conn.WriteToUDP(data, addr)
	d.statsMu.Lock()
	d.stats.PacketsSent++
	d.statsMu.Unlock()
}

// generateToken generates a secure token with rotation for announce_peer
func (d *DHT) generateToken(addr *net.UDPAddr) string {
	d.tokenMu.RLock()
	defer d.tokenMu.RUnlock()
	
	// Rotate secret if needed
	d.rotateTokenSecretIfNeeded()
	
	// Get current time window (5-minute windows)
	timeWindow := time.Now().Unix() / (TokenRotationMinutes * 60)
	
	h := sha1.New()
	h.Write(d.tokenSecret)
	h.Write(addr.IP)
	binary.Write(h, binary.BigEndian, uint16(addr.Port))
	binary.Write(h, binary.BigEndian, timeWindow)
	return fmt.Sprintf("%x", h.Sum(nil)[:TokenLength])
}

// validateToken validates a token for announce_peer (accepts current and previous time window)
func (d *DHT) validateToken(token string, addr *net.UDPAddr) bool {
	d.tokenMu.RLock()
	defer d.tokenMu.RUnlock()
	
	currentTime := time.Now().Unix() / (TokenRotationMinutes * 60)
	
	// Check current time window
	if d.validateTokenForTimeWindow(token, addr, currentTime, d.tokenSecret) {
		return true
	}
	
	// Check previous time window with current secret
	if d.validateTokenForTimeWindow(token, addr, currentTime-1, d.tokenSecret) {
		return true
	}
	
	// Check current time window with previous secret (if available)
	if len(d.previousSecret) > 0 {
		if d.validateTokenForTimeWindow(token, addr, currentTime, d.previousSecret) {
			return true
		}
		
		// Check previous time window with previous secret
		if d.validateTokenForTimeWindow(token, addr, currentTime-1, d.previousSecret) {
			return true
		}
	}
	
	return false
}

// handlePingQuery handles ping queries
func (d *DHT) handlePingQuery(msg *DHTMessage, addr *net.UDPAddr) {
	// Add querying node to routing table
	if args := msg.Arguments; args != nil {
		if nodeIDBytes, ok := args["id"].(string); ok && len(nodeIDBytes) == NodeIDLength {
			var nodeID NodeID
			copy(nodeID[:], nodeIDBytes)
			node := &Node{
				ID:       nodeID,
				Addr:     addr,
				LastSeen: time.Now(),
				Health:   NodeGood,
			}
			d.routingTable.AddNode(node)
		}
	}
	
	// Send pong response
	response := map[string]interface{}{}
	d.sendResponse(msg.TransactionID, addr, response)
}

// handleFindNodeQuery handles find_node queries
func (d *DHT) handleFindNodeQuery(msg *DHTMessage, addr *net.UDPAddr) {
	// Add querying node to routing table
	if args := msg.Arguments; args != nil {
		if nodeIDBytes, ok := args["id"].(string); ok && len(nodeIDBytes) == NodeIDLength {
			var nodeID NodeID
			copy(nodeID[:], nodeIDBytes)
			node := &Node{
				ID:       nodeID,
				Addr:     addr,
				LastSeen: time.Now(),
				Health:   NodeGood,
			}
			d.routingTable.AddNode(node)
		}
		
		// Get target from arguments
		if targetBytes, ok := args["target"].(string); ok && len(targetBytes) == NodeIDLength {
			var targetID NodeID
			copy(targetID[:], targetBytes)
			
			// Find closest nodes
			closestNodes := d.findClosestNodes(targetID, MaxNodesReturn)
			nodesBytes := d.encodeNodes(closestNodes)
			
			response := map[string]interface{}{
				"nodes": nodesBytes,
			}
			d.sendResponse(msg.TransactionID, addr, response)
			return
		}
	}
	
	d.sendError(msg.TransactionID, addr, 203, "Protocol Error")
}

// handleGetPeersQuery handles get_peers queries with enhanced peer selection and node inclusion
func (d *DHT) handleGetPeersQuery(msg *DHTMessage, addr *net.UDPAddr) {
	// Add querying node to routing table
	if args := msg.Arguments; args != nil {
		if nodeIDBytes, ok := args["id"].(string); ok && len(nodeIDBytes) == NodeIDLength {
			var nodeID NodeID
			copy(nodeID[:], nodeIDBytes)
			node := &Node{
				ID:       nodeID,
				Addr:     addr,
				LastSeen: time.Now(),
				Health:   NodeGood,
			}
			d.routingTable.AddNode(node)
		}
		
		// Get info_hash from arguments
		if infoHashBytes, ok := args["info_hash"].(string); ok {
			infoHash := fmt.Sprintf("%x", infoHashBytes)
			
			// Generate token for potential announce_peer
			token := d.generateToken(addr)
			
			// Get all peers for this torrent
			allPeers := d.peerStorage.GetPeers(infoHash)
			
			// Always find closest nodes for recursive search
			var targetID NodeID
			copy(targetID[:], infoHashBytes)
			closestNodes := d.findClosestNodes(targetID, MaxResponseNodes)
			nodesBytes := d.encodeNodes(closestNodes)
			
			// Prepare response
			response := map[string]interface{}{
				"token": token,
			}
			
			if len(allPeers) > 0 {
				// Select reliable peers preferentially
				reliablePeers := d.selectReliablePeers(allPeers, MaxResponsePeers)
				
				// Convert peers to compact format
				var values []string
				for _, peer := range reliablePeers {
					// Handle both IPv4 and IPv6
					if ip4 := peer.IP.To4(); ip4 != nil {
						// IPv4 format: 6 bytes (4 IP + 2 port)
						peerBytes := make([]byte, 6)
						copy(peerBytes[:4], ip4)
						binary.BigEndian.PutUint16(peerBytes[4:], uint16(peer.Port))
						values = append(values, string(peerBytes))
					} else if len(peer.IP) == 16 {
						// IPv6 format: 18 bytes (16 IP + 2 port)
						peerBytes := make([]byte, 18)
						copy(peerBytes[:16], peer.IP)
						binary.BigEndian.PutUint16(peerBytes[16:], uint16(peer.Port))
						values = append(values, string(peerBytes))
					}
				}
				
				if len(values) > 0 {
					response["values"] = values
					log.Printf("DHT: Returned %d peers for %s to %s", len(values), infoHash[:8], addr)
				}
			}
			
			// Always include closest nodes to help with recursive search
			if len(closestNodes) > 0 {
				response["nodes"] = nodesBytes
				log.Printf("DHT: Also returned %d nodes for %s to %s", len(closestNodes), infoHash[:8], addr)
			}
			
			// Send response (will contain peers and/or nodes plus token)
			d.sendResponse(msg.TransactionID, addr, response)
			return
		}
	}
	
	d.sendError(msg.TransactionID, addr, 203, "Protocol Error")
}

// min returns the smaller of two integers
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// handleAnnouncePeerQuery handles announce_peer queries with enhanced validation and storage
func (d *DHT) handleAnnouncePeerQuery(msg *DHTMessage, addr *net.UDPAddr) {
	ipStr := addr.IP.String()
	
	// Check if IP is blocked
	if d.rateLimiter.IsBlocked(ipStr) {
		log.Printf("DHT: Blocked IP %s attempted announce", ipStr)
		d.sendError(msg.TransactionID, addr, 201, "Generic Error")
		return
	}
	
	// Check announce rate limit
	if !d.rateLimiter.CheckAnnounceRate(ipStr) {
		log.Printf("DHT: Rate limited IP %s", ipStr)
		d.sendError(msg.TransactionID, addr, 201, "Generic Error")
		return
	}
	
	if args := msg.Arguments; args != nil {
		// Validate token (must be from our get_peers response)
		if token, ok := args["token"].(string); ok {
			if !d.validateToken(token, addr) {
				log.Printf("DHT: Invalid token from %s (token: %s)", addr, token[:min(len(token), 16)])
				d.sendError(msg.TransactionID, addr, 203, "Protocol Error")
				return
			}
		} else {
			log.Printf("DHT: Missing token from %s", addr)
			d.sendError(msg.TransactionID, addr, 203, "Protocol Error")
			return
		}
		
		// Extract peer ID if available
		var peerID string
		if nodeIDBytes, ok := args["id"].(string); ok && len(nodeIDBytes) == NodeIDLength {
			peerID = hex.EncodeToString([]byte(nodeIDBytes))
			
			// Add querying node to routing table
			var nodeID NodeID
			copy(nodeID[:], nodeIDBytes)
			node := &Node{
				ID:       nodeID,
				Addr:     addr,
				LastSeen: time.Now(),
				Health:   NodeGood,
			}
			d.routingTable.AddNode(node)
		}
		
		// Store peer info
		if infoHashBytes, ok := args["info_hash"].(string); ok {
			if port, ok := args["port"].(int); ok {
				infoHash := fmt.Sprintf("%x", infoHashBytes)
				
				// Check storage limit for this IP
				if !d.rateLimiter.CheckStorageLimit(ipStr) {
					log.Printf("DHT: Storage limit exceeded for IP %s", ipStr)
					d.sendError(msg.TransactionID, addr, 201, "Generic Error")
					return
				}
				
				// Create enhanced peer with metadata
				now := time.Now()
				peer := &Peer{
					IP:               addr.IP,
					Port:             port,
					PeerID:           peerID,
					Added:            now,
					LastAnnounced:    now,
					Source:           "announce",
					ReliabilityScore: 50, // Start with neutral score
					AnnounceCount:    1,
					Verified:         false,
				}
				
				// Store peer with error handling
				if err := d.peerStorage.AddPeer(infoHash, peer); err != nil {
					log.Printf("DHT: Failed to store peer %s:%d for %s: %v", addr.IP, port, infoHash[:8], err)
					// Still send success to avoid revealing internal errors
				} else {
					// Increment storage count for rate limiting
					d.rateLimiter.IncrementStorage(ipStr)
					
					// Queue peer for verification if enabled
					if d.peerVerification {
						select {
						case d.verificationQueue <- &PeerVerificationRequest{
							InfoHash: infoHash,
							Peer:     peer,
							Retries:  0,
						}:
						default:
							// Verification queue full, skip verification
						}
					}
				}
				
				log.Printf("DHT: Successfully stored peer %s:%d for infohash %s", addr.IP, port, infoHash[:8])
				
				// Send success response
				response := map[string]interface{}{}
				d.sendResponse(msg.TransactionID, addr, response)
				return
			}
		}
	}
	
	d.sendError(msg.TransactionID, addr, 203, "Protocol Error")
}

// findClosestNodes finds the closest nodes to a target ID (wrapper for RoutingTable method)
func (d *DHT) findClosestNodes(targetID NodeID, count int) []*Node {
	return d.routingTable.FindClosestNodes(targetID, count)
}

// compareNodeIDs compares two node IDs, returns -1 if a < b, 0 if a == b, 1 if a > b
func compareNodeIDs(a, b NodeID) int {
	for i := 0; i < NodeIDLength; i++ {
		if a[i] < b[i] {
			return -1
		}
		if a[i] > b[i] {
			return 1
		}
	}
	return 0
}

// encodeNodes encodes nodes in compact format
func (d *DHT) encodeNodes(nodes []*Node) string {
	var result []byte
	
	for _, node := range nodes {
		// Each node: 20 bytes node ID + 4 bytes IP + 2 bytes port
		result = append(result, node.ID[:]...)
		result = append(result, node.Addr.IP.To4()...)
		
		portBytes := make([]byte, 2)
		binary.BigEndian.PutUint16(portBytes, uint16(node.Addr.Port))
		result = append(result, portBytes...)
	}
	
	return string(result)
}

// sendPing sends a ping message to a node (wrapper for Ping operation)
func (d *DHT) sendPing(addr *net.UDPAddr) error {
	_, err := d.Ping(addr)
	return err
}

// maintenance performs periodic maintenance tasks
func (d *DHT) maintenance() {
	ticker := time.NewTicker(5 * time.Minute)
	defer ticker.Stop()

	for {
		select {
		case <-d.stopCh:
			return
		case <-ticker.C:
			d.performMaintenance()
		}
	}
}

// performMaintenance cleans up old nodes and refreshes buckets
func (d *DHT) performMaintenance() {
	// Rotate token secret if needed
	d.tokenMu.Lock()
	d.rotateTokenSecretIfNeeded()
	d.tokenMu.Unlock()
	
	// Perform health checks on all nodes
	d.routingTable.PerformHealthCheck(d)
	
	// Clean up nodes marked as bad
	d.routingTable.CleanupBadNodes()
	
	// Clean up expired peers
	d.peerStorage.CleanExpiredPeers()
	
	// Clean up unreliable peers
	d.CleanUnreliablePeers()
	
	// Refresh stale buckets
	d.routingTable.RefreshBuckets(d)
	
	// Update statistics
	totalNodes := 0
	goodNodes := 0
	questionableNodes := 0
	badNodes := 0
	
	d.routingTable.mu.RLock()
	for _, bucket := range d.routingTable.buckets {
		if bucket == nil {
			continue
		}
		
		bucket.mu.RLock()
		for _, node := range bucket.nodes {
			totalNodes++
			switch node.Health {
			case NodeGood:
				goodNodes++
			case NodeQuestionable:
				questionableNodes++
			case NodeBad:
				badNodes++
			}
		}
		bucket.mu.RUnlock()
	}
	d.routingTable.mu.RUnlock()

	// Get peer storage stats
	peerStats := d.peerStorage.GetStats()
	torrentCount := d.peerStorage.GetTorrentCount()
	verificationStats := d.GetVerificationStats()

	d.statsMu.Lock()
	d.stats.NodesInTable = totalNodes
	d.stats.StoredItems = int(peerStats.TotalPeers)
	d.statsMu.Unlock()

	log.Printf("DHT maintenance: %d nodes (%d good, %d questionable, %d bad), %d peers across %d torrents", 
		totalNodes, goodNodes, questionableNodes, badNodes, peerStats.TotalPeers, torrentCount)
	log.Printf("DHT peer cache: %d hits, %d misses, %d DB writes, %d DB reads", 
		peerStats.CacheHits, peerStats.CacheMisses, peerStats.DatabaseWrites, peerStats.DatabaseReads)
	log.Printf("DHT verification: %.1f%% verified (%d/%d), %d high reliability, %d low reliability",
		verificationStats["verification_rate"], verificationStats["verified_peers"], 
		verificationStats["total_peers"], verificationStats["high_reliability_peers"], 
		verificationStats["low_reliability_peers"])
}

// handleAnnouncements processes torrent announcements
func (d *DHT) handleAnnouncements() {
	for {
		select {
		case <-d.stopCh:
			return
		case req := <-d.announceQueue:
			d.processAnnounce(req)
		}
	}
}

// processAnnounce processes a torrent announce request
func (d *DHT) processAnnounce(req AnnounceRequest) {
	// Store our own peer info for this torrent
	peerInfo := make([]byte, 6) // 4 bytes IP + 2 bytes port
	
	// Get our external IP (simplified - would need proper detection)
	ip := net.ParseIP("127.0.0.1").To4()
	copy(peerInfo[:4], ip)
	binary.BigEndian.PutUint16(peerInfo[4:], uint16(req.Port))

	d.storageMu.Lock()
	d.storage[req.InfoHash] = peerInfo
	d.storageMu.Unlock()

	log.Printf("Announced torrent %s on port %d", req.InfoHash, req.Port)
}

// Announce announces a torrent to the DHT
func (d *DHT) Announce(infoHash string, port int) {
	select {
	case d.announceQueue <- AnnounceRequest{InfoHash: infoHash, Port: port}:
		log.Printf("Queued announce for torrent %s", infoHash)
	default:
		log.Printf("Announce queue full, dropping announce for %s", infoHash)
	}
}

// FindPeers searches for peers for a given info hash using new peer storage
func (d *DHT) FindPeers(infoHash string) ([]*Peer, error) {
	peers := d.peerStorage.GetPeers(infoHash)
	return peers, nil
}

// FindPeersFromNetwork actively searches the network for peers
func (d *DHT) FindPeersFromNetwork(infohash []byte) ([]*Peer, error) {
	var allPeers []*Peer
	
	// Get closest nodes to the infohash
	var targetID NodeID
	copy(targetID[:], infohash)
	closestNodes := d.routingTable.FindClosestNodes(targetID, Alpha)
	
	// Query each node for peers
	for _, node := range closestNodes {
		peers, nodes, err := d.GetPeers(infohash, node.Addr)
		if err != nil {
			log.Printf("GetPeers failed for node %s: %v", node.Addr, err)
			continue
		}
		
		if peers != nil {
			// Found peers
			allPeers = append(allPeers, peers...)
		} else if nodes != nil {
			// Got more nodes to query
			for _, newNode := range nodes {
				morePeers, _, err := d.GetPeers(infohash, newNode.Addr)
				if err == nil && morePeers != nil {
					allPeers = append(allPeers, morePeers...)
				}
			}
		}
	}
	
	return allPeers, nil
}

// GetPeerStorageStats returns peer storage statistics
func (ps *PeerStorage) GetStats() PeerStorageStats {
	ps.mu.RLock()
	defer ps.mu.RUnlock()
	return ps.stats
}

// GetTorrentCount returns the number of torrents with stored peers
func (ps *PeerStorage) GetTorrentCount() int {
	ps.mu.RLock()
	defer ps.mu.RUnlock()
	return len(ps.infohashes)
}

// GetPeerCountForTorrent returns the number of peers for a specific torrent
func (ps *PeerStorage) GetPeerCountForTorrent(infohash string) int {
	ps.mu.RLock()
	peerList, exists := ps.infohashes[infohash]
	ps.mu.RUnlock()
	
	if !exists {
		// Try database
		if ps.db != nil {
			count, err := ps.db.GetPeerCount(infohash)
			if err == nil {
				return count
			}
		}
		return 0
	}
	
	peerList.mu.RLock()
	defer peerList.mu.RUnlock()
	return len(peerList.peers)
}

// GetStats returns current DHT statistics including peer storage
func (d *DHT) GetStats() Stats {
	d.statsMu.Lock()
	defer d.statsMu.Unlock()
	
	// Update peer storage stats
	peerStats := d.peerStorage.GetStats()
	d.stats.StoredItems = int(peerStats.TotalPeers)
	
	return d.stats
}

// AddNode adds a node to the routing table following Kademlia rules
func (rt *RoutingTable) AddNode(node *Node) {
	// Don't add ourselves
	if node.ID == rt.selfID {
		return
	}
	
	distance := xor(rt.selfID, node.ID)
	bucketIndex := leadingZeros(distance)
	
	if bucketIndex >= NumBuckets {
		bucketIndex = NumBuckets - 1
	}

	bucket := rt.buckets[bucketIndex]
	bucket.mu.Lock()
	defer bucket.mu.Unlock()

	// Check if node already exists
	for i, existingNode := range bucket.nodes {
		if existingNode.ID == node.ID {
			// Update existing node - move to tail (most recently seen)
			updated := *node
			updated.Health = NodeGood
			updated.FailedQueries = 0
			
			// Remove from current position
			bucket.nodes = append(bucket.nodes[:i], bucket.nodes[i+1:]...)
			// Add to tail
			bucket.nodes = append(bucket.nodes, &updated)
			bucket.lastChanged = time.Now()
			return
		}
	}

	// Add new node if bucket not full
	if len(bucket.nodes) < BucketSize {
		newNode := *node
		newNode.Health = NodeGood
		bucket.nodes = append(bucket.nodes, &newNode)
		bucket.lastChanged = time.Now()
		return
	}

	// Bucket is full - apply Kademlia replacement rules
	// Look for bad nodes to replace first
	for i, n := range bucket.nodes {
		if n.Health == NodeBad {
			newNode := *node
			newNode.Health = NodeGood
			bucket.nodes[i] = &newNode
			bucket.lastChanged = time.Now()
			return
		}
	}
	
	// No bad nodes - look for questionable nodes
	for i, n := range bucket.nodes {
		if n.Health == NodeQuestionable {
			// Ping the questionable node to verify it's still alive
			// For now, just replace it (in a full implementation, we'd ping first)
			newNode := *node
			newNode.Health = NodeGood
			bucket.nodes[i] = &newNode
			bucket.lastChanged = time.Now()
			return
		}
	}
	
	// All nodes are good - check if the head node is stale
	// Kademlia: if all nodes are good, ping the head (least recently seen)
	// If it responds, move to tail and discard new node
	// If it doesn't respond, replace it with new node
	head := bucket.nodes[0]
	if time.Since(head.LastSeen) > 15*time.Minute {
		head.Health = NodeQuestionable
		// In a full implementation, we'd ping here and wait for response
		// For now, just replace stale nodes
		newNode := *node
		newNode.Health = NodeGood
		bucket.nodes[0] = &newNode
		bucket.lastChanged = time.Now()
	}
	// If head is fresh, discard the new node (Kademlia rule)
}

// FindClosestNodes returns the K closest nodes to a target ID using proper Kademlia algorithm
func (rt *RoutingTable) FindClosestNodes(targetID NodeID, count int) []*Node {
	distance := xor(rt.selfID, targetID)
	startBucket := leadingZeros(distance)
	
	if startBucket >= NumBuckets {
		startBucket = NumBuckets - 1
	}
	
	var candidates []*Node
	
	// Start from the closest bucket and expand outward
	for offset := 0; offset < NumBuckets; offset++ {
		// Check bucket at startBucket + offset
		if startBucket+offset < NumBuckets {
			bucket := rt.buckets[startBucket+offset]
			bucket.mu.RLock()
			for _, node := range bucket.nodes {
				if node.Health != NodeBad {
					candidates = append(candidates, node)
				}
			}
			bucket.mu.RUnlock()
		}
		
		// Check bucket at startBucket - offset (if different)
		if offset > 0 && startBucket-offset >= 0 {
			bucket := rt.buckets[startBucket-offset]
			bucket.mu.RLock()
			for _, node := range bucket.nodes {
				if node.Health != NodeBad {
					candidates = append(candidates, node)
				}
			}
			bucket.mu.RUnlock()
		}
		
		// If we have enough candidates, stop searching
		if len(candidates) >= count*2 {
			break
		}
	}
	
	// Sort candidates by XOR distance
	type nodeDistance struct {
		node     *Node
		distance NodeID
	}
	
	var distances []nodeDistance
	for _, node := range candidates {
		dist := xor(node.ID, targetID)
		distances = append(distances, nodeDistance{node: node, distance: dist})
	}
	
	// Sort using more efficient sorting
	for i := 0; i < len(distances)-1; i++ {
		minIdx := i
		for j := i + 1; j < len(distances); j++ {
			if compareNodeIDs(distances[j].distance, distances[minIdx].distance) < 0 {
				minIdx = j
			}
		}
		if minIdx != i {
			distances[i], distances[minIdx] = distances[minIdx], distances[i]
		}
	}
	
	// Return up to count closest nodes
	var result []*Node
	for i := 0; i < len(distances) && i < count; i++ {
		result = append(result, distances[i].node)
	}
	
	return result
}

// UpdateNodeHealth updates the health status of a node
func (rt *RoutingTable) UpdateNodeHealth(nodeID NodeID, health NodeHealth) {
	distance := xor(rt.selfID, nodeID)
	bucketIndex := leadingZeros(distance)
	
	if bucketIndex >= NumBuckets {
		bucketIndex = NumBuckets - 1
	}
	
	bucket := rt.buckets[bucketIndex]
	bucket.mu.Lock()
	defer bucket.mu.Unlock()
	
	for _, node := range bucket.nodes {
		if node.ID == nodeID {
			node.Health = health
			if health == NodeBad {
				node.FailedQueries++
			} else if health == NodeGood {
				node.FailedQueries = 0
				node.LastSeen = time.Now()
			}
			return
		}
	}
}

// RemoveNode removes a node from the routing table
func (rt *RoutingTable) RemoveNode(nodeID NodeID) {
	distance := xor(rt.selfID, nodeID)
	bucketIndex := leadingZeros(distance)
	
	if bucketIndex >= NumBuckets {
		bucketIndex = NumBuckets - 1
	}
	
	bucket := rt.buckets[bucketIndex]
	bucket.mu.Lock()
	defer bucket.mu.Unlock()
	
	for i, node := range bucket.nodes {
		if node.ID == nodeID {
			// Remove node from slice
			bucket.nodes = append(bucket.nodes[:i], bucket.nodes[i+1:]...)
			bucket.lastChanged = time.Now()
			return
		}
	}
}

// GetBucketInfo returns information about a bucket for debugging
func (rt *RoutingTable) GetBucketInfo(bucketIndex int) (int, time.Time) {
	if bucketIndex >= NumBuckets || bucketIndex < 0 {
		return 0, time.Time{}
	}
	
	bucket := rt.buckets[bucketIndex]
	bucket.mu.RLock()
	defer bucket.mu.RUnlock()
	
	return len(bucket.nodes), bucket.lastChanged
}

// RefreshBuckets performs bucket refresh according to Kademlia
func (rt *RoutingTable) RefreshBuckets(dht *DHT) {
	now := time.Now()
	refreshInterval := 15 * time.Minute
	
	for i, bucket := range rt.buckets {
		bucket.mu.Lock()
		needsRefresh := now.Sub(bucket.lastRefresh) > refreshInterval
		
		if needsRefresh {
			// Generate a random ID in this bucket's range
			targetID := rt.generateRandomIDForBucket(i)
			bucket.lastRefresh = now
			bucket.mu.Unlock()
			
			// Perform lookup to refresh this bucket
			go dht.performLookup(targetID)
		} else {
			bucket.mu.Unlock()
		}
	}
}

// generateRandomIDForBucket generates a random node ID that would fall in the specified bucket
func (rt *RoutingTable) generateRandomIDForBucket(bucketIndex int) NodeID {
	var targetID NodeID
	copy(targetID[:], rt.selfID[:])
	
	if bucketIndex == 0 {
		// For bucket 0, flip a random bit in the last few bits
		randomByte := byte(time.Now().UnixNano() % 256)
		targetID[NodeIDLength-1] ^= randomByte
	} else if bucketIndex < 160 {
		// Calculate which bit to flip based on bucket index
		byteIndex := (159 - bucketIndex) / 8
		bitIndex := (159 - bucketIndex) % 8
		
		// Flip the bit at the calculated position
		targetID[byteIndex] ^= (1 << bitIndex)
		
		// Add some randomness to the remaining bits
		for i := byteIndex + 1; i < NodeIDLength; i++ {
			targetID[i] ^= byte(time.Now().UnixNano() % 256)
		}
	}
	
	return targetID
}

// PerformHealthCheck checks the health of nodes and marks questionable ones
func (rt *RoutingTable) PerformHealthCheck(dht *DHT) {
	now := time.Now()
	questionableThreshold := 15 * time.Minute
	badThreshold := 30 * time.Minute
	
	for _, bucket := range rt.buckets {
		bucket.mu.Lock()
		
		for _, node := range bucket.nodes {
			timeSinceLastSeen := now.Sub(node.LastSeen)
			
			// Mark nodes as questionable if not seen recently
			if node.Health == NodeGood && timeSinceLastSeen > questionableThreshold {
				node.Health = NodeQuestionable
				// Ping questionable nodes to verify they're still alive
				go dht.pingNode(node)
			} else if node.Health == NodeQuestionable && timeSinceLastSeen > badThreshold {
				// Mark as bad if no response for a long time
				node.Health = NodeBad
			}
		}
		
		bucket.mu.Unlock()
	}
}

// CleanupBadNodes removes nodes marked as bad from the routing table
func (rt *RoutingTable) CleanupBadNodes() {
	for _, bucket := range rt.buckets {
		bucket.mu.Lock()
		
		// Filter out bad nodes
		goodNodes := make([]*Node, 0, len(bucket.nodes))
		for _, node := range bucket.nodes {
			if node.Health != NodeBad || node.FailedQueries < 3 {
				goodNodes = append(goodNodes, node)
			}
		}
		
		if len(goodNodes) != len(bucket.nodes) {
			bucket.nodes = goodNodes
			bucket.lastChanged = time.Now()
		}
		
		bucket.mu.Unlock()
	}
}

// pingNode pings a node to check if it's still alive
func (d *DHT) pingNode(node *Node) {
	start := time.Now()
	err := d.sendPing(node.Addr)
	
	if err == nil {
		// Node responded - mark as good and update RTT
		node.Health = NodeGood
		node.FailedQueries = 0
		node.LastSeen = time.Now()
		node.RTT = time.Since(start)
	} else {
		// Node didn't respond - increment failed queries
		node.FailedQueries++
		if node.FailedQueries >= 3 {
			node.Health = NodeBad
		}
	}
}

// performLookup performs a node lookup for bucket refresh
func (d *DHT) performLookup(targetID NodeID) {
	// This is a simplified lookup - in a full implementation,
	// this would be an iterative process finding closer and closer nodes
	closestNodes := d.routingTable.FindClosestNodes(targetID, Alpha)
	
	for _, node := range closestNodes {
		// Send find_node query to each close node
		args := map[string]interface{}{
			"id":     string(d.nodeID[:]),
			"target": string(targetID[:]),
		}
		
		go func(n *Node) {
			response, err := d.sendQuery("find_node", args, n.Addr)
			if err == nil && response.Response != nil {
				if nodesBytes, ok := response.Response["nodes"].(string); ok {
					// Parse and add returned nodes to routing table
					d.parseAndAddNodes([]byte(nodesBytes))
				}
			}
		}(node)
	}
}

// ============ CORE DHT OPERATIONS ============

// Ping sends a ping query to a node and updates routing table on response
func (d *DHT) Ping(addr *net.UDPAddr) (*Node, error) {
	args := map[string]interface{}{
		"id": string(d.nodeID[:]),
	}
	
	response, err := d.sendQuery("ping", args, addr)
	if err != nil {
		return nil, fmt.Errorf("ping failed: %w", err)
	}
	
	// Parse response and extract node ID
	if response.Response == nil {
		return nil, fmt.Errorf("invalid ping response")
	}
	
	nodeIDBytes, ok := response.Response["id"].(string)
	if !ok || len(nodeIDBytes) != NodeIDLength {
		return nil, fmt.Errorf("invalid node ID in ping response")
	}
	
	var nodeID NodeID
	copy(nodeID[:], nodeIDBytes)
	
	// Create and add node to routing table
	node := &Node{
		ID:       nodeID,
		Addr:     addr,
		LastSeen: time.Now(),
		Health:   NodeGood,
		RTT:      time.Since(time.Now()), // This would be calculated properly in sendQuery
	}
	
	d.routingTable.AddNode(node)
	log.Printf("Ping successful: node %x at %s", nodeID[:8], addr)
	
	return node, nil
}

// FindNode queries for nodes close to a target ID
func (d *DHT) FindNode(targetID NodeID, addr *net.UDPAddr) ([]*Node, error) {
	args := map[string]interface{}{
		"id":     string(d.nodeID[:]),
		"target": string(targetID[:]),
	}
	
	response, err := d.sendQuery("find_node", args, addr)
	if err != nil {
		return nil, fmt.Errorf("find_node failed: %w", err)
	}
	
	if response.Response == nil {
		return nil, fmt.Errorf("invalid find_node response")
	}
	
	// Parse responding node ID and add to routing table
	if nodeIDBytes, ok := response.Response["id"].(string); ok && len(nodeIDBytes) == NodeIDLength {
		var nodeID NodeID
		copy(nodeID[:], nodeIDBytes)
		respondingNode := &Node{
			ID:       nodeID,
			Addr:     addr,
			LastSeen: time.Now(),
			Health:   NodeGood,
		}
		d.routingTable.AddNode(respondingNode)
	}
	
	// Parse nodes from response
	nodesBytes, ok := response.Response["nodes"].(string)
	if !ok {
		return nil, fmt.Errorf("no nodes in find_node response")
	}
	
	nodes := d.parseCompactNodes([]byte(nodesBytes))
	
	// Add discovered nodes to routing table
	for _, node := range nodes {
		d.routingTable.AddNode(node)
	}
	
	log.Printf("FindNode successful: discovered %d nodes from %s", len(nodes), addr)
	return nodes, nil
}

// GetPeers searches for peers of a torrent by infohash
func (d *DHT) GetPeers(infohash []byte, addr *net.UDPAddr) ([]*Peer, []*Node, error) {
	args := map[string]interface{}{
		"id":        string(d.nodeID[:]),
		"info_hash": string(infohash),
	}
	
	response, err := d.sendQuery("get_peers", args, addr)
	if err != nil {
		return nil, nil, fmt.Errorf("get_peers failed: %w", err)
	}
	
	if response.Response == nil {
		return nil, nil, fmt.Errorf("invalid get_peers response")
	}
	
	// Parse responding node ID and add to routing table
	if nodeIDBytes, ok := response.Response["id"].(string); ok && len(nodeIDBytes) == NodeIDLength {
		var nodeID NodeID
		copy(nodeID[:], nodeIDBytes)
		respondingNode := &Node{
			ID:       nodeID,
			Addr:     addr,
			LastSeen: time.Now(),
			Health:   NodeGood,
		}
		d.routingTable.AddNode(respondingNode)
	}
	
	// Check if response contains peers or nodes
	if values, ok := response.Response["values"].([]interface{}); ok {
		// Response contains peers
		var peers []*Peer
		for _, value := range values {
			if peerBytes, ok := value.(string); ok && len(peerBytes) == 6 {
				ip := net.IP(peerBytes[:4])
				port := int(binary.BigEndian.Uint16([]byte(peerBytes[4:])))
				peers = append(peers, &Peer{
					IP:    ip,
					Port:  port,
					Added: time.Now(),
				})
			}
		}
		log.Printf("GetPeers successful: found %d peers from %s", len(peers), addr)
		return peers, nil, nil
	}
	
	// Response contains nodes
	nodesBytes, ok := response.Response["nodes"].(string)
	if !ok {
		return nil, nil, fmt.Errorf("no peers or nodes in get_peers response")
	}
	
	nodes := d.parseCompactNodes([]byte(nodesBytes))
	
	// Add discovered nodes to routing table
	for _, node := range nodes {
		d.routingTable.AddNode(node)
	}
	
	log.Printf("GetPeers successful: found %d nodes from %s", len(nodes), addr)
	return nil, nodes, nil
}

// AnnouncePeer stores peer info for an infohash after validating token
func (d *DHT) AnnouncePeer(infohash []byte, port int, token string, addr *net.UDPAddr) error {
	args := map[string]interface{}{
		"id":        string(d.nodeID[:]),
		"info_hash": string(infohash),
		"port":      port,
		"token":     token,
	}
	
	response, err := d.sendQuery("announce_peer", args, addr)
	if err != nil {
		return fmt.Errorf("announce_peer failed: %w", err)
	}
	
	if response.Response == nil {
		return fmt.Errorf("invalid announce_peer response")
	}
	
	// Parse responding node ID and add to routing table
	if nodeIDBytes, ok := response.Response["id"].(string); ok && len(nodeIDBytes) == NodeIDLength {
		var nodeID NodeID
		copy(nodeID[:], nodeIDBytes)
		respondingNode := &Node{
			ID:       nodeID,
			Addr:     addr,
			LastSeen: time.Now(),
			Health:   NodeGood,
		}
		d.routingTable.AddNode(respondingNode)
	}
	
	// Store peer info in our storage
	peer := &Peer{
		IP:    addr.IP,
		Port:  port,
		Added: time.Now(),
	}
	
	infohashHex := fmt.Sprintf("%x", infohash)
	d.peerStorage.AddPeer(infohashHex, peer)
	
	log.Printf("AnnouncePeer successful: stored peer %s:%d for %s", addr.IP, port, infohashHex[:8])
	return nil
}

// ============ HELPER FUNCTIONS ============

// parseCompactNodes parses compact node format (26 bytes per node)
func (d *DHT) parseCompactNodes(nodesBytes []byte) []*Node {
	var nodes []*Node
	nodeSize := 26 // 20-byte ID + 4-byte IP + 2-byte port
	
	for i := 0; i+nodeSize <= len(nodesBytes); i += nodeSize {
		var nodeID NodeID
		copy(nodeID[:], nodesBytes[i:i+20])
		
		ip := net.IP(nodesBytes[i+20 : i+24])
		port := binary.BigEndian.Uint16(nodesBytes[i+24 : i+26])
		
		addr := &net.UDPAddr{
			IP:   ip,
			Port: int(port),
		}
		
		node := &Node{
			ID:       nodeID,
			Addr:     addr,
			LastSeen: time.Now(),
			Health:   NodeGood,
		}
		
		nodes = append(nodes, node)
	}
	
	return nodes
}

// parseAndAddNodes parses compact node format and adds nodes to routing table
func (d *DHT) parseAndAddNodes(nodesBytes []byte) {
	nodes := d.parseCompactNodes(nodesBytes)
	for _, node := range nodes {
		d.routingTable.AddNode(node)
	}
}

// ============ ITERATIVE LOOKUP FUNCTIONS ============

// iterativeFindNode performs an iterative node lookup using Kademlia algorithm
func (d *DHT) iterativeFindNode(targetID NodeID, initialNodes []*net.UDPAddr) []*Node {
	return d.iterativeLookup(targetID, initialNodes, "find_node")
}

// iterativeFindPeers performs an iterative peer lookup
func (d *DHT) iterativeFindPeers(targetID NodeID, initialNodes []*net.UDPAddr) ([]*Peer, []*Node) {
	nodes := d.iterativeLookup(targetID, initialNodes, "get_peers")
	// For now, return nodes only. Peers would be collected during the lookup process
	return nil, nodes
}

// iterativeLookup performs the core iterative lookup algorithm
func (d *DHT) iterativeLookup(targetID NodeID, initialNodes []*net.UDPAddr, queryType string) []*Node {
	// Shortlist of closest nodes found so far
	shortlist := make(map[string]*Node)
	queried := make(map[string]bool)
	
	// Convert initial nodes to Node objects
	for _, addr := range initialNodes {
		// Create temporary node (we'll get real node ID when we query)
		tempNode := &Node{
			Addr:     addr,
			LastSeen: time.Now(),
			Health:   NodeGood,
		}
		key := fmt.Sprintf("%s:%d", addr.IP, addr.Port)
		shortlist[key] = tempNode
	}
	
	// Add nodes from our routing table
	routingTableNodes := d.routingTable.FindClosestNodes(targetID, BucketSize*2)
	for _, node := range routingTableNodes {
		key := fmt.Sprintf("%s:%d", node.Addr.IP, node.Addr.Port)
		shortlist[key] = node
	}
	
	log.Printf("Starting iterative lookup for target %x with %d initial nodes", targetID[:8], len(shortlist))
	
	// Iterative process
	for iteration := 0; iteration < 10; iteration++ { // Max 10 iterations
		// Find Alpha closest unqueried nodes
		candidates := d.selectCandidates(shortlist, queried, targetID, Alpha)
		if len(candidates) == 0 {
			log.Printf("No more candidates, stopping after %d iterations", iteration)
			break
		}
		
		log.Printf("Iteration %d: querying %d nodes", iteration+1, len(candidates))
		
		// Query candidates in parallel
		newNodes := d.queryNodesParallel(candidates, targetID, queryType, queried)
		
		// Add new nodes to shortlist
		addedNew := false
		for _, node := range newNodes {
			key := fmt.Sprintf("%s:%d", node.Addr.IP, node.Addr.Port)
			if _, exists := shortlist[key]; !exists {
				shortlist[key] = node
				addedNew = true
			}
		}
		
		// If no new nodes found, stop
		if !addedNew {
			log.Printf("No new nodes found, stopping after %d iterations", iteration+1)
			break
		}
	}
	
	// Return the closest nodes found
	result := d.getClosestFromShortlist(shortlist, targetID, BucketSize)
	log.Printf("Iterative lookup completed: found %d nodes", len(result))
	return result
}

// selectCandidates selects the Alpha closest unqueried nodes
func (d *DHT) selectCandidates(shortlist map[string]*Node, queried map[string]bool, targetID NodeID, count int) []*Node {
	type nodeDistance struct {
		node     *Node
		distance NodeID
		key      string
	}
	
	var candidates []nodeDistance
	for key, node := range shortlist {
		if !queried[key] && node.Health != NodeBad {
			// For initial nodes without real node IDs, use a hash of their address
			var nodeIDForDistance NodeID
			if node.ID == (NodeID{}) {
				// Create pseudo node ID from address for distance calculation
			hash := sha1.Sum([]byte(fmt.Sprintf("%s:%d", node.Addr.IP, node.Addr.Port)))
				copy(nodeIDForDistance[:], hash[:])
			} else {
				nodeIDForDistance = node.ID
			}
			
			dist := xor(nodeIDForDistance, targetID)
			candidates = append(candidates, nodeDistance{
				node:     node,
				distance: dist,
				key:      key,
			})
		}
	}
	
	// Sort by distance
	for i := 0; i < len(candidates)-1; i++ {
		for j := i + 1; j < len(candidates); j++ {
			if compareNodeIDs(candidates[i].distance, candidates[j].distance) > 0 {
				candidates[i], candidates[j] = candidates[j], candidates[i]
			}
		}
	}
	
	// Return up to count closest candidates
	var result []*Node
	for i := 0; i < len(candidates) && i < count; i++ {
		result = append(result, candidates[i].node)
	}
	
	return result
}

// queryNodesParallel queries multiple nodes in parallel
func (d *DHT) queryNodesParallel(candidates []*Node, targetID NodeID, queryType string, queried map[string]bool) []*Node {
	type queryResult struct {
		nodes []*Node
		node  *Node
	}
	
	resultChan := make(chan queryResult, len(candidates))
	var wg sync.WaitGroup
	
	// Launch parallel queries
	for _, candidate := range candidates {
		wg.Add(1)
		go func(node *Node) {
			defer wg.Done()
			
			key := fmt.Sprintf("%s:%d", node.Addr.IP, node.Addr.Port)
			queried[key] = true
			
			var discoveredNodes []*Node
			
			if queryType == "find_node" {
				nodes, err := d.FindNode(targetID, node.Addr)
				if err == nil {
					discoveredNodes = nodes
				}
			} else if queryType == "get_peers" {
				_, nodes, err := d.GetPeers(targetID[:], node.Addr)
				if err == nil && nodes != nil {
					discoveredNodes = nodes
				}
			}
			
			resultChan <- queryResult{nodes: discoveredNodes, node: node}
		}(candidate)
	}
	
	// Close channel when all queries complete
	go func() {
		wg.Wait()
		close(resultChan)
	}()
	
	// Collect results
	var allNodes []*Node
	for result := range resultChan {
		if len(result.nodes) > 0 {
			allNodes = append(allNodes, result.nodes...)
		}
	}
	
	return allNodes
}

// getClosestFromShortlist returns the closest nodes from the shortlist
func (d *DHT) getClosestFromShortlist(shortlist map[string]*Node, targetID NodeID, count int) []*Node {
	type nodeDistance struct {
		node     *Node
		distance NodeID
	}
	
	var candidates []nodeDistance
	for _, node := range shortlist {
		if node.Health != NodeBad {
			// Use actual node ID if available, otherwise create from address
			var nodeIDForDistance NodeID
			if node.ID != (NodeID{}) {
				nodeIDForDistance = node.ID
			} else {
				hash := sha1.Sum([]byte(fmt.Sprintf("%s:%d", node.Addr.IP, node.Addr.Port)))
				copy(nodeIDForDistance[:], hash[:])
			}
			
			dist := xor(nodeIDForDistance, targetID)
			candidates = append(candidates, nodeDistance{
				node:     node,
				distance: dist,
			})
		}
	}
	
	// Sort by distance
	for i := 0; i < len(candidates)-1; i++ {
		for j := i + 1; j < len(candidates); j++ {
			if compareNodeIDs(candidates[i].distance, candidates[j].distance) > 0 {
				candidates[i], candidates[j] = candidates[j], candidates[i]
			}
		}
	}
	
	// Return up to count closest nodes
	var result []*Node
	for i := 0; i < len(candidates) && i < count; i++ {
		result = append(result, candidates[i].node)
	}
	
	return result
}

// xor calculates XOR distance between two node IDs
func xor(a, b NodeID) NodeID {
	var result NodeID
	for i := 0; i < NodeIDLength; i++ {
		result[i] = a[i] ^ b[i]
	}
	return result
}

// leadingZeros counts leading zero bits
func leadingZeros(id NodeID) int {
	for i, b := range id {
		if b != 0 {
			// Count zeros in this byte
			zeros := 0
			for bit := 7; bit >= 0; bit-- {
				if (b>>bit)&1 == 0 {
					zeros++
				} else {
					break
				}
			}
			return i*8 + zeros
		}
	}
	return NodeIDLength * 8
}

// SimpleDatabaseImpl provides a simple database implementation
type SimpleDatabaseImpl struct {
	db *sql.DB
}

// NewSimpleDatabase creates a new simple database implementation
func NewSimpleDatabase(db *sql.DB) *SimpleDatabaseImpl {
	return &SimpleDatabaseImpl{db: db}
}

// CreateDHTTables creates the necessary DHT tables with reliability tracking
func (sdb *SimpleDatabaseImpl) CreateDHTTables() error {
	query := `
	CREATE TABLE IF NOT EXISTS dht_stored_peers (
		infohash TEXT NOT NULL,
		ip TEXT NOT NULL,
		port INTEGER NOT NULL,
		peer_id TEXT,
		last_announced DATETIME DEFAULT CURRENT_TIMESTAMP,
		reliability_score INTEGER DEFAULT 50,
		announce_count INTEGER DEFAULT 1,
		verified BOOLEAN DEFAULT FALSE,
		last_verified DATETIME,
		PRIMARY KEY (infohash, ip, port)
	);
	
	CREATE INDEX IF NOT EXISTS idx_dht_peers_infohash ON dht_stored_peers(infohash);
	CREATE INDEX IF NOT EXISTS idx_dht_peers_last_announced ON dht_stored_peers(last_announced);
	CREATE INDEX IF NOT EXISTS idx_dht_peers_reliability ON dht_stored_peers(reliability_score);
	`
	_, err := sdb.db.Exec(query)
	return err
}

// StorePeer stores a peer in the database with reliability tracking
func (sdb *SimpleDatabaseImpl) StorePeer(infohash, ip string, port int, peerID string, lastAnnounced time.Time) error {
	// Check if peer exists to update announce count
	var existingCount int
	existQuery := `SELECT announce_count FROM dht_stored_peers WHERE infohash = ? AND ip = ? AND port = ?`
	err := sdb.db.QueryRow(existQuery, infohash, ip, port).Scan(&existingCount)
	
	if err == sql.ErrNoRows {
		// New peer
		query := `INSERT INTO dht_stored_peers (infohash, ip, port, peer_id, last_announced, reliability_score, announce_count) 
				  VALUES (?, ?, ?, ?, ?, 50, 1)`
		_, err := sdb.db.Exec(query, infohash, ip, port, peerID, lastAnnounced)
		return err
	} else if err != nil {
		return err
	} else {
		// Existing peer - update
		query := `UPDATE dht_stored_peers 
				  SET peer_id = ?, last_announced = ?, announce_count = announce_count + 1,
				      reliability_score = MIN(100, reliability_score + 5)
				  WHERE infohash = ? AND ip = ? AND port = ?`
		_, err := sdb.db.Exec(query, peerID, lastAnnounced, infohash, ip, port)
		return err
	}
}

// UpdatePeerReliability updates peer reliability information
func (sdb *SimpleDatabaseImpl) UpdatePeerReliability(infohash, ip string, port int, reliabilityScore int, verified bool, lastVerified time.Time) error {
	query := `UPDATE dht_stored_peers 
			  SET reliability_score = ?, verified = ?, last_verified = ?
			  WHERE infohash = ? AND ip = ? AND port = ?`
	_, err := sdb.db.Exec(query, reliabilityScore, verified, lastVerified, infohash, ip, port)
	return err
}

// GetPeersForTorrent retrieves peers for a specific torrent ordered by reliability
func (sdb *SimpleDatabaseImpl) GetPeersForTorrent(infohash string) ([]*StoredPeer, error) {
	query := `SELECT infohash, ip, port, peer_id, last_announced, reliability_score, announce_count, verified, last_verified
			  FROM dht_stored_peers 
			  WHERE infohash = ? AND last_announced > datetime('now', '-30 minutes')
			  ORDER BY reliability_score DESC, last_announced DESC 
			  LIMIT ?`
	
	rows, err := sdb.db.Query(query, infohash, MaxPeersPerTorrent)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	
	var peers []*StoredPeer
	for rows.Next() {
		var peer StoredPeer
		var lastVerified sql.NullTime
		err := rows.Scan(&peer.InfoHash, &peer.IP, &peer.Port, &peer.PeerID, 
			&peer.LastAnnounced, &peer.ReliabilityScore, &peer.AnnounceCount, 
			&peer.Verified, &lastVerified)
		if err != nil {
			return nil, err
		}
		
		if lastVerified.Valid {
			peer.LastVerified = lastVerified.Time
		}
		
		peers = append(peers, &peer)
	}
	
	return peers, rows.Err()
}

// CleanExpiredPeers removes peers older than 30 minutes
func (sdb *SimpleDatabaseImpl) CleanExpiredPeers() error {
	query := `DELETE FROM dht_stored_peers WHERE last_announced < datetime('now', '-30 minutes')`
	result, err := sdb.db.Exec(query)
	if err != nil {
		return err
	}
	
	rowsAffected, _ := result.RowsAffected()
	if rowsAffected > 0 {
		log.Printf("DHT: Cleaned %d expired peers from database", rowsAffected)
	}
	return nil
}

// GetPeerCount returns the number of peers for a specific torrent
func (sdb *SimpleDatabaseImpl) GetPeerCount(infohash string) (int, error) {
	query := `SELECT COUNT(*) FROM dht_stored_peers 
			  WHERE infohash = ? AND last_announced > datetime('now', '-30 minutes')`
	
	var count int
	err := sdb.db.QueryRow(query, infohash).Scan(&count)
	return count, err
}

// GetStoredPeersWithNodes returns both stored peers and closest nodes for better queryability
func (d *DHT) GetStoredPeersWithNodes(infoHash string) (peers []*Peer, nodes []*Node, err error) {
	// Get stored peers first
	peers = d.peerStorage.GetPeers(infoHash)
	
	// If we have peers, return them
	if len(peers) > 0 {
		return peers, nil, nil
	}
	
	// Otherwise, find closest nodes to the infohash
	var targetID NodeID
	if hashBytes, err := hex.DecodeString(infoHash); err == nil && len(hashBytes) == 20 {
		copy(targetID[:], hashBytes)
	} else {
		// Generate target from infohash string
		hash := sha1.Sum([]byte(infoHash))
		copy(targetID[:], hash[:])
	}
	
	nodes = d.routingTable.FindClosestNodes(targetID, MaxResponseNodes)
	return nil, nodes, nil
}

// GetAllStoredTorrents returns a list of all torrents with stored peers
func (ps *PeerStorage) GetAllStoredTorrents() []string {
	ps.mu.RLock()
	defer ps.mu.RUnlock()
	
	var torrents []string
	for infohash := range ps.infohashes {
		torrents = append(torrents, infohash)
	}
	
	return torrents
}

// GetStorageInfo returns comprehensive storage information
func (d *DHT) GetStorageInfo() map[string]interface{} {
	peerStats := d.peerStorage.GetStats()
	torrentCount := d.peerStorage.GetTorrentCount()
	allTorrents := d.peerStorage.GetAllStoredTorrents()
	
	return map[string]interface{}{
		"total_peers":       peerStats.TotalPeers,
		"total_torrents":    torrentCount,
		"cache_hits":        peerStats.CacheHits,
		"cache_misses":      peerStats.CacheMisses,
		"database_writes":   peerStats.DatabaseWrites,
		"database_reads":    peerStats.DatabaseReads,
		"stored_torrents":   allTorrents,
		"max_peers_limit":   MaxPeersPerTorrent,
		"peer_expiry_mins":  PeerExpiryMinutes,
		"token_rotation_mins": TokenRotationMinutes,
	}
}

// FindPeersWithFallback provides enhanced peer discovery with multiple fallbacks
func (d *DHT) FindPeersWithFallback(infoHash string) ([]*Peer, []*Node, error) {
	// Try storage first
	peers, nodes, err := d.GetStoredPeersWithNodes(infoHash)
	if err != nil {
		return nil, nil, err
	}
	
	// If we have peers, return them
	if len(peers) > 0 {
		log.Printf("DHT: Found %d stored peers for %s", len(peers), infoHash[:8])
		return peers, nil, nil
	}
	
	// If we have nodes, we can use them for further queries
	if len(nodes) > 0 {
		log.Printf("DHT: Found %d closest nodes for %s", len(nodes), infoHash[:8])
		return nil, nodes, nil
	}
	
	// Last resort: try network lookup if we have routing table nodes
	if infoHashBytes, err := hex.DecodeString(infoHash); err == nil && len(infoHashBytes) == 20 {
		networkPeers, err := d.FindPeersFromNetwork(infoHashBytes)
		if err == nil && len(networkPeers) > 0 {
			log.Printf("DHT: Found %d network peers for %s", len(networkPeers), infoHash[:8])
			return networkPeers, nil, nil
		}
	}
	
	log.Printf("DHT: No peers found for %s", infoHash[:8])
	return nil, nil, fmt.Errorf("no peers found")
}

// selectReliablePeers selects the most reliable peers up to the limit
func (d *DHT) selectReliablePeers(peers []*Peer, limit int) []*Peer {
	if len(peers) <= limit {
		return peers
	}
	
	// Sort peers by reliability score (descending)
	sortedPeers := make([]*Peer, len(peers))
	copy(sortedPeers, peers)
	
	// Simple bubble sort by reliability score
	for i := 0; i < len(sortedPeers)-1; i++ {
		for j := 0; j < len(sortedPeers)-1-i; j++ {
			if sortedPeers[j].ReliabilityScore < sortedPeers[j+1].ReliabilityScore {
				sortedPeers[j], sortedPeers[j+1] = sortedPeers[j+1], sortedPeers[j]
			}
		}
	}
	
	// Filter out peers with very low reliability scores
	var reliablePeers []*Peer
	for _, peer := range sortedPeers {
		if peer.ReliabilityScore >= MinReliabilityScore && len(reliablePeers) < limit {
			reliablePeers = append(reliablePeers, peer)
		}
	}
	
	// If we don't have enough reliable peers, fill with less reliable ones
	if len(reliablePeers) < limit {
		for _, peer := range sortedPeers {
			if peer.ReliabilityScore < MinReliabilityScore && len(reliablePeers) < limit {
				// Include less reliable peers but flag them
				reliablePeers = append(reliablePeers, peer)
			}
		}
	}
	
	return reliablePeers[:min(len(reliablePeers), limit)]
}

// handlePeerVerification processes peer verification requests
func (d *DHT) handlePeerVerification() {
	for {
		select {
		case <-d.stopCh:
			return
		case req := <-d.verificationQueue:
			d.verifyPeer(req)
		}
	}
}

// verifyPeer attempts to verify a peer by connecting to it
func (d *DHT) verifyPeer(req *PeerVerificationRequest) {
	peerAddr := fmt.Sprintf("%s:%d", req.Peer.IP.String(), req.Peer.Port)
	
	// Attempt to connect with timeout
	conn, err := net.DialTimeout("tcp", peerAddr, PeerVerificationTimeout)
	if err != nil {
		// Peer verification failed
		d.handleVerificationFailure(req)
		return
	}
	defer conn.Close()
	
	// Peer responded - update reliability score
	d.updatePeerReliability(req.InfoHash, req.Peer, true)
	log.Printf("DHT: Verified peer %s for %s", peerAddr, req.InfoHash[:8])
}

// handleVerificationFailure handles when peer verification fails
func (d *DHT) handleVerificationFailure(req *PeerVerificationRequest) {
	peerAddr := fmt.Sprintf("%s:%d", req.Peer.IP.String(), req.Peer.Port)
	
	// Retry up to 2 times with exponential backoff
	if req.Retries < 2 {
		req.Retries++
		
		// Exponential backoff: 30s, 60s
		delay := time.Duration(30*req.Retries) * time.Second
		
		go func() {
			time.Sleep(delay)
			select {
			case d.verificationQueue <- req:
				log.Printf("DHT: Retrying verification for peer %s (attempt %d)", peerAddr, req.Retries+1)
			default:
				// Queue full, skip retry
				log.Printf("DHT: Failed to queue verification retry for peer %s", peerAddr)
			}
		}()
		return
	}
	
	// All retries failed - mark peer as unreliable
	d.updatePeerReliability(req.InfoHash, req.Peer, false)
	log.Printf("DHT: Peer verification failed for %s after %d attempts", peerAddr, req.Retries+1)
}

// updatePeerReliability updates the reliability score of a peer
func (d *DHT) updatePeerReliability(infoHash string, peer *Peer, verified bool) {
	d.peerStorage.mu.Lock()
	defer d.peerStorage.mu.Unlock()
	
	peerList, exists := d.peerStorage.infohashes[infoHash]
	if !exists {
		return
	}
	
	peerList.mu.Lock()
	defer peerList.mu.Unlock()
	
	key := fmt.Sprintf("%s:%d", peer.IP.String(), peer.Port)
	if storedPeer, exists := peerList.peers[key]; exists {
		now := time.Now()
		
		if verified {
			// Increase reliability score
			storedPeer.ReliabilityScore = min(MaxReliabilityScore, storedPeer.ReliabilityScore+20)
			storedPeer.Verified = true
			storedPeer.LastVerified = now
		} else {
			// Decrease reliability score
			storedPeer.ReliabilityScore = max(0, storedPeer.ReliabilityScore-30)
			// Don't mark as verified=false immediately, might be temporary network issue
		}
		
		// Update database if available
		if d.peerStorage.db != nil {
			err := d.updatePeerInDatabase(infoHash, storedPeer)
			if err != nil {
				log.Printf("DHT: Failed to update peer reliability in database: %v", err)
			}
		}
		
		log.Printf("DHT: Updated peer %s reliability score to %d (verified: %v)", 
			key, storedPeer.ReliabilityScore, storedPeer.Verified)
	}
}

// max returns the larger of two integers
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// updatePeerInDatabase updates peer information in the database
func (d *DHT) updatePeerInDatabase(infoHash string, peer *Peer) error {
	if d.peerStorage.db == nil {
		return nil
	}
	
	// Update peer reliability information
	return d.peerStorage.db.UpdatePeerReliability(
		infoHash,
		peer.IP.String(),
		peer.Port,
		peer.ReliabilityScore,
		peer.Verified,
		peer.LastVerified,
	)
}

// SetPeerVerification enables or disables peer verification
func (d *DHT) SetPeerVerification(enabled bool) {
	d.peerVerification = enabled
	log.Printf("DHT: Peer verification %s", map[bool]string{true: "enabled", false: "disabled"}[enabled])
}

// GetVerificationStats returns peer verification statistics
func (d *DHT) GetVerificationStats() map[string]interface{} {
	stats := make(map[string]interface{})
	
	// Count peers by verification status
	totalPeers := 0
	verifiedPeers := 0
	highReliabilityPeers := 0
	lowReliabilityPeers := 0
	
	d.peerStorage.mu.RLock()
	for _, peerList := range d.peerStorage.infohashes {
		peerList.mu.RLock()
		for _, peer := range peerList.peers {
			totalPeers++
			if peer.Verified {
				verifiedPeers++
			}
			if peer.ReliabilityScore >= 80 {
				highReliabilityPeers++
			} else if peer.ReliabilityScore <= MinReliabilityScore {
				lowReliabilityPeers++
			}
		}
		peerList.mu.RUnlock()
	}
	d.peerStorage.mu.RUnlock()
	
	stats["total_peers"] = totalPeers
	stats["verified_peers"] = verifiedPeers
	stats["high_reliability_peers"] = highReliabilityPeers
	stats["low_reliability_peers"] = lowReliabilityPeers
	stats["verification_enabled"] = d.peerVerification
	stats["verification_queue_length"] = len(d.verificationQueue)
	
	if totalPeers > 0 {
		stats["verification_rate"] = float64(verifiedPeers) / float64(totalPeers) * 100
	} else {
		stats["verification_rate"] = 0.0
	}
	
	return stats
}

// CleanUnreliablePeers removes peers with very low reliability scores
func (d *DHT) CleanUnreliablePeers() {
	d.peerStorage.mu.Lock()
	defer d.peerStorage.mu.Unlock()
	
	cleanedCount := 0
	
	for infoHash, peerList := range d.peerStorage.infohashes {
		peerList.mu.Lock()
		
		reliablePeers := make(map[string]*Peer)
		for key, peer := range peerList.peers {
			// Keep peers with decent reliability or recent announces
			if peer.ReliabilityScore >= 10 || time.Since(peer.LastAnnounced) < 10*time.Minute {
				reliablePeers[key] = peer
			} else {
				cleanedCount++
				log.Printf("DHT: Removed unreliable peer %s (score: %d) for %s", 
					key, peer.ReliabilityScore, infoHash[:8])
			}
		}
		
		peerList.peers = reliablePeers
		peerList.mu.Unlock()
		
		// Remove empty peer lists
		if len(reliablePeers) == 0 {
			delete(d.peerStorage.infohashes, infoHash)
		}
	}
	
	if cleanedCount > 0 {
		log.Printf("DHT: Cleaned %d unreliable peers", cleanedCount)
	}
}

// GetRateLimitStats returns rate limiting statistics
func (d *DHT) GetRateLimitStats() map[string]interface{} {
	d.rateLimiter.mu.RLock()
	defer d.rateLimiter.mu.RUnlock()
	
	blockedIPs := 0
	activeIPs := len(d.rateLimiter.announces)
	
	now := time.Now()
	for _, unblockTime := range d.rateLimiter.blocklist {
		if now.Before(unblockTime) {
			blockedIPs++
		}
	}
	
	return map[string]interface{}{
		"active_ips":               activeIPs,
		"blocked_ips":              blockedIPs,
		"max_announces_per_hour":   MaxAnnouncesPerIPPerHour,
		"max_storage_per_ip":       MaxStoragePerIP,
		"blocklist_duration_mins":  BlocklistDurationMinutes,
	}
}

// GetComprehensiveStats returns all DHT statistics
func (d *DHT) GetComprehensiveStats() map[string]interface{} {
	baseStats := d.GetStats()
	storageInfo := d.GetStorageInfo()
	verificationStats := d.GetVerificationStats()
	rateLimitStats := d.GetRateLimitStats()
	
	return map[string]interface{}{
		"node_stats":         baseStats,
		"storage_stats":      storageInfo,
		"verification_stats": verificationStats,
		"rate_limit_stats":   rateLimitStats,
		"node_id":           hex.EncodeToString(d.nodeID[:]),
		"port":              d.config.Port,
		"routing_table_buckets": NumBuckets,
	}
}

// ForceVerifyPeer manually triggers verification for a specific peer
func (d *DHT) ForceVerifyPeer(infoHash string, ip string, port int) error {
	peers := d.peerStorage.GetPeers(infoHash)
	
	for _, peer := range peers {
		if peer.IP.String() == ip && peer.Port == port {
			req := &PeerVerificationRequest{
				InfoHash: infoHash,
				Peer:     peer,
				Retries:  0,
			}
			
			select {
			case d.verificationQueue <- req:
				log.Printf("DHT: Queued forced verification for peer %s:%d", ip, port)
				return nil
			default:
				return fmt.Errorf("verification queue full")
			}
		}
	}
	
	return fmt.Errorf("peer not found")
}

// GetTokenStats returns token generation statistics
func (d *DHT) GetTokenStats() map[string]interface{} {
	d.tokenMu.RLock()
	defer d.tokenMu.RUnlock()
	
	return map[string]interface{}{
		"secret_rotated_at":        d.lastSecretRotation,
		"next_rotation":           d.lastSecretRotation.Add(TokenRotationMinutes * time.Minute),
		"rotation_interval_mins":  TokenRotationMinutes,
		"token_length_bytes":      TokenLength,
		"has_previous_secret":     len(d.previousSecret) > 0,
	}
}

// GenerateInfoHash generates an info hash for a torrent name (for testing)
func GenerateInfoHash(name string) string {
	hash := sha1.Sum([]byte(name))
	return fmt.Sprintf("%x", hash)
}