package sign

import (
	"fmt"
	"sync"
)

// Manager provides high-level management of the sign system
type Manager struct {
	signs         map[int32]*Sign   // Signs by ID
	signsByZone   map[int32][]*Sign // Signs by zone ID
	signsByWidget map[int32]*Sign   // Signs by widget ID
	database      Database
	logger        Logger
	mutex         sync.RWMutex

	// Statistics
	totalSigns       int64
	signsByType      map[int8]int64 // Sign type -> count
	signInteractions int64
	zoneTransports   int64
	transporterUses  int64
}

// NewManager creates a new sign manager
func NewManager(database Database, logger Logger) *Manager {
	return &Manager{
		signs:         make(map[int32]*Sign),
		signsByZone:   make(map[int32][]*Sign),
		signsByWidget: make(map[int32]*Sign),
		database:      database,
		logger:        logger,
		signsByType:   make(map[int8]int64),
	}
}

// Initialize loads signs from database
func (m *Manager) Initialize() error {
	if m.logger != nil {
		m.logger.LogInfo("Initializing sign manager...")
	}

	// TODO: Load all signs from database when database system is integrated
	// This would typically iterate through all zones and load their signs

	return nil
}

// LoadZoneSigns loads signs for a specific zone
func (m *Manager) LoadZoneSigns(zoneID int32) error {
	if m.database == nil {
		return fmt.Errorf("database is nil")
	}

	signs, err := m.database.LoadSigns(zoneID)
	if err != nil {
		return fmt.Errorf("failed to load signs for zone %d: %w", zoneID, err)
	}

	m.mutex.Lock()
	defer m.mutex.Unlock()

	for _, sign := range signs {
		m.addSignUnsafe(sign)
	}

	if m.logger != nil {
		m.logger.LogInfo("Loaded %d signs for zone %d", len(signs), zoneID)
	}

	return nil
}

// AddSign adds a sign to the manager
func (m *Manager) AddSign(sign *Sign) error {
	if sign == nil {
		return fmt.Errorf("sign is nil")
	}

	// Validate the sign
	if issues := sign.Validate(); len(issues) > 0 {
		return fmt.Errorf("sign validation failed: %v", issues)
	}

	m.mutex.Lock()
	defer m.mutex.Unlock()

	m.addSignUnsafe(sign)

	if m.logger != nil {
		m.logger.LogInfo("Added sign %d (widget %d) of type %d",
			sign.Spawn.GetDatabaseID(), sign.GetWidgetID(), sign.GetSignType())
	}

	return nil
}

// addSignUnsafe adds a sign without locking (internal use)
func (m *Manager) addSignUnsafe(sign *Sign) {
	signID := sign.Spawn.GetDatabaseID()
	widgetID := sign.GetWidgetID()

	// Add to main collection
	m.signs[signID] = sign

	// Add to widget collection
	if widgetID > 0 {
		m.signsByWidget[widgetID] = sign
	}

	// Add to zone collection
	if sign.Spawn != nil {
		zoneID := sign.Spawn.GetZone()
		m.signsByZone[zoneID] = append(m.signsByZone[zoneID], sign)
	}

	// Update statistics
	m.totalSigns++
	m.signsByType[sign.GetSignType()]++
}

// RemoveSign removes a sign from the manager
func (m *Manager) RemoveSign(signID int32) bool {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	sign, exists := m.signs[signID]
	if !exists {
		return false
	}

	// Remove from main collection
	delete(m.signs, signID)

	// Remove from widget collection
	if sign.GetWidgetID() > 0 {
		delete(m.signsByWidget, sign.GetWidgetID())
	}

	// Remove from zone collection
	if sign.Spawn != nil {
		zoneID := sign.Spawn.GetZone()
		if zoneSigns, exists := m.signsByZone[zoneID]; exists {
			for i, zoneSign := range zoneSigns {
				if zoneSign == sign {
					m.signsByZone[zoneID] = append(zoneSigns[:i], zoneSigns[i+1:]...)
					break
				}
			}
		}
	}

	// Update statistics
	m.totalSigns--
	m.signsByType[sign.GetSignType()]--

	if m.logger != nil {
		m.logger.LogInfo("Removed sign %d (widget %d)", signID, sign.GetWidgetID())
	}

	return true
}

// GetSign returns a sign by ID
func (m *Manager) GetSign(signID int32) *Sign {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return m.signs[signID]
}

// GetSignByWidget returns a sign by widget ID
func (m *Manager) GetSignByWidget(widgetID int32) *Sign {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return m.signsByWidget[widgetID]
}

// GetZoneSigns returns all signs in a zone
func (m *Manager) GetZoneSigns(zoneID int32) []*Sign {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	signs := m.signsByZone[zoneID]

	// Return a copy to prevent external modification
	result := make([]*Sign, len(signs))
	copy(result, signs)

	return result
}

// GetSignsByType returns all signs of a specific type
func (m *Manager) GetSignsByType(signType int8) []*Sign {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	var result []*Sign
	for _, sign := range m.signs {
		if sign.GetSignType() == signType {
			result = append(result, sign)
		}
	}

	return result
}

// SaveSign saves a sign to database
func (m *Manager) SaveSign(sign *Sign) error {
	if m.database == nil {
		return fmt.Errorf("database is nil")
	}

	if sign == nil {
		return fmt.Errorf("sign is nil")
	}

	err := m.database.SaveSign(sign)
	if err != nil {
		return fmt.Errorf("failed to save sign: %w", err)
	}

	if m.logger != nil {
		m.logger.LogDebug("Saved sign %d to database", sign.Spawn.GetDatabaseID())
	}

	return nil
}

// HandleSignUse processes sign usage and records statistics
func (m *Manager) HandleSignUse(sign *Sign, client Client, command string) error {
	if sign == nil {
		return fmt.Errorf("sign is nil")
	}

	m.mutex.Lock()
	m.signInteractions++
	m.mutex.Unlock()

	err := sign.HandleUse(client, command)

	// Record specific interaction types for statistics
	if err == nil {
		m.mutex.Lock()
		if sign.IsZoneSign() {
			m.zoneTransports++
		}
		if sign.Spawn != nil && sign.Spawn.GetTransporterID() > 0 {
			m.transporterUses++
		}
		m.mutex.Unlock()
	}

	if m.logger != nil {
		if err != nil {
			m.logger.LogError("Sign %d use failed: %v", sign.Spawn.GetDatabaseID(), err)
		} else {
			m.logger.LogDebug("Sign %d used successfully by character %d",
				sign.Spawn.GetDatabaseID(), client.GetCharacterID())
		}
	}

	return err
}

// GetStatistics returns sign system statistics
func (m *Manager) GetStatistics() map[string]interface{} {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	stats := make(map[string]interface{})
	stats["total_signs"] = m.totalSigns
	stats["sign_interactions"] = m.signInteractions
	stats["zone_transports"] = m.zoneTransports
	stats["transporter_uses"] = m.transporterUses

	// Copy sign type statistics
	typeStats := make(map[int8]int64)
	for signType, count := range m.signsByType {
		typeStats[signType] = count
	}
	stats["signs_by_type"] = typeStats

	// Zone statistics
	zoneStats := make(map[int32]int)
	for zoneID, signs := range m.signsByZone {
		zoneStats[zoneID] = len(signs)
	}
	stats["signs_by_zone"] = zoneStats

	return stats
}

// ResetStatistics resets all statistics
func (m *Manager) ResetStatistics() {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	m.signInteractions = 0
	m.zoneTransports = 0
	m.transporterUses = 0
}

// ValidateAllSigns validates all signs in the system
func (m *Manager) ValidateAllSigns() map[int32][]string {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	issues := make(map[int32][]string)

	for signID, sign := range m.signs {
		if signIssues := sign.Validate(); len(signIssues) > 0 {
			issues[signID] = signIssues
		}
	}

	return issues
}

// GetSignCount returns the total number of signs
func (m *Manager) GetSignCount() int64 {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return m.totalSigns
}

// GetSignTypeCount returns the number of signs of a specific type
func (m *Manager) GetSignTypeCount(signType int8) int64 {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return m.signsByType[signType]
}

// ProcessCommand handles sign-related commands
func (m *Manager) ProcessCommand(command string, args []string) (string, error) {
	switch command {
	case "stats":
		return m.handleStatsCommand(args)
	case "validate":
		return m.handleValidateCommand(args)
	case "list":
		return m.handleListCommand(args)
	case "info":
		return m.handleInfoCommand(args)
	default:
		return "", fmt.Errorf("unknown sign command: %s", command)
	}
}

// handleStatsCommand shows sign system statistics
func (m *Manager) handleStatsCommand(args []string) (string, error) {
	stats := m.GetStatistics()

	result := "Sign System Statistics:\n"
	result += fmt.Sprintf("Total Signs: %d\n", stats["total_signs"])
	result += fmt.Sprintf("Sign Interactions: %d\n", stats["sign_interactions"])
	result += fmt.Sprintf("Zone Transports: %d\n", stats["zone_transports"])
	result += fmt.Sprintf("Transporter Uses: %d\n", stats["transporter_uses"])

	typeStats := stats["signs_by_type"].(map[int8]int64)
	result += fmt.Sprintf("Generic Signs: %d\n", typeStats[SignTypeGeneric])
	result += fmt.Sprintf("Zone Signs: %d\n", typeStats[SignTypeZone])

	return result, nil
}

// handleValidateCommand validates all signs
func (m *Manager) handleValidateCommand(args []string) (string, error) {
	issues := m.ValidateAllSigns()

	if len(issues) == 0 {
		return "All signs are valid.", nil
	}

	result := fmt.Sprintf("Found issues with %d signs:\n", len(issues))
	count := 0
	for signID, signIssues := range issues {
		if count >= 10 { // Limit output
			result += "... (and more)\n"
			break
		}
		result += fmt.Sprintf("Sign %d:\n", signID)
		for _, issue := range signIssues {
			result += fmt.Sprintf("  - %s\n", issue)
		}
		count++
	}

	return result, nil
}

// handleListCommand lists signs
func (m *Manager) handleListCommand(args []string) (string, error) {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	if len(m.signs) == 0 {
		return "No signs loaded.", nil
	}

	result := fmt.Sprintf("Signs (%d):\n", len(m.signs))
	count := 0
	for signID, sign := range m.signs {
		if count >= 20 { // Limit output
			result += "... (and more)\n"
			break
		}

		typeName := "Generic"
		if sign.GetSignType() == SignTypeZone {
			typeName = "Zone"
		}

		result += fmt.Sprintf("  %d: %s (%s, Widget: %d)\n",
			signID, sign.GetSignTitle(), typeName, sign.GetWidgetID())
		count++
	}

	return result, nil
}

// handleInfoCommand shows information about a specific sign
func (m *Manager) handleInfoCommand(args []string) (string, error) {
	if len(args) == 0 {
		return "", fmt.Errorf("sign ID required")
	}

	// Parse sign ID
	var signID int32
	if _, err := fmt.Sscanf(args[0], "%d", &signID); err != nil {
		return "", fmt.Errorf("invalid sign ID: %s", args[0])
	}

	sign := m.GetSign(signID)
	if sign == nil {
		return fmt.Sprintf("Sign %d not found.", signID), nil
	}

	result := fmt.Sprintf("Sign Information:\n")
	result += fmt.Sprintf("ID: %d\n", signID)
	result += fmt.Sprintf("Widget ID: %d\n", sign.GetWidgetID())
	result += fmt.Sprintf("Type: %d\n", sign.GetSignType())
	result += fmt.Sprintf("Title: %s\n", sign.GetSignTitle())
	result += fmt.Sprintf("Description: %s\n", sign.GetSignDescription())
	result += fmt.Sprintf("Language: %d\n", sign.GetLanguage())

	if sign.IsZoneSign() {
		result += fmt.Sprintf("Zone ID: %d\n", sign.GetSignZoneID())
		result += fmt.Sprintf("Zone Coords: (%.2f, %.2f, %.2f)\n",
			sign.GetSignZoneX(), sign.GetSignZoneY(), sign.GetSignZoneZ())
		result += fmt.Sprintf("Zone Heading: %.2f\n", sign.GetSignZoneHeading())
		result += fmt.Sprintf("Distance: %.2f\n", sign.GetSignDistance())
	}

	result += fmt.Sprintf("Include Location: %t\n", sign.GetIncludeLocation())
	result += fmt.Sprintf("Include Heading: %t\n", sign.GetIncludeHeading())

	return result, nil
}

// Shutdown gracefully shuts down the manager
func (m *Manager) Shutdown() {
	if m.logger != nil {
		m.logger.LogInfo("Shutting down sign manager...")
	}

	// Nothing to clean up currently, but placeholder for future cleanup
}
