package main

import (
	"fmt"
	"log"
	"sync"
	"time"
)

// WorldServer represents a game world server
type WorldServer struct {
	ID              int32
	Name            string
	Description     string
	IPAddress       string
	Port            int
	Status          string
	Population      int32
	PopulationLevel uint8
	Locked          bool
	Hidden          bool
	Online          bool
	CreatedDate     time.Time
	LastUpdate      time.Time
}

// WorldServerStats holds runtime statistics
type WorldServerStats struct {
	Population    int32
	ZonesActive   int32
	PlayersOnline int32
	UptimeSeconds int64
}

// WorldList manages all world servers
type WorldList struct {
	servers      map[int32]*WorldServer
	mutex        sync.RWMutex
	database     *Database
	updateTicker *time.Ticker
	stopChan     chan struct{}
}

// NewWorldList creates a new world list manager
func NewWorldList(database *Database) *WorldList {
	return &WorldList{
		servers:  make(map[int32]*WorldServer),
		database: database,
		stopChan: make(chan struct{}),
	}
}

// Start begins world server monitoring
func (wl *WorldList) Start() {
	log.Println("Starting world list monitoring...")

	// Load world servers from database
	if err := wl.LoadFromDatabase(); err != nil {
		log.Printf("Failed to load world servers: %v", err)
	}

	// Start periodic updates
	wl.updateTicker = time.NewTicker(30 * time.Second)

	go func() {
		for {
			select {
			case <-wl.updateTicker.C:
				wl.UpdateStats()
			case <-wl.stopChan:
				return
			}
		}
	}()
}

// Stop shuts down world server monitoring
func (wl *WorldList) Stop() {
	log.Println("Stopping world list monitoring...")

	if wl.updateTicker != nil {
		wl.updateTicker.Stop()
	}

	close(wl.stopChan)
}

// LoadFromDatabase loads world servers from the database
func (wl *WorldList) LoadFromDatabase() error {
	servers, err := wl.database.GetWorldServers()
	if err != nil {
		return fmt.Errorf("failed to load world servers: %w", err)
	}

	wl.mutex.Lock()
	defer wl.mutex.Unlock()

	// Clear existing servers
	wl.servers = make(map[int32]*WorldServer)

	// Add loaded servers
	for _, server := range servers {
		wl.servers[server.ID] = server
		log.Printf("Loaded world server: %s (ID: %d)", server.Name, server.ID)
	}

	log.Printf("Loaded %d world servers", len(servers))
	return nil
}

// GetActiveWorlds returns all online world servers
func (wl *WorldList) GetActiveWorlds() []*WorldServer {
	wl.mutex.RLock()
	defer wl.mutex.RUnlock()

	var active []*WorldServer
	for _, server := range wl.servers {
		if server.Online && !server.Hidden {
			active = append(active, server)
		}
	}

	return active
}

// GetWorld returns a specific world server by ID
func (wl *WorldList) GetWorld(id int32) *WorldServer {
	wl.mutex.RLock()
	defer wl.mutex.RUnlock()

	return wl.servers[id]
}

// GetActiveCount returns the number of online world servers
func (wl *WorldList) GetActiveCount() int {
	wl.mutex.RLock()
	defer wl.mutex.RUnlock()

	count := 0
	for _, server := range wl.servers {
		if server.Online {
			count++
		}
	}

	return count
}

// UpdateServerStatus updates a world server's status
func (wl *WorldList) UpdateServerStatus(id int32, online bool, population int32, locked bool) {
	wl.mutex.Lock()
	defer wl.mutex.Unlock()

	server, exists := wl.servers[id]
	if !exists {
		log.Printf("Attempted to update unknown server ID: %d", id)
		return
	}

	server.Online = online
	server.Population = population
	server.Locked = locked
	server.LastUpdate = time.Now()

	// Update population level
	server.PopulationLevel = wl.calculatePopulationLevel(population)

	if online {
		server.Status = "online"
	} else {
		server.Status = "offline"
	}

	log.Printf("Updated server %s: online=%t, population=%d, locked=%t",
		server.Name, online, population, locked)
}

// calculatePopulationLevel converts population to display level
func (wl *WorldList) calculatePopulationLevel(population int32) uint8 {
	switch {
	case population >= 1000:
		return 3 // Full
	case population >= 500:
		return 2 // High
	case population >= 100:
		return 1 // Medium
	default:
		return 0 // Low
	}
}

// UpdateStats updates world server statistics
func (wl *WorldList) UpdateStats() {
	wl.mutex.RLock()
	servers := make([]*WorldServer, 0, len(wl.servers))
	for _, server := range wl.servers {
		servers = append(servers, server)
	}
	wl.mutex.RUnlock()

	// Update statistics for each server
	for _, server := range servers {
		if server.Online {
			stats := &WorldServerStats{
				Population:    server.Population,
				ZonesActive:   0, // Would be updated by world server
				PlayersOnline: server.Population,
				UptimeSeconds: int64(time.Since(server.LastUpdate).Seconds()),
			}

			if err := wl.database.UpdateWorldServerStats(server.ID, stats); err != nil {
				log.Printf("Failed to update stats for server %d: %v", server.ID, err)
			}
		}
	}
}

// SendPlayRequest sends a character play request to a world server
func (wl *WorldList) SendPlayRequest(world *WorldServer, accountID, charID int32) error {
	// In a real implementation, this would establish communication with the world server
	// and send the play request. For now, we'll simulate the response.

	log.Printf("Sending play request to world server %s for account %d, character %d",
		world.Name, accountID, charID)

	// Simulate world server response after a short delay
	go func() {
		time.Sleep(100 * time.Millisecond)

		// For demonstration, we'll always succeed
		// In reality, the world server would validate the character and respond
		accessKey := generateAccessKey()

		// This would normally come from the world server's response
		wl.HandlePlayResponse(world.ID, accountID, charID, true,
			world.IPAddress, world.Port, accessKey)
	}()

	return nil
}

// HandlePlayResponse processes a play response from a world server
func (wl *WorldList) HandlePlayResponse(worldID, accountID, charID int32,
	success bool, ipAddress string, port int, accessKey int32) {

	// Find the client that requested this
	// This is simplified - in reality you'd track pending requests
	log.Printf("Play response from world %d: success=%t, access_key=%d",
		worldID, success, accessKey)

	// Send response to appropriate client
	// This would need to be implemented with proper client tracking
}

// generateAccessKey generates a random access key for world server connections
func generateAccessKey() int32 {
	return int32(time.Now().UnixNano() & 0x7FFFFFFF)
}

// AddServer adds a new world server (for dynamic registration)
func (wl *WorldList) AddServer(server *WorldServer) {
	wl.mutex.Lock()
	defer wl.mutex.Unlock()

	wl.servers[server.ID] = server
	log.Printf("Added world server: %s (ID: %d)", server.Name, server.ID)
}

// RemoveServer removes a world server
func (wl *WorldList) RemoveServer(id int32) {
	wl.mutex.Lock()
	defer wl.mutex.Unlock()

	if server, exists := wl.servers[id]; exists {
		delete(wl.servers, id)
		log.Printf("Removed world server: %s (ID: %d)", server.Name, id)
	}
}

// GetServerList returns a formatted server list for client packets
func (wl *WorldList) GetServerList() []byte {
	wl.mutex.RLock()
	defer wl.mutex.RUnlock()

	// Build server list packet data
	data := make([]byte, 0, 1024)

	// Count active servers
	activeCount := 0
	for _, server := range wl.servers {
		if !server.Hidden {
			activeCount++
		}
	}

	// Add server count
	data = append(data, byte(activeCount))

	// Add server data
	for _, server := range wl.servers {
		if server.Hidden {
			continue
		}

		// Server ID (4 bytes)
		serverIDBytes := make([]byte, 4)
		serverIDBytes[0] = byte(server.ID)
		serverIDBytes[1] = byte(server.ID >> 8)
		serverIDBytes[2] = byte(server.ID >> 16)
		serverIDBytes[3] = byte(server.ID >> 24)
		data = append(data, serverIDBytes...)

		// Server name (null-terminated)
		data = append(data, []byte(server.Name)...)
		data = append(data, 0)

		// Server flags
		var flags byte
		if server.Online {
			flags |= 0x01
		}
		if server.Locked {
			flags |= 0x02
		}
		data = append(data, flags)

		// Population level
		data = append(data, server.PopulationLevel)
	}

	return data
}

// GetStats returns world list statistics
func (wl *WorldList) GetStats() map[string]any {
	wl.mutex.RLock()
	defer wl.mutex.RUnlock()

	totalServers := len(wl.servers)
	onlineServers := 0
	totalPopulation := int32(0)

	for _, server := range wl.servers {
		if server.Online {
			onlineServers++
			totalPopulation += server.Population
		}
	}

	return map[string]any{
		"total_servers":    totalServers,
		"online_servers":   onlineServers,
		"offline_servers":  totalServers - onlineServers,
		"total_population": totalPopulation,
	}
}
