package udp

import (
	"fmt"
	"net"
	"sync"
	"time"
)

// Server manages multiple UDP connections and handles packet routing
type Server struct {
	conn        *net.UDPConn           // Main UDP socket
	connections map[string]*Connection // Active connections by address
	mutex       sync.RWMutex           // Protects connections map
	handler     PacketHandler          // Application packet handler
	running     bool                   // Server running state
	config      Config                 // Server configuration
}

// NewServer creates a UDP server with simplified configuration
func NewServer(addr string, handler PacketHandler, config ...Config) (*Server, error) {
	cfg := DefaultConfig()
	if len(config) > 0 {
		cfg = config[0]
	}

	udpAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, fmt.Errorf("invalid UDP address %s: %w", addr, err)
	}

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

	// Set socket buffer size for better performance
	if cfg.BufferSize > 0 {
		conn.SetReadBuffer(cfg.BufferSize)
		conn.SetWriteBuffer(cfg.BufferSize)
	}

	return &Server{
		conn:        conn,
		connections: make(map[string]*Connection),
		handler:     handler,
		config:      cfg,
	}, nil
}

// Start begins accepting and processing UDP packets
func (s *Server) Start() error {
	s.running = true

	// Start background management tasks
	go s.connectionManager()

	// Main packet receive loop
	buffer := make([]byte, 8192)
	for s.running {
		n, addr, err := s.conn.ReadFromUDP(buffer)
		if err != nil {
			if s.running {
				fmt.Printf("UDP read error: %v\n", err)
			}
			continue
		}

		// Handle packet in separate goroutine to avoid blocking
		go s.handleIncomingPacket(buffer[:n], addr)
	}

	return nil
}

// Stop gracefully shuts down the server
func (s *Server) Stop() {
	s.running = false

	// Close all connections
	s.mutex.Lock()
	for _, conn := range s.connections {
		conn.Close()
	}
	s.connections = make(map[string]*Connection)
	s.mutex.Unlock()

	// Close UDP socket
	s.conn.Close()
}

// handleIncomingPacket processes a single UDP packet
func (s *Server) handleIncomingPacket(data []byte, addr *net.UDPAddr) {
	if len(data) < 1 {
		return
	}

	connKey := addr.String()

	// Find or create connection
	s.mutex.Lock()
	conn, exists := s.connections[connKey]
	if !exists {
		// Check connection limit
		if len(s.connections) >= s.config.MaxConnections {
			s.mutex.Unlock()
			return // Drop packet if at capacity
		}

		conn = NewConnection(addr, s.conn, s.handler, s.config)
		conn.StartRetransmitLoop()
		s.connections[connKey] = conn
	}
	s.mutex.Unlock()

	// Process packet
	conn.ProcessPacket(data)
}

// connectionManager handles connection cleanup and maintenance
func (s *Server) connectionManager() {
	ticker := time.NewTicker(5 * time.Second)
	defer ticker.Stop()

	for range ticker.C {
		if !s.running {
			return
		}

		// Clean up timed out connections
		s.cleanupTimedOutConnections()
	}
}

// cleanupTimedOutConnections removes connections that have timed out
func (s *Server) cleanupTimedOutConnections() {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	for key, conn := range s.connections {
		if conn.IsTimedOut() {
			conn.Close()
			delete(s.connections, key)
		}
	}
}

// GetConnectionCount returns the number of active connections
func (s *Server) GetConnectionCount() int {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return len(s.connections)
}

// GetConnection returns a connection by address string
func (s *Server) GetConnection(addr string) *Connection {
	s.mutex.RLock()
	defer s.mutex.RUnlock()
	return s.connections[addr]
}

// GetAllConnections returns a snapshot of all active connections
func (s *Server) GetAllConnections() []*Connection {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	connections := make([]*Connection, 0, len(s.connections))
	for _, conn := range s.connections {
		connections = append(connections, conn)
	}
	return connections
}

// BroadcastPacket sends a packet to all connected clients
func (s *Server) BroadcastPacket(packet *ApplicationPacket) {
	connections := s.GetAllConnections()
	for _, conn := range connections {
		if conn.GetState() == StateEstablished {
			conn.SendPacket(packet)
		}
	}
}

// DisconnectClient forcibly disconnects a client by address
func (s *Server) DisconnectClient(addr string) bool {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if conn, exists := s.connections[addr]; exists {
		conn.Close()
		delete(s.connections, addr)
		return true
	}
	return false
}

// ServerStats contains server runtime statistics
type ServerStats struct {
	ConnectionCount int           // Current number of connections
	MaxConnections  int           // Maximum allowed connections
	Running         bool          // Whether server is running
	Timeout         time.Duration // Connection timeout setting
}

// GetStats returns server statistics
func (s *Server) GetStats() ServerStats {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	return ServerStats{
		ConnectionCount: len(s.connections),
		MaxConnections:  s.config.MaxConnections,
		Running:         s.running,
		Timeout:         s.config.Timeout,
	}
}

// SetConnectionLimit updates the maximum connection limit
func (s *Server) SetConnectionLimit(limit int) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.config.MaxConnections = limit
}

// SetTimeout updates the connection timeout duration
func (s *Server) SetTimeout(timeout time.Duration) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.config.Timeout = timeout
}
