package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"sync"
	"time"

	"eq2emu/internal/database"
	"eq2emu/internal/udp"
)

// WorldTime represents the in-game time
type WorldTime struct {
	Year   int32 `json:"year"`
	Month  int32 `json:"month"`
	Day    int32 `json:"day"`
	Hour   int32 `json:"hour"`
	Minute int32 `json:"minute"`
}

// WorldConfig holds all world server configuration
type WorldConfig struct {
	// Network settings
	ListenAddr string `json:"listen_addr"`
	ListenPort int    `json:"listen_port"`
	MaxClients int    `json:"max_clients"`
	BufferSize int    `json:"buffer_size"`

	// Web server settings
	WebAddr     string `json:"web_addr"`
	WebPort     int    `json:"web_port"`
	CertFile    string `json:"cert_file"`
	KeyFile     string `json:"key_file"`
	KeyPassword string `json:"key_password"`
	WebUser     string `json:"web_user"`
	WebPassword string `json:"web_password"`

	// Database settings
	DatabasePath string `json:"database_path"`

	// Game settings
	XPRate       float64 `json:"xp_rate"`
	TSXPRate     float64 `json:"ts_xp_rate"`
	VitalityRate float64 `json:"vitality_rate"`

	// Server settings
	LogLevel     string `json:"log_level"`
	ThreadedLoad bool   `json:"threaded_load"`
	WorldLocked  bool   `json:"world_locked"`
	IsPrimary    bool   `json:"is_primary"`

	// Login server settings
	LoginServers []LoginServerInfo `json:"login_servers"`

	// Peer server settings
	PeerServers  []PeerServerInfo `json:"peer_servers"`
	PeerPriority int              `json:"peer_priority"`
}

// LoginServerInfo represents login server connection details
type LoginServerInfo struct {
	Address  string `json:"address"`
	Port     int    `json:"port"`
	Account  string `json:"account"`
	Password string `json:"password"`
}

// PeerServerInfo represents peer server connection details
type PeerServerInfo struct {
	Address string `json:"address"`
	Port    int    `json:"port"`
}

// ClientInfo represents a connected client
type ClientInfo struct {
	ID          int32     `json:"id"`
	AccountID   int32     `json:"account_id"`
	CharacterID int32     `json:"character_id"`
	Name        string    `json:"name"`
	ZoneID      int32     `json:"zone_id"`
	ConnectedAt time.Time `json:"connected_at"`
	LastActive  time.Time `json:"last_active"`
	IPAddress   string    `json:"ip_address"`
}

// ZoneInfo represents zone server information
type ZoneInfo struct {
	ID          int32  `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	PlayerCount int32  `json:"player_count"`
	MaxPlayers  int32  `json:"max_players"`
	IsShutdown  bool   `json:"is_shutdown"`
	Address     string `json:"address"`
	Port        int    `json:"port"`
}

// ServerStats holds server statistics
type ServerStats struct {
	StartTime        time.Time `json:"start_time"`
	ClientCount      int32     `json:"client_count"`
	ZoneCount        int32     `json:"zone_count"`
	TotalConnections int64     `json:"total_connections"`
	PacketsProcessed int64     `json:"packets_processed"`
	DataLoaded       bool      `json:"data_loaded"`
	ItemsLoaded      bool      `json:"items_loaded"`
	SpellsLoaded     bool      `json:"spells_loaded"`
	QuestsLoaded     bool      `json:"quests_loaded"`
}

// World represents the main world server
type World struct {
	config *WorldConfig
	db     *database.DB

	// Network components
	udpServer *udp.Server
	webServer *http.Server

	// Game state
	worldTime    WorldTime
	worldTimeMux sync.RWMutex

	// Client management
	clients    map[int32]*ClientInfo
	clientsMux sync.RWMutex

	// Zone management
	zones    map[int32]*ZoneInfo
	zonesMux sync.RWMutex

	// Statistics
	stats    ServerStats
	statsMux sync.RWMutex

	// Control
	ctx        context.Context
	cancel     context.CancelFunc
	shutdownWg *sync.WaitGroup

	// Timers
	timeTickTimer   *time.Ticker
	saveTimer       *time.Ticker
	vitalityTimer   *time.Ticker
	statsTimer      *time.Ticker
	watchdogTimer   *time.Ticker
	loginCheckTimer *time.Ticker

	// Loading state
	loadingMux   sync.RWMutex
	itemsLoaded  bool
	spellsLoaded bool
	questsLoaded bool
	traitsLoaded bool
	dataLoaded   bool
}

// NewWorld creates a new world server instance
func NewWorld(config *WorldConfig) (*World, error) {
	ctx, cancel := context.WithCancel(context.Background())

	db, err := database.Open(config.DatabasePath)
	if err != nil {
		cancel()
		return nil, fmt.Errorf("failed to open database: %w", err)
	}

	w := &World{
		config:     config,
		db:         db,
		ctx:        ctx,
		cancel:     cancel,
		shutdownWg: &sync.WaitGroup{},
		clients:    make(map[int32]*ClientInfo),
		zones:      make(map[int32]*ZoneInfo),
		stats: ServerStats{
			StartTime: time.Now(),
		},
	}

	// Initialize world time from database
	if err := w.loadWorldTime(); err != nil {
		log.Printf("Warning: failed to load world time: %v", err)
		w.setDefaultWorldTime()
	}

	return w, nil
}

// Initialize sets up all world server components
func (w *World) Initialize() error {
	log.Println("Loading System Data...")

	// Initialize database schema
	if err := w.initializeDatabase(); err != nil {
		return fmt.Errorf("database initialization failed: %w", err)
	}

	// Load game data (threaded or sequential)
	if w.config.ThreadedLoad {
		log.Println("Using threaded loading of static data...")
		if err := w.loadGameDataThreaded(); err != nil {
			return fmt.Errorf("threaded game data loading failed: %w", err)
		}
	} else {
		if err := w.loadGameData(); err != nil {
			return fmt.Errorf("game data loading failed: %w", err)
		}
	}

	// Setup UDP server for game connections
	if err := w.setupUDPServer(); err != nil {
		return fmt.Errorf("UDP server setup failed: %w", err)
	}

	// Setup web server for admin/API
	if err := w.setupWebServer(); err != nil {
		return fmt.Errorf("web server setup failed: %w", err)
	}

	// Initialize timers
	w.initializeTimers()

	log.Println("World Server initialization complete")
	return nil
}

// Run starts the world server main loop
func (w *World) Run() error {
	// Start background processes
	w.shutdownWg.Add(6)
	go w.processTimeUpdates()
	go w.processSaveOperations()
	go w.processVitalityUpdates()
	go w.processStatsUpdates()
	go w.processWatchdog()
	go w.processLoginCheck()

	// Start network servers
	if w.udpServer != nil {
		go func() {
			if err := w.udpServer.Start(); err != nil {
				log.Printf("UDP server error: %v", err)
			}
		}()
	}

	// Start web server
	w.startWebServer()

	log.Printf("World Server running on UDP %s:%d, Web %s:%d",
		w.config.ListenAddr, w.config.ListenPort,
		w.config.WebAddr, w.config.WebPort)

	// Wait for shutdown signal
	<-w.ctx.Done()

	return w.shutdown()
}

// Shutdown gracefully stops the world server
func (w *World) Shutdown() {
	log.Println("Initiating World Server shutdown...")
	w.cancel()
}

// setupUDPServer initializes the UDP server for game client connections
func (w *World) setupUDPServer() error {
	handler := func(conn *udp.Connection, packet *udp.ApplicationPacket) {
		w.handleGamePacket(conn, packet)
	}

	config := udp.DefaultConfig()
	config.MaxConnections = w.config.MaxClients
	config.BufferSize = w.config.BufferSize
	config.EnableCompression = true
	config.EnableEncryption = true

	addr := fmt.Sprintf("%s:%d", w.config.ListenAddr, w.config.ListenPort)
	server, err := udp.NewServer(addr, handler, config)
	if err != nil {
		return err
	}

	w.udpServer = server
	return nil
}

// initializeTimers sets up all periodic timers
func (w *World) initializeTimers() {
	w.timeTickTimer = time.NewTicker(5 * time.Second)    // Game time updates
	w.saveTimer = time.NewTicker(5 * time.Minute)        // Save operations
	w.vitalityTimer = time.NewTicker(1 * time.Hour)      // Vitality updates
	w.statsTimer = time.NewTicker(1 * time.Minute)       // Statistics updates
	w.watchdogTimer = time.NewTicker(30 * time.Second)   // Watchdog checks
	w.loginCheckTimer = time.NewTicker(30 * time.Second) // Login server check
}

// Background processes

// processTimeUpdates handles game world time progression
func (w *World) processTimeUpdates() {
	defer w.shutdownWg.Done()

	for {
		select {
		case <-w.ctx.Done():
			return
		case <-w.timeTickTimer.C:
			w.updateWorldTime()
		}
	}
}

// processSaveOperations handles periodic save operations
func (w *World) processSaveOperations() {
	defer w.shutdownWg.Done()

	for {
		select {
		case <-w.ctx.Done():
			return
		case <-w.saveTimer.C:
			w.saveWorldState()
		}
	}
}

// processVitalityUpdates handles vitality system updates
func (w *World) processVitalityUpdates() {
	defer w.shutdownWg.Done()

	for {
		select {
		case <-w.ctx.Done():
			return
		case <-w.vitalityTimer.C:
			w.updateVitality()
		}
	}
}

// processStatsUpdates handles statistics collection
func (w *World) processStatsUpdates() {
	defer w.shutdownWg.Done()

	for {
		select {
		case <-w.ctx.Done():
			return
		case <-w.statsTimer.C:
			w.updateStatistics()
		}
	}
}

// processWatchdog handles connection timeouts and cleanup
func (w *World) processWatchdog() {
	defer w.shutdownWg.Done()

	for {
		select {
		case <-w.ctx.Done():
			return
		case <-w.watchdogTimer.C:
			w.cleanupInactiveClients()
			w.cleanupTimeoutConnections()
		}
	}
}

// processLoginCheck handles login server connectivity
func (w *World) processLoginCheck() {
	defer w.shutdownWg.Done()

	for {
		select {
		case <-w.ctx.Done():
			return
		case <-w.loginCheckTimer.C:
			w.checkLoginServers()
		}
	}
}

// Game packet handling
func (w *World) handleGamePacket(conn *udp.Connection, packet *udp.ApplicationPacket) {
	// Update connection activity
	w.updateConnectionActivity(conn)

	// Route packet based on opcode
	switch packet.Opcode {
	case 0x2000: // Login request
		w.handleLoginRequest(conn, packet)
	case 0x0020: // Zone change request
		w.handleZoneChange(conn, packet)
	case 0x0080: // Client command
		w.handleClientCommand(conn, packet)
	case 0x01F0: // Chat message
		w.handleChatMessage(conn, packet)
	default:
		// @TODO: Implement comprehensive packet routing
		log.Printf("Unhandled packet opcode: 0x%04X, size: %d", packet.Opcode, len(packet.Data))
	}

	// Update packet statistics
	w.statsMux.Lock()
	w.stats.PacketsProcessed++
	w.statsMux.Unlock()
}

// Game packet handlers
func (w *World) handleLoginRequest(conn *udp.Connection, packet *udp.ApplicationPacket) {
	// @TODO: Parse login request packet
	// @TODO: Validate credentials with login server
	// @TODO: Create client session
	// @TODO: Send login response

	log.Printf("Login request from connection %d", conn.GetSessionID())
}

func (w *World) handleZoneChange(conn *udp.Connection, packet *udp.ApplicationPacket) {
	// @TODO: Parse zone change request
	// @TODO: Validate zone transfer
	// @TODO: Coordinate with zone servers
	// @TODO: Send zone change response

	log.Printf("Zone change request from connection %d", conn.GetSessionID())
}

func (w *World) handleClientCommand(conn *udp.Connection, packet *udp.ApplicationPacket) {
	// @TODO: Parse client command packet
	// @TODO: Process administrative commands
	// @TODO: Route to appropriate handlers

	log.Printf("Client command from connection %d", conn.GetSessionID())
}

func (w *World) handleChatMessage(conn *udp.Connection, packet *udp.ApplicationPacket) {
	// @TODO: Parse chat message packet
	// @TODO: Handle channel routing
	// @TODO: Apply filters and permissions
	// @TODO: Broadcast to appropriate recipients

	log.Printf("Chat message from connection %d", conn.GetSessionID())
}

// Game state management
func (w *World) updateWorldTime() {
	w.worldTimeMux.Lock()
	defer w.worldTimeMux.Unlock()

	w.worldTime.Minute++
	if w.worldTime.Minute >= 60 {
		w.worldTime.Minute = 0
		w.worldTime.Hour++
		if w.worldTime.Hour >= 24 {
			w.worldTime.Hour = 0
			w.worldTime.Day++
			if w.worldTime.Day >= 30 {
				w.worldTime.Day = 0
				w.worldTime.Month++
				if w.worldTime.Month >= 12 {
					w.worldTime.Month = 0
					w.worldTime.Year++
				}
			}
		}
	}

	// @TODO: Broadcast time update to all zones/clients
	// @TODO: Save time to database periodically
}

func (w *World) saveWorldState() {
	// @TODO: Save world time to database
	// @TODO: Save player data
	// @TODO: Save guild data
	// @TODO: Save zone states
	// @TODO: Save server statistics

	log.Println("Saving world state...")
}

func (w *World) updateVitality() {
	// @TODO: Update player vitality for offline/resting players
	// @TODO: Broadcast vitality updates to zones
	// @TODO: Apply vitality bonuses

	log.Println("Updating vitality...")
}

func (w *World) updateStatistics() {
	w.statsMux.Lock()
	defer w.statsMux.Unlock()

	// Update client count
	w.clientsMux.RLock()
	w.stats.ClientCount = int32(len(w.clients))
	w.clientsMux.RUnlock()

	// Update zone count
	w.zonesMux.RLock()
	w.stats.ZoneCount = int32(len(w.zones))
	w.zonesMux.RUnlock()

	// Update loading status
	w.loadingMux.RLock()
	w.stats.DataLoaded = w.dataLoaded
	w.stats.ItemsLoaded = w.itemsLoaded
	w.stats.SpellsLoaded = w.spellsLoaded
	w.stats.QuestsLoaded = w.questsLoaded
	w.loadingMux.RUnlock()
}

func (w *World) cleanupInactiveClients() {
	w.clientsMux.Lock()
	defer w.clientsMux.Unlock()

	timeout := time.Now().Add(-5 * time.Minute)
	for id, client := range w.clients {
		if client.LastActive.Before(timeout) {
			log.Printf("Removing inactive client %d (%s)", id, client.Name)
			delete(w.clients, id)
		}
	}
}

func (w *World) cleanupTimeoutConnections() {
	// @TODO: Clean up timed out UDP connections
	// @TODO: Update connection statistics
}

func (w *World) checkLoginServers() {
	// @TODO: Check connectivity to login servers
	// @TODO: Attempt reconnection if disconnected
	// @TODO: Update server status
}

func (w *World) updateConnectionActivity(conn *udp.Connection) {
	sessionID := conn.GetSessionID()

	w.clientsMux.Lock()
	if client, exists := w.clients[int32(sessionID)]; exists {
		client.LastActive = time.Now()
	}
	w.clientsMux.Unlock()
}

// Database operations
func (w *World) initializeDatabase() error {
	// @TODO: Create/update database schema tables
	// @TODO: Initialize character tables
	// @TODO: Initialize guild tables
	// @TODO: Initialize item tables
	// @TODO: Initialize zone tables

	log.Println("Database schema initialized")
	return nil
}

func (w *World) loadGameData() error {
	log.Println("Loading game data sequentially...")

	// Load items
	log.Println("Loading items...")
	if err := w.loadItems(); err != nil {
		return fmt.Errorf("failed to load items: %w", err)
	}

	// Load spells
	log.Println("Loading spells...")
	if err := w.loadSpells(); err != nil {
		return fmt.Errorf("failed to load spells: %w", err)
	}

	// Load quests
	log.Println("Loading quests...")
	if err := w.loadQuests(); err != nil {
		return fmt.Errorf("failed to load quests: %w", err)
	}

	// Load additional data
	if err := w.loadTraits(); err != nil {
		return fmt.Errorf("failed to load traits: %w", err)
	}

	if err := w.loadNPCs(); err != nil {
		return fmt.Errorf("failed to load NPCs: %w", err)
	}

	if err := w.loadZones(); err != nil {
		return fmt.Errorf("failed to load zones: %w", err)
	}

	w.loadingMux.Lock()
	w.dataLoaded = true
	w.loadingMux.Unlock()

	log.Println("Game data loading complete")
	return nil
}

func (w *World) loadGameDataThreaded() error {
	log.Println("Loading game data with threads...")

	var wg sync.WaitGroup
	errChan := make(chan error, 10)

	// Load items in thread
	wg.Add(1)
	go func() {
		defer wg.Done()
		log.Println("Loading items...")
		if err := w.loadItems(); err != nil {
			errChan <- fmt.Errorf("failed to load items: %w", err)
			return
		}
		w.loadingMux.Lock()
		w.itemsLoaded = true
		w.loadingMux.Unlock()
		log.Println("Items loaded")
	}()

	// Load spells in thread
	wg.Add(1)
	go func() {
		defer wg.Done()
		log.Println("Loading spells...")
		if err := w.loadSpells(); err != nil {
			errChan <- fmt.Errorf("failed to load spells: %w", err)
			return
		}
		w.loadingMux.Lock()
		w.spellsLoaded = true
		w.loadingMux.Unlock()
		log.Println("Spells loaded")
	}()

	// Load quests in thread
	wg.Add(1)
	go func() {
		defer wg.Done()
		log.Println("Loading quests...")
		if err := w.loadQuests(); err != nil {
			errChan <- fmt.Errorf("failed to load quests: %w", err)
			return
		}
		w.loadingMux.Lock()
		w.questsLoaded = true
		w.loadingMux.Unlock()
		log.Println("Quests loaded")
	}()

	// Wait for completion
	go func() {
		wg.Wait()
		close(errChan)
	}()

	// Check for errors
	for err := range errChan {
		if err != nil {
			return err
		}
	}

	// Load additional data sequentially
	if err := w.loadTraits(); err != nil {
		return fmt.Errorf("failed to load traits: %w", err)
	}

	if err := w.loadNPCs(); err != nil {
		return fmt.Errorf("failed to load NPCs: %w", err)
	}

	if err := w.loadZones(); err != nil {
		return fmt.Errorf("failed to load zones: %w", err)
	}

	// Wait for threaded loads to complete
	for !w.isDataLoaded() {
		time.Sleep(100 * time.Millisecond)
	}

	w.loadingMux.Lock()
	w.dataLoaded = true
	w.loadingMux.Unlock()

	log.Println("Threaded game data loading complete")
	return nil
}

// Data loading functions
func (w *World) loadItems() error {
	// @TODO: Load items from database
	// @TODO: Build item lookup tables
	// @TODO: Load item templates
	// @TODO: Initialize item factories

	return nil
}

func (w *World) loadSpells() error {
	// @TODO: Load spells from database
	// @TODO: Build spell lookup tables
	// @TODO: Load spell effects
	// @TODO: Initialize spell system

	return nil
}

func (w *World) loadQuests() error {
	// @TODO: Load quests from database
	// @TODO: Build quest lookup tables
	// @TODO: Load quest rewards
	// @TODO: Initialize quest system

	return nil
}

func (w *World) loadTraits() error {
	// @TODO: Load traits from database
	// @TODO: Build trait trees
	// @TODO: Initialize trait system

	return nil
}

func (w *World) loadNPCs() error {
	// @TODO: Load NPCs from database
	// @TODO: Load NPC templates
	// @TODO: Load NPC spawn data

	return nil
}

func (w *World) loadZones() error {
	// @TODO: Load zone definitions
	// @TODO: Load zone spawn points
	// @TODO: Initialize zone management

	return nil
}

func (w *World) loadWorldTime() error {
	// @TODO: Load world time from database
	w.worldTime = WorldTime{
		Year:   3800,
		Month:  0,
		Day:    0,
		Hour:   8,
		Minute: 30,
	}
	return nil
}

func (w *World) setDefaultWorldTime() {
	w.worldTimeMux.Lock()
	defer w.worldTimeMux.Unlock()

	w.worldTime = WorldTime{
		Year:   3800,
		Month:  0,
		Day:    0,
		Hour:   8,
		Minute: 30,
	}
}

func (w *World) isDataLoaded() bool {
	w.loadingMux.RLock()
	defer w.loadingMux.RUnlock()

	if w.config.ThreadedLoad {
		return w.itemsLoaded && w.spellsLoaded && w.questsLoaded && w.traitsLoaded
	}
	return w.dataLoaded
}

// Cleanup and shutdown
func (w *World) shutdown() error {
	log.Println("Shutting down World Server...")

	// Stop timers
	if w.timeTickTimer != nil {
		w.timeTickTimer.Stop()
	}
	if w.saveTimer != nil {
		w.saveTimer.Stop()
	}
	if w.vitalityTimer != nil {
		w.vitalityTimer.Stop()
	}
	if w.statsTimer != nil {
		w.statsTimer.Stop()
	}
	if w.watchdogTimer != nil {
		w.watchdogTimer.Stop()
	}
	if w.loginCheckTimer != nil {
		w.loginCheckTimer.Stop()
	}

	// Stop network servers
	if err := w.stopWebServer(); err != nil {
		log.Printf("Error stopping web server: %v", err)
	}

	if w.udpServer != nil {
		w.udpServer.Stop()
	}

	// Wait for background processes
	w.shutdownWg.Wait()

	// Save final state
	w.saveWorldState()

	// Close database
	if w.db != nil {
		w.db.Close()
	}

	log.Println("World Server shutdown complete")
	return nil
}
