package languages

import (
	"fmt"
	"sync"
)

// Language represents a single language that can be learned by players
type Language struct {
	id         int32        // Unique language identifier
	name       string       // Language name
	saveNeeded bool         // Whether this language needs to be saved to database
	mutex      sync.RWMutex // Thread safety
}

// NewLanguage creates a new language with default values
func NewLanguage() *Language {
	return &Language{
		id:         0,
		name:       "",
		saveNeeded: false,
	}
}

// NewLanguageFromExisting creates a copy of an existing language
func NewLanguageFromExisting(source *Language) *Language {
	if source == nil {
		return NewLanguage()
	}

	source.mutex.RLock()
	defer source.mutex.RUnlock()

	return &Language{
		id:         source.id,
		name:       source.name,
		saveNeeded: source.saveNeeded,
	}
}

// GetID returns the language's unique identifier
func (l *Language) GetID() int32 {
	l.mutex.RLock()
	defer l.mutex.RUnlock()

	return l.id
}

// SetID updates the language's unique identifier
func (l *Language) SetID(id int32) {
	l.mutex.Lock()
	defer l.mutex.Unlock()

	l.id = id
}

// GetName returns the language name
func (l *Language) GetName() string {
	l.mutex.RLock()
	defer l.mutex.RUnlock()

	return l.name
}

// SetName updates the language name
func (l *Language) SetName(name string) {
	l.mutex.Lock()
	defer l.mutex.Unlock()

	// Truncate if too long
	if len(name) > MaxLanguageNameLength {
		name = name[:MaxLanguageNameLength]
	}

	l.name = name
}

// GetSaveNeeded returns whether this language needs database saving
func (l *Language) GetSaveNeeded() bool {
	l.mutex.RLock()
	defer l.mutex.RUnlock()

	return l.saveNeeded
}

// SetSaveNeeded updates the save status
func (l *Language) SetSaveNeeded(needed bool) {
	l.mutex.Lock()
	defer l.mutex.Unlock()

	l.saveNeeded = needed
}

// IsValid validates the language data
func (l *Language) IsValid() bool {
	l.mutex.RLock()
	defer l.mutex.RUnlock()

	if l.id < MinLanguageID || l.id > MaxLanguageID {
		return false
	}

	if len(l.name) == 0 || len(l.name) > MaxLanguageNameLength {
		return false
	}

	return true
}

// String returns a string representation of the language
func (l *Language) String() string {
	l.mutex.RLock()
	defer l.mutex.RUnlock()

	return fmt.Sprintf("Language{ID: %d, Name: %s, SaveNeeded: %v}", l.id, l.name, l.saveNeeded)
}

// Copy creates a deep copy of the language
func (l *Language) Copy() *Language {
	return NewLanguageFromExisting(l)
}

// MasterLanguagesList manages the global list of all available languages
type MasterLanguagesList struct {
	languages map[int32]*Language  // Languages indexed by ID for fast lookup
	nameIndex map[string]*Language // Languages indexed by name for name lookups
	mutex     sync.RWMutex         // Thread safety
}

// NewMasterLanguagesList creates a new master languages list
func NewMasterLanguagesList() *MasterLanguagesList {
	return &MasterLanguagesList{
		languages: make(map[int32]*Language),
		nameIndex: make(map[string]*Language),
	}
}

// Clear removes all languages from the list
func (mll *MasterLanguagesList) Clear() {
	mll.mutex.Lock()
	defer mll.mutex.Unlock()

	mll.languages = make(map[int32]*Language)
	mll.nameIndex = make(map[string]*Language)
}

// Size returns the number of languages in the list
func (mll *MasterLanguagesList) Size() int32 {
	mll.mutex.RLock()
	defer mll.mutex.RUnlock()

	return int32(len(mll.languages))
}

// AddLanguage adds a new language to the master list
func (mll *MasterLanguagesList) AddLanguage(language *Language) error {
	if language == nil {
		return fmt.Errorf("language cannot be nil")
	}

	if !language.IsValid() {
		return fmt.Errorf("language is not valid: %s", language.String())
	}

	mll.mutex.Lock()
	defer mll.mutex.Unlock()

	// Check for duplicate ID
	if _, exists := mll.languages[language.GetID()]; exists {
		return fmt.Errorf("language with ID %d already exists", language.GetID())
	}

	// Check for duplicate name
	name := language.GetName()
	if _, exists := mll.nameIndex[name]; exists {
		return fmt.Errorf("language with name '%s' already exists", name)
	}

	// Add to both indexes
	mll.languages[language.GetID()] = language
	mll.nameIndex[name] = language

	return nil
}

// GetLanguage retrieves a language by ID
func (mll *MasterLanguagesList) GetLanguage(id int32) *Language {
	mll.mutex.RLock()
	defer mll.mutex.RUnlock()

	return mll.languages[id]
}

// GetLanguageByName retrieves a language by name
func (mll *MasterLanguagesList) GetLanguageByName(name string) *Language {
	mll.mutex.RLock()
	defer mll.mutex.RUnlock()

	return mll.nameIndex[name]
}

// GetAllLanguages returns a copy of all languages
func (mll *MasterLanguagesList) GetAllLanguages() []*Language {
	mll.mutex.RLock()
	defer mll.mutex.RUnlock()

	result := make([]*Language, 0, len(mll.languages))
	for _, lang := range mll.languages {
		result = append(result, lang)
	}

	return result
}

// HasLanguage checks if a language exists by ID
func (mll *MasterLanguagesList) HasLanguage(id int32) bool {
	mll.mutex.RLock()
	defer mll.mutex.RUnlock()

	_, exists := mll.languages[id]
	return exists
}

// HasLanguageByName checks if a language exists by name
func (mll *MasterLanguagesList) HasLanguageByName(name string) bool {
	mll.mutex.RLock()
	defer mll.mutex.RUnlock()

	_, exists := mll.nameIndex[name]
	return exists
}

// RemoveLanguage removes a language by ID
func (mll *MasterLanguagesList) RemoveLanguage(id int32) bool {
	mll.mutex.Lock()
	defer mll.mutex.Unlock()

	language, exists := mll.languages[id]
	if !exists {
		return false
	}

	// Remove from both indexes
	delete(mll.languages, id)
	delete(mll.nameIndex, language.GetName())

	return true
}

// UpdateLanguage updates an existing language
func (mll *MasterLanguagesList) UpdateLanguage(language *Language) error {
	if language == nil {
		return fmt.Errorf("language cannot be nil")
	}

	if !language.IsValid() {
		return fmt.Errorf("language is not valid: %s", language.String())
	}

	mll.mutex.Lock()
	defer mll.mutex.Unlock()

	id := language.GetID()
	oldLanguage, exists := mll.languages[id]
	if !exists {
		return fmt.Errorf("language with ID %d does not exist", id)
	}

	// Remove old name index if name changed
	oldName := oldLanguage.GetName()
	newName := language.GetName()
	if oldName != newName {
		delete(mll.nameIndex, oldName)

		// Check for name conflicts
		if _, exists := mll.nameIndex[newName]; exists {
			return fmt.Errorf("language with name '%s' already exists", newName)
		}

		mll.nameIndex[newName] = language
	}

	// Update language
	mll.languages[id] = language

	return nil
}

// GetLanguageNames returns all language names
func (mll *MasterLanguagesList) GetLanguageNames() []string {
	mll.mutex.RLock()
	defer mll.mutex.RUnlock()

	names := make([]string, 0, len(mll.nameIndex))
	for name := range mll.nameIndex {
		names = append(names, name)
	}

	return names
}

// PlayerLanguagesList manages languages known by a specific player
type PlayerLanguagesList struct {
	languages map[int32]*Language  // Player's languages indexed by ID
	nameIndex map[string]*Language // Player's languages indexed by name
	mutex     sync.RWMutex         // Thread safety
}

// NewPlayerLanguagesList creates a new player languages list
func NewPlayerLanguagesList() *PlayerLanguagesList {
	return &PlayerLanguagesList{
		languages: make(map[int32]*Language),
		nameIndex: make(map[string]*Language),
	}
}

// Clear removes all languages from the player's list
func (pll *PlayerLanguagesList) Clear() {
	pll.mutex.Lock()
	defer pll.mutex.Unlock()

	pll.languages = make(map[int32]*Language)
	pll.nameIndex = make(map[string]*Language)
}

// Add adds a language to the player's known languages
func (pll *PlayerLanguagesList) Add(language *Language) error {
	if language == nil {
		return fmt.Errorf("language cannot be nil")
	}

	pll.mutex.Lock()
	defer pll.mutex.Unlock()

	id := language.GetID()
	name := language.GetName()

	// Check if already known
	if _, exists := pll.languages[id]; exists {
		return fmt.Errorf("player already knows language with ID %d", id)
	}

	// Check player language limit
	if len(pll.languages) >= MaxLanguagesPerPlayer {
		return fmt.Errorf("player has reached maximum language limit (%d)", MaxLanguagesPerPlayer)
	}

	// Add to both indexes
	pll.languages[id] = language
	pll.nameIndex[name] = language

	return nil
}

// GetLanguage retrieves a language the player knows by ID
func (pll *PlayerLanguagesList) GetLanguage(id int32) *Language {
	pll.mutex.RLock()
	defer pll.mutex.RUnlock()

	return pll.languages[id]
}

// GetLanguageByName retrieves a language the player knows by name
func (pll *PlayerLanguagesList) GetLanguageByName(name string) *Language {
	pll.mutex.RLock()
	defer pll.mutex.RUnlock()

	return pll.nameIndex[name]
}

// GetAllLanguages returns a copy of all languages the player knows
func (pll *PlayerLanguagesList) GetAllLanguages() []*Language {
	pll.mutex.RLock()
	defer pll.mutex.RUnlock()

	result := make([]*Language, 0, len(pll.languages))
	for _, lang := range pll.languages {
		result = append(result, lang)
	}

	return result
}

// HasLanguage checks if the player knows a language by ID
func (pll *PlayerLanguagesList) HasLanguage(id int32) bool {
	pll.mutex.RLock()
	defer pll.mutex.RUnlock()

	_, exists := pll.languages[id]
	return exists
}

// HasLanguageByName checks if the player knows a language by name
func (pll *PlayerLanguagesList) HasLanguageByName(name string) bool {
	pll.mutex.RLock()
	defer pll.mutex.RUnlock()

	_, exists := pll.nameIndex[name]
	return exists
}

// RemoveLanguage removes a language from the player's known languages
func (pll *PlayerLanguagesList) RemoveLanguage(id int32) bool {
	pll.mutex.Lock()
	defer pll.mutex.Unlock()

	language, exists := pll.languages[id]
	if !exists {
		return false
	}

	// Remove from both indexes
	delete(pll.languages, id)
	delete(pll.nameIndex, language.GetName())

	return true
}

// Size returns the number of languages the player knows
func (pll *PlayerLanguagesList) Size() int32 {
	pll.mutex.RLock()
	defer pll.mutex.RUnlock()

	return int32(len(pll.languages))
}

// GetLanguageIDs returns all language IDs the player knows
func (pll *PlayerLanguagesList) GetLanguageIDs() []int32 {
	pll.mutex.RLock()
	defer pll.mutex.RUnlock()

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

	return ids
}

// GetLanguageNames returns all language names the player knows
func (pll *PlayerLanguagesList) GetLanguageNames() []string {
	pll.mutex.RLock()
	defer pll.mutex.RUnlock()

	names := make([]string, 0, len(pll.nameIndex))
	for name := range pll.nameIndex {
		names = append(names, name)
	}

	return names
}

// LanguageStatistics contains language system statistics
type LanguageStatistics struct {
	TotalLanguages       int              `json:"total_languages"`
	PlayersWithLanguages int              `json:"players_with_languages"`
	LanguageUsageCount   map[int32]int64  `json:"language_usage_count"`
	LanguageLookups      int64            `json:"language_lookups"`
	LanguagesByName      map[string]int32 `json:"languages_by_name"`
}
