package appearances

import (
	"fmt"
	"sync"
)

// Manager provides high-level management of the appearance system
type Manager struct {
	appearances *Appearances
	database    Database
	logger      Logger
	mutex       sync.RWMutex

	// Statistics
	totalLookups      int64
	successfulLookups int64
	failedLookups     int64
	cacheHits         int64
	cacheMisses       int64
}

// NewManager creates a new appearance manager
func NewManager(database Database, logger Logger) *Manager {
	return &Manager{
		appearances: NewAppearances(),
		database:    database,
		logger:      logger,
	}
}

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

	if m.database == nil {
		if m.logger != nil {
			m.logger.LogWarning("No database provided, starting with empty appearance list")
		}
		return nil
	}

	appearances, err := m.database.LoadAllAppearances()
	if err != nil {
		return fmt.Errorf("failed to load appearances from database: %w", err)
	}

	for _, appearance := range appearances {
		if err := m.appearances.InsertAppearance(appearance); err != nil {
			if m.logger != nil {
				m.logger.LogError("Failed to insert appearance %d: %v", appearance.GetID(), err)
			}
		}
	}

	if m.logger != nil {
		m.logger.LogInfo("Loaded %d appearances from database", len(appearances))
	}

	return nil
}

// GetAppearances returns the appearances collection
func (m *Manager) GetAppearances() *Appearances {
	return m.appearances
}

// FindAppearanceByID finds an appearance by ID with statistics tracking
func (m *Manager) FindAppearanceByID(id int32) *Appearance {
	m.mutex.Lock()
	m.totalLookups++
	m.mutex.Unlock()

	appearance := m.appearances.FindAppearanceByID(id)

	m.mutex.Lock()
	if appearance != nil {
		m.successfulLookups++
		m.cacheHits++
	} else {
		m.failedLookups++
		m.cacheMisses++
	}
	m.mutex.Unlock()

	if m.logger != nil && appearance == nil {
		m.logger.LogDebug("Appearance lookup failed for ID: %d", id)
	}

	return appearance
}

// AddAppearance adds a new appearance
func (m *Manager) AddAppearance(appearance *Appearance) error {
	if appearance == nil {
		return fmt.Errorf("appearance cannot be nil")
	}

	// Validate the appearance
	if len(appearance.GetName()) == 0 {
		return fmt.Errorf("appearance name cannot be empty")
	}

	if appearance.GetID() <= 0 {
		return fmt.Errorf("appearance ID must be positive")
	}

	// Check if appearance already exists
	if m.appearances.HasAppearance(appearance.GetID()) {
		return fmt.Errorf("appearance with ID %d already exists", appearance.GetID())
	}

	// Add to collection
	if err := m.appearances.InsertAppearance(appearance); err != nil {
		return fmt.Errorf("failed to insert appearance: %w", err)
	}

	// Save to database if available
	if m.database != nil {
		if err := m.database.SaveAppearance(appearance); err != nil {
			// Remove from collection if database save failed
			m.appearances.RemoveAppearance(appearance.GetID())
			return fmt.Errorf("failed to save appearance to database: %w", err)
		}
	}

	if m.logger != nil {
		m.logger.LogInfo("Added appearance %d: %s (min client: %d)",
			appearance.GetID(), appearance.GetName(), appearance.GetMinClientVersion())
	}

	return nil
}

// UpdateAppearance updates an existing appearance
func (m *Manager) UpdateAppearance(appearance *Appearance) error {
	if appearance == nil {
		return fmt.Errorf("appearance cannot be nil")
	}

	// Check if appearance exists
	if !m.appearances.HasAppearance(appearance.GetID()) {
		return fmt.Errorf("appearance with ID %d does not exist", appearance.GetID())
	}

	// Update in collection
	if err := m.appearances.UpdateAppearance(appearance); err != nil {
		return fmt.Errorf("failed to update appearance: %w", err)
	}

	// Save to database if available
	if m.database != nil {
		if err := m.database.SaveAppearance(appearance); err != nil {
			return fmt.Errorf("failed to save appearance to database: %w", err)
		}
	}

	if m.logger != nil {
		m.logger.LogInfo("Updated appearance %d: %s", appearance.GetID(), appearance.GetName())
	}

	return nil
}

// RemoveAppearance removes an appearance
func (m *Manager) RemoveAppearance(id int32) error {
	// Check if appearance exists
	if !m.appearances.HasAppearance(id) {
		return fmt.Errorf("appearance with ID %d does not exist", id)
	}

	// Remove from database first if available
	if m.database != nil {
		if err := m.database.DeleteAppearance(id); err != nil {
			return fmt.Errorf("failed to delete appearance from database: %w", err)
		}
	}

	// Remove from collection
	if !m.appearances.RemoveAppearance(id) {
		return fmt.Errorf("failed to remove appearance from collection")
	}

	if m.logger != nil {
		m.logger.LogInfo("Removed appearance %d", id)
	}

	return nil
}

// GetCompatibleAppearances returns appearances compatible with client version
func (m *Manager) GetCompatibleAppearances(clientVersion int16) []*Appearance {
	return m.appearances.GetCompatibleAppearances(clientVersion)
}

// SearchAppearancesByName searches for appearances by name substring
func (m *Manager) SearchAppearancesByName(nameSubstring string) []*Appearance {
	return m.appearances.FindAppearancesByName(nameSubstring)
}

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

	// Get basic appearance statistics
	stats := m.appearances.GetStatistics()

	// Add manager statistics
	stats["total_lookups"] = m.totalLookups
	stats["successful_lookups"] = m.successfulLookups
	stats["failed_lookups"] = m.failedLookups
	stats["cache_hits"] = m.cacheHits
	stats["cache_misses"] = m.cacheMisses

	if m.totalLookups > 0 {
		stats["success_rate"] = float64(m.successfulLookups) / float64(m.totalLookups) * 100
		stats["cache_hit_rate"] = float64(m.cacheHits) / float64(m.totalLookups) * 100
	}

	return stats
}

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

	m.totalLookups = 0
	m.successfulLookups = 0
	m.failedLookups = 0
	m.cacheHits = 0
	m.cacheMisses = 0
}

// ValidateAllAppearances validates all appearances in the system
func (m *Manager) ValidateAllAppearances() []string {
	return m.appearances.ValidateAppearances()
}

// ReloadFromDatabase reloads all appearances from database
func (m *Manager) ReloadFromDatabase() error {
	if m.database == nil {
		return fmt.Errorf("no database available")
	}

	// Clear current appearances
	m.appearances.ClearAppearances()

	// Reload from database
	return m.Initialize()
}

// GetAppearanceCount returns the total number of appearances
func (m *Manager) GetAppearanceCount() int {
	return m.appearances.GetAppearanceCount()
}

// ProcessCommand handles appearance-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 "search":
		return m.handleSearchCommand(args)
	case "info":
		return m.handleInfoCommand(args)
	case "reload":
		return m.handleReloadCommand(args)
	default:
		return "", fmt.Errorf("unknown appearance command: %s", command)
	}
}

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

	result := "Appearance System Statistics:\n"
	result += fmt.Sprintf("Total Appearances: %d\n", stats["total_appearances"])
	result += fmt.Sprintf("Total Lookups: %d\n", stats["total_lookups"])
	result += fmt.Sprintf("Successful Lookups: %d\n", stats["successful_lookups"])
	result += fmt.Sprintf("Failed Lookups: %d\n", stats["failed_lookups"])

	if successRate, exists := stats["success_rate"]; exists {
		result += fmt.Sprintf("Success Rate: %.1f%%\n", successRate)
	}

	if cacheHitRate, exists := stats["cache_hit_rate"]; exists {
		result += fmt.Sprintf("Cache Hit Rate: %.1f%%\n", cacheHitRate)
	}

	if minID, exists := stats["min_id"]; exists {
		result += fmt.Sprintf("ID Range: %d - %d\n", minID, stats["max_id"])
	}

	return result, nil
}

// handleValidateCommand validates all appearances
func (m *Manager) handleValidateCommand(_ []string) (string, error) {
	issues := m.ValidateAllAppearances()

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

	result := fmt.Sprintf("Found %d issues with appearances:\n", len(issues))
	for i, issue := range issues {
		if i >= 10 { // Limit output
			result += "... (and more)\n"
			break
		}
		result += fmt.Sprintf("%d. %s\n", i+1, issue)
	}

	return result, nil
}

// handleSearchCommand searches for appearances by name
func (m *Manager) handleSearchCommand(args []string) (string, error) {
	if len(args) == 0 {
		return "", fmt.Errorf("search term required")
	}

	searchTerm := args[0]
	results := m.SearchAppearancesByName(searchTerm)

	if len(results) == 0 {
		return fmt.Sprintf("No appearances found matching '%s'.", searchTerm), nil
	}

	result := fmt.Sprintf("Found %d appearances matching '%s':\n", len(results), searchTerm)
	for i, appearance := range results {
		if i >= 20 { // Limit output
			result += "... (and more)\n"
			break
		}
		result += fmt.Sprintf("  %d: %s (min client: %d)\n",
			appearance.GetID(), appearance.GetName(), appearance.GetMinClientVersion())
	}

	return result, nil
}

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

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

	appearance := m.FindAppearanceByID(appearanceID)
	if appearance == nil {
		return fmt.Sprintf("Appearance %d not found.", appearanceID), nil
	}

	result := "Appearance Information:\n"
	result += fmt.Sprintf("ID: %d\n", appearance.GetID())
	result += fmt.Sprintf("Name: %s\n", appearance.GetName())
	result += fmt.Sprintf("Min Client Version: %d\n", appearance.GetMinClientVersion())

	return result, nil
}

// handleReloadCommand reloads appearances from database
func (m *Manager) handleReloadCommand(_ []string) (string, error) {
	if err := m.ReloadFromDatabase(); err != nil {
		return "", fmt.Errorf("failed to reload appearances: %w", err)
	}

	count := m.GetAppearanceCount()
	return fmt.Sprintf("Successfully reloaded %d appearances from database.", count), nil
}

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

	// Clear appearances
	m.appearances.ClearAppearances()
}
