package races

import (
	"math/rand"
	"strings"
	"sync"
)

// Races manages race information and lookups
// Converted from C++ Races class
type Races struct {
	// Race name to ID mapping (uppercase keys)
	raceMap map[string]int8

	// ID to friendly name mapping
	friendlyNameMap map[int8]string

	// Alignment-based race lists for randomization
	goodRaces []string
	evilRaces []string

	// Thread safety
	mutex sync.RWMutex
}

// NewRaces creates a new races manager with all EQ2 races
// Converted from C++ Races::Races constructor
func NewRaces() *Races {
	races := &Races{
		raceMap:         make(map[string]int8),
		friendlyNameMap: make(map[int8]string),
		goodRaces:       make([]string, 0),
		evilRaces:       make([]string, 0),
	}

	races.initializeRaces()
	return races
}

// initializeRaces sets up all race mappings
func (r *Races) initializeRaces() {
	// Initialize race name to ID mappings (from C++ constructor)
	r.raceMap[RaceNameBarbarian] = RaceBarbarian
	r.raceMap[RaceNameDarkElf] = RaceDarkElf
	r.raceMap[RaceNameDwarf] = RaceDwarf
	r.raceMap[RaceNameErudite] = RaceErudite
	r.raceMap[RaceNameFroglok] = RaceFroglok
	r.raceMap[RaceNameGnome] = RaceGnome
	r.raceMap[RaceNameHalfElf] = RaceHalfElf
	r.raceMap[RaceNameHalfling] = RaceHalfling
	r.raceMap[RaceNameHighElf] = RaceHighElf
	r.raceMap[RaceNameHuman] = RaceHuman
	r.raceMap[RaceNameIksar] = RaceIksar
	r.raceMap[RaceNameKerra] = RaceKerra
	r.raceMap[RaceNameOgre] = RaceOgre
	r.raceMap[RaceNameRatonga] = RaceRatonga
	r.raceMap[RaceNameTroll] = RaceTroll
	r.raceMap[RaceNameWoodElf] = RaceWoodElf
	r.raceMap[RaceNameFaeLight] = RaceFae
	r.raceMap[RaceNameFaeDark] = RaceArasai
	r.raceMap[RaceNameSarnak] = RaceSarnak
	r.raceMap[RaceNameVampire] = RaceVampire
	r.raceMap[RaceNameAerakyn] = RaceAerakyn

	// Initialize friendly display names (from C++ constructor)
	r.friendlyNameMap[RaceBarbarian] = DisplayNameBarbarian
	r.friendlyNameMap[RaceDarkElf] = DisplayNameDarkElf
	r.friendlyNameMap[RaceDwarf] = DisplayNameDwarf
	r.friendlyNameMap[RaceErudite] = DisplayNameErudite
	r.friendlyNameMap[RaceFroglok] = DisplayNameFroglok
	r.friendlyNameMap[RaceGnome] = DisplayNameGnome
	r.friendlyNameMap[RaceHalfElf] = DisplayNameHalfElf
	r.friendlyNameMap[RaceHalfling] = DisplayNameHalfling
	r.friendlyNameMap[RaceHighElf] = DisplayNameHighElf
	r.friendlyNameMap[RaceHuman] = DisplayNameHuman
	r.friendlyNameMap[RaceIksar] = DisplayNameIksar
	r.friendlyNameMap[RaceKerra] = DisplayNameKerra
	r.friendlyNameMap[RaceOgre] = DisplayNameOgre
	r.friendlyNameMap[RaceRatonga] = DisplayNameRatonga
	r.friendlyNameMap[RaceTroll] = DisplayNameTroll
	r.friendlyNameMap[RaceWoodElf] = DisplayNameWoodElf
	r.friendlyNameMap[RaceFae] = DisplayNameFae
	r.friendlyNameMap[RaceArasai] = DisplayNameArasai
	r.friendlyNameMap[RaceSarnak] = DisplayNameSarnak
	r.friendlyNameMap[RaceVampire] = DisplayNameVampire
	r.friendlyNameMap[RaceAerakyn] = DisplayNameAerakyn

	// Initialize good races (from C++ race_map_good)
	// "Neutral" races appear in both lists for /randomize functionality
	r.goodRaces = []string{
		RaceNameDwarf,     // 0
		RaceNameFaeLight,  // 1
		RaceNameFroglok,   // 2
		RaceNameHalfling,  // 3
		RaceNameHighElf,   // 4
		RaceNameWoodElf,   // 5
		RaceNameBarbarian, // 6 (neutral)
		RaceNameErudite,   // 7 (neutral)
		RaceNameGnome,     // 8 (neutral)
		RaceNameHalfElf,   // 9 (neutral)
		RaceNameHuman,     // 10 (neutral)
		RaceNameKerra,     // 11 (neutral)
		RaceNameVampire,   // 12 (neutral)
		RaceNameAerakyn,   // 13 (neutral)
	}

	// Initialize evil races (from C++ race_map_evil)
	r.evilRaces = []string{
		RaceNameFaeDark,   // 0
		RaceNameDarkElf,   // 1
		RaceNameIksar,     // 2
		RaceNameOgre,      // 3
		RaceNameRatonga,   // 4
		RaceNameSarnak,    // 5
		RaceNameTroll,     // 6
		RaceNameBarbarian, // 7 (neutral)
		RaceNameErudite,   // 8 (neutral)
		RaceNameGnome,     // 9 (neutral)
		RaceNameHalfElf,   // 10 (neutral)
		RaceNameHuman,     // 11 (neutral)
		RaceNameKerra,     // 12 (neutral)
		RaceNameVampire,   // 13 (neutral)
		RaceNameAerakyn,   // 14 (neutral)
	}
}

// GetRaceID returns the race ID for a given race name
// Converted from C++ Races::GetRaceID
func (r *Races) GetRaceID(name string) int8 {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	raceName := strings.ToUpper(strings.TrimSpace(name))
	if raceID, exists := r.raceMap[raceName]; exists {
		return raceID
	}

	return -1 // Invalid race
}

// GetRaceName returns the uppercase race name for a given ID
// Converted from C++ Races::GetRaceName
func (r *Races) GetRaceName(raceID int8) string {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	// Search through race map to find the name
	for name, id := range r.raceMap {
		if id == raceID {
			return name
		}
	}

	return "" // Invalid race ID
}

// GetRaceNameCase returns the friendly display name for a given race ID
// Converted from C++ Races::GetRaceNameCase
func (r *Races) GetRaceNameCase(raceID int8) string {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	if friendlyName, exists := r.friendlyNameMap[raceID]; exists {
		return friendlyName
	}

	return "" // Invalid race ID
}

// GetRandomGoodRace returns a random good-aligned race ID
// Converted from C++ Races::GetRaceNameGood
func (r *Races) GetRandomGoodRace() int8 {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	if len(r.goodRaces) == 0 {
		return DefaultRaceID
	}

	randomIndex := rand.Intn(len(r.goodRaces))
	raceName := r.goodRaces[randomIndex]

	if raceID, exists := r.raceMap[raceName]; exists {
		return raceID
	}

	return DefaultRaceID // Default to Human if error
}

// GetRandomEvilRace returns a random evil-aligned race ID
// Converted from C++ Races::GetRaceNameEvil
func (r *Races) GetRandomEvilRace() int8 {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	if len(r.evilRaces) == 0 {
		return DefaultRaceID
	}

	randomIndex := rand.Intn(len(r.evilRaces))
	raceName := r.evilRaces[randomIndex]

	if raceID, exists := r.raceMap[raceName]; exists {
		return raceID
	}

	return DefaultRaceID // Default to Human if error
}

// IsValidRaceID checks if a race ID is valid
func (r *Races) IsValidRaceID(raceID int8) bool {
	return raceID >= MinRaceID && raceID <= MaxRaceID
}

// GetAllRaces returns all race IDs and their friendly names
func (r *Races) GetAllRaces() map[int8]string {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	result := make(map[int8]string)
	for raceID, friendlyName := range r.friendlyNameMap {
		result[raceID] = friendlyName
	}

	return result
}

// GetRacesByAlignment returns races filtered by alignment
func (r *Races) GetRacesByAlignment(alignment string) []int8 {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	var raceNames []string

	switch strings.ToLower(alignment) {
	case AlignmentGood:
		raceNames = r.goodRaces
	case AlignmentEvil:
		raceNames = r.evilRaces
	default:
		// Return all races for neutral or unknown alignment
		result := make([]int8, 0, len(r.friendlyNameMap))
		for raceID := range r.friendlyNameMap {
			result = append(result, raceID)
		}
		return result
	}

	result := make([]int8, 0, len(raceNames))
	for _, raceName := range raceNames {
		if raceID, exists := r.raceMap[raceName]; exists {
			result = append(result, raceID)
		}
	}

	return result
}

// IsGoodRace checks if a race is considered good-aligned
func (r *Races) IsGoodRace(raceID int8) bool {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	raceName := ""
	for name, id := range r.raceMap {
		if id == raceID {
			raceName = name
			break
		}
	}

	if raceName == "" {
		return false
	}

	for _, goodRace := range r.goodRaces {
		if goodRace == raceName {
			return true
		}
	}

	return false
}

// IsEvilRace checks if a race is considered evil-aligned
func (r *Races) IsEvilRace(raceID int8) bool {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	raceName := ""
	for name, id := range r.raceMap {
		if id == raceID {
			raceName = name
			break
		}
	}

	if raceName == "" {
		return false
	}

	for _, evilRace := range r.evilRaces {
		if evilRace == raceName {
			return true
		}
	}

	return false
}

// IsNeutralRace checks if a race appears in both good and evil lists (neutral)
func (r *Races) IsNeutralRace(raceID int8) bool {
	return r.IsGoodRace(raceID) && r.IsEvilRace(raceID)
}

// GetRaceAlignment returns the primary alignment of a race
func (r *Races) GetRaceAlignment(raceID int8) string {
	if r.IsNeutralRace(raceID) {
		return AlignmentNeutral
	} else if r.IsGoodRace(raceID) {
		return AlignmentGood
	} else if r.IsEvilRace(raceID) {
		return AlignmentEvil
	}

	return AlignmentNeutral // Default for invalid races
}

// GetRaceCount returns the total number of races
func (r *Races) GetRaceCount() int {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	return len(r.friendlyNameMap)
}

// GetGoodRaceCount returns the number of good-aligned races
func (r *Races) GetGoodRaceCount() int {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	return len(r.goodRaces)
}

// GetEvilRaceCount returns the number of evil-aligned races
func (r *Races) GetEvilRaceCount() int {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	return len(r.evilRaces)
}

// GetRaceInfo returns comprehensive information about a race
func (r *Races) GetRaceInfo(raceID int8) map[string]interface{} {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	info := make(map[string]interface{})

	if !r.IsValidRaceID(raceID) {
		info["valid"] = false
		return info
	}

	info["valid"] = true
	info["race_id"] = raceID
	info["name"] = r.GetRaceName(raceID)
	info["display_name"] = r.GetRaceNameCase(raceID)
	info["alignment"] = r.GetRaceAlignment(raceID)
	info["is_good"] = r.IsGoodRace(raceID)
	info["is_evil"] = r.IsEvilRace(raceID)
	info["is_neutral"] = r.IsNeutralRace(raceID)

	return info
}

// Global races instance
var globalRaces *Races
var initRacesOnce sync.Once

// GetGlobalRaces returns the global races manager (singleton)
func GetGlobalRaces() *Races {
	initRacesOnce.Do(func() {
		globalRaces = NewRaces()
	})
	return globalRaces
}
