package main

import (
	"eq2emu/internal/packets"
	"eq2emu/internal/udp"
	"fmt"
	"log"
	"sync"
	"time"
)

// LoginServer manages the main login server functionality
type LoginServer struct {
	config      *Config
	server      *udp.Server
	database    *Database
	worldList   *WorldList
	webServer   *WebServer
	clients     map[string]*LoginClient
	clientMutex sync.RWMutex

	// Statistics
	stats struct {
		ConnectionCount  int32
		LoginAttempts    int32
		SuccessfulLogins int32
		StartTime        time.Time
	}
}

// NewLoginServer creates a new login server instance
func NewLoginServer(config *Config) (*LoginServer, error) {
	ls := &LoginServer{
		config:  config,
		clients: make(map[string]*LoginClient),
	}

	ls.stats.StartTime = time.Now()

	// Initialize packet definitions
	log.Printf("Loaded %d packet definitions", packets.GetPacketCount())

	// Initialize database
	db, err := NewDatabase(config.Database)
	if err != nil {
		return nil, fmt.Errorf("database initialization failed: %w", err)
	}
	ls.database = db

	// Initialize world list
	ls.worldList = NewWorldList(db)

	// Create UDP server with login packet handler
	udpConfig := udp.DefaultConfig()
	udpConfig.MaxConnections = config.MaxConnections
	udpConfig.Timeout = time.Duration(config.TimeoutSeconds) * time.Second
	udpConfig.EnableCompression = config.EnableCompression
	udpConfig.EnableEncryption = config.EnableEncryption

	server, err := udp.NewServer(fmt.Sprintf(":%d", config.Port), ls.handlePacket, udpConfig)
	if err != nil {
		return nil, fmt.Errorf("UDP server creation failed: %w", err)
	}
	ls.server = server

	// Initialize web server if configured
	if config.WebServer.Enabled {
		webServer, err := NewWebServer(config.WebServer, ls)
		if err != nil {
			log.Printf("Web server initialization failed: %v", err)
		} else {
			ls.webServer = webServer
		}
	}

	return ls, nil
}

// Start begins accepting connections and processing packets
func (ls *LoginServer) Start() error {
	log.Println("Starting login server components...")

	// Start world list monitoring
	go ls.worldList.Start()

	// Start web server if configured
	if ls.webServer != nil {
		go ls.webServer.Start()
	}

	// Start UDP server
	return ls.server.Start()
}

// Stop gracefully shuts down the server
func (ls *LoginServer) Stop() {
	log.Println("Stopping login server...")

	if ls.webServer != nil {
		ls.webServer.Stop()
	}

	ls.worldList.Stop()
	ls.server.Stop()
	ls.database.Close()
}

// handlePacket processes incoming packets from clients
func (ls *LoginServer) handlePacket(conn *udp.Connection, packet *udp.ApplicationPacket) {
	clientKey := conn.GetSessionID()

	// Get or create client
	ls.clientMutex.Lock()
	client, exists := ls.clients[fmt.Sprintf("%d", clientKey)]
	if !exists {
		client = NewLoginClient(conn, ls)
		ls.clients[fmt.Sprintf("%d", clientKey)] = client
	}
	ls.clientMutex.Unlock()

	// Process packet
	client.ProcessPacket(packet)
}

// RemoveClient removes a client from the active clients list
func (ls *LoginServer) RemoveClient(sessionID string) {
	ls.clientMutex.Lock()
	delete(ls.clients, sessionID)
	ls.clientMutex.Unlock()
}

// UpdateStats updates server statistics
func (ls *LoginServer) UpdateStats() {
	ls.clientMutex.RLock()
	ls.stats.ConnectionCount = int32(len(ls.clients))
	ls.clientMutex.RUnlock()

	// Update world server statistics
	ls.worldList.UpdateStats()

	// Clean up old database entries
	ls.database.CleanupOldEntries()

	log.Printf("Stats: %d connections, %d login attempts, %d successful logins",
		ls.stats.ConnectionCount, ls.stats.LoginAttempts, ls.stats.SuccessfulLogins)
}

// CleanupStaleConnections removes inactive connections
func (ls *LoginServer) CleanupStaleConnections() {
	var staleClients []string

	ls.clientMutex.RLock()
	for sessionID, client := range ls.clients {
		if client.IsStale() {
			staleClients = append(staleClients, sessionID)
		}
	}
	ls.clientMutex.RUnlock()

	ls.clientMutex.Lock()
	for _, sessionID := range staleClients {
		if client, exists := ls.clients[sessionID]; exists {
			client.Disconnect()
			delete(ls.clients, sessionID)
		}
	}
	ls.clientMutex.Unlock()

	if len(staleClients) > 0 {
		log.Printf("Cleaned up %d stale connections", len(staleClients))
	}
}

// GetStats returns current server statistics
func (ls *LoginServer) GetStats() map[string]any {
	ls.clientMutex.RLock()
	connectionCount := len(ls.clients)
	ls.clientMutex.RUnlock()

	return map[string]any{
		"connection_count":   connectionCount,
		"login_attempts":     ls.stats.LoginAttempts,
		"successful_logins":  ls.stats.SuccessfulLogins,
		"uptime_seconds":     int(time.Since(ls.stats.StartTime).Seconds()),
		"world_server_count": ls.worldList.GetActiveCount(),
	}
}

// IncrementLoginAttempts atomically increments login attempt counter
func (ls *LoginServer) IncrementLoginAttempts() {
	ls.stats.LoginAttempts++
}

// IncrementSuccessfulLogins atomically increments successful login counter
func (ls *LoginServer) IncrementSuccessfulLogins() {
	ls.stats.SuccessfulLogins++
}

// GetDatabase returns the database instance
func (ls *LoginServer) GetDatabase() *Database {
	return ls.database
}

// GetWorldList returns the world list instance
func (ls *LoginServer) GetWorldList() *WorldList {
	return ls.worldList
}

// GetConfig returns the server configuration
func (ls *LoginServer) GetConfig() *Config {
	return ls.config
}
