package appearances

import (
	"fmt"
	"sync"
)

// Appearances manages a collection of appearance objects with thread-safe operations
type Appearances struct {
	appearanceMap map[int32]*Appearance // Map of appearance ID to appearance
	mutex         sync.RWMutex          // Thread safety for concurrent access
}

// NewAppearances creates a new appearances manager
func NewAppearances() *Appearances {
	return &Appearances{
		appearanceMap: make(map[int32]*Appearance),
	}
}

// Reset clears all appearances from the manager
func (a *Appearances) Reset() {
	a.ClearAppearances()
}

// ClearAppearances removes all appearances from the manager
func (a *Appearances) ClearAppearances() {
	a.mutex.Lock()
	defer a.mutex.Unlock()

	// Clear the map - Go's garbage collector will handle cleanup
	a.appearanceMap = make(map[int32]*Appearance)
}

// InsertAppearance adds an appearance to the manager
func (a *Appearances) InsertAppearance(appearance *Appearance) error {
	if appearance == nil {
		return fmt.Errorf("appearance cannot be nil")
	}

	a.mutex.Lock()
	defer a.mutex.Unlock()

	a.appearanceMap[appearance.GetID()] = appearance
	return nil
}

// FindAppearanceByID retrieves an appearance by its ID
func (a *Appearances) FindAppearanceByID(id int32) *Appearance {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	if appearance, exists := a.appearanceMap[id]; exists {
		return appearance
	}

	return nil
}

// HasAppearance checks if an appearance exists by ID
func (a *Appearances) HasAppearance(id int32) bool {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	_, exists := a.appearanceMap[id]
	return exists
}

// GetAppearanceCount returns the total number of appearances
func (a *Appearances) GetAppearanceCount() int {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	return len(a.appearanceMap)
}

// GetAllAppearances returns a copy of all appearances
func (a *Appearances) GetAllAppearances() map[int32]*Appearance {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	// Return a copy to prevent external modification
	result := make(map[int32]*Appearance)
	for id, appearance := range a.appearanceMap {
		result[id] = appearance
	}

	return result
}

// GetAppearanceIDs returns all appearance IDs
func (a *Appearances) GetAppearanceIDs() []int32 {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	ids := make([]int32, 0, len(a.appearanceMap))
	for id := range a.appearanceMap {
		ids = append(ids, id)
	}

	return ids
}

// FindAppearancesByName finds all appearances with names containing the given substring
func (a *Appearances) FindAppearancesByName(nameSubstring string) []*Appearance {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	var results []*Appearance

	for _, appearance := range a.appearanceMap {
		if contains(appearance.GetName(), nameSubstring) {
			results = append(results, appearance)
		}
	}

	return results
}

// FindAppearancesByMinClient finds all appearances with a specific minimum client version
func (a *Appearances) FindAppearancesByMinClient(minClient int16) []*Appearance {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	var results []*Appearance

	for _, appearance := range a.appearanceMap {
		if appearance.GetMinClientVersion() == minClient {
			results = append(results, appearance)
		}
	}

	return results
}

// GetCompatibleAppearances returns all appearances compatible with the given client version
func (a *Appearances) GetCompatibleAppearances(clientVersion int16) []*Appearance {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	var results []*Appearance

	for _, appearance := range a.appearanceMap {
		if appearance.IsCompatibleWithClient(clientVersion) {
			results = append(results, appearance)
		}
	}

	return results
}

// RemoveAppearance removes an appearance by ID
func (a *Appearances) RemoveAppearance(id int32) bool {
	a.mutex.Lock()
	defer a.mutex.Unlock()

	if _, exists := a.appearanceMap[id]; exists {
		delete(a.appearanceMap, id)
		return true
	}

	return false
}

// UpdateAppearance updates an existing appearance or inserts it if it doesn't exist
func (a *Appearances) UpdateAppearance(appearance *Appearance) error {
	if appearance == nil {
		return fmt.Errorf("appearance cannot be nil")
	}

	a.mutex.Lock()
	defer a.mutex.Unlock()

	a.appearanceMap[appearance.GetID()] = appearance
	return nil
}

// GetAppearancesByIDRange returns all appearances within the given ID range (inclusive)
func (a *Appearances) GetAppearancesByIDRange(minID, maxID int32) []*Appearance {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	var results []*Appearance

	for id, appearance := range a.appearanceMap {
		if id >= minID && id <= maxID {
			results = append(results, appearance)
		}
	}

	return results
}

// ValidateAppearances checks all appearances for consistency
func (a *Appearances) ValidateAppearances() []string {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	var issues []string

	for id, appearance := range a.appearanceMap {
		if appearance == nil {
			issues = append(issues, fmt.Sprintf("Appearance ID %d is nil", id))
			continue
		}

		if appearance.GetID() != id {
			issues = append(issues, fmt.Sprintf("Appearance ID mismatch: map key %d != appearance ID %d", id, appearance.GetID()))
		}

		if len(appearance.GetName()) == 0 {
			issues = append(issues, fmt.Sprintf("Appearance ID %d has empty name", id))
		}

		if appearance.GetMinClientVersion() < 0 {
			issues = append(issues, fmt.Sprintf("Appearance ID %d has negative min client version: %d", id, appearance.GetMinClientVersion()))
		}
	}

	return issues
}

// IsValid returns true if all appearances are valid
func (a *Appearances) IsValid() bool {
	issues := a.ValidateAppearances()
	return len(issues) == 0
}

// GetStatistics returns statistics about the appearance collection
func (a *Appearances) GetStatistics() map[string]interface{} {
	a.mutex.RLock()
	defer a.mutex.RUnlock()

	stats := make(map[string]interface{})
	stats["total_appearances"] = len(a.appearanceMap)

	// Count by minimum client version
	versionCounts := make(map[int16]int)
	for _, appearance := range a.appearanceMap {
		versionCounts[appearance.GetMinClientVersion()]++
	}
	stats["appearances_by_min_client"] = versionCounts

	// Find ID range
	if len(a.appearanceMap) > 0 {
		var minID, maxID int32
		first := true

		for id := range a.appearanceMap {
			if first {
				minID = id
				maxID = id
				first = false
			} else {
				if id < minID {
					minID = id
				}
				if id > maxID {
					maxID = id
				}
			}
		}

		stats["min_id"] = minID
		stats["max_id"] = maxID
		stats["id_range"] = maxID - minID
	}

	return stats
}

// contains checks if a string contains a substring (case-sensitive)
func contains(str, substr string) bool {
	if len(substr) == 0 {
		return true
	}
	if len(str) < len(substr) {
		return false
	}

	for i := 0; i <= len(str)-len(substr); i++ {
		if str[i:i+len(substr)] == substr {
			return true
		}
	}

	return false
}
