package titles

import (
	"fmt"
	"sync"
	"time"

	"eq2emu/internal/database"
)

// TitleManager manages the entire title system for the server
type TitleManager struct {
	masterList  *MasterTitlesList           // Global title definitions
	playerLists map[int32]*PlayerTitlesList // Player-specific title collections
	mutex       sync.RWMutex                // Thread safety

	// Background cleanup
	cleanupTicker *time.Ticker
	stopCleanup   chan bool

	// Statistics
	totalTitlesGranted int64
	totalTitlesExpired int64
}

// NewTitleManager creates a new title manager instance
func NewTitleManager() *TitleManager {
	tm := &TitleManager{
		masterList:         NewMasterTitlesList(),
		playerLists:        make(map[int32]*PlayerTitlesList),
		totalTitlesGranted: 0,
		totalTitlesExpired: 0,
	}

	// Start background cleanup process
	tm.startCleanupProcess()

	return tm
}

// GetMasterList returns the master titles list
func (tm *TitleManager) GetMasterList() *MasterTitlesList {
	return tm.masterList
}

// GetPlayerTitles retrieves or creates a player's title collection
func (tm *TitleManager) GetPlayerTitles(playerID int32) *PlayerTitlesList {
	tm.mutex.Lock()
	defer tm.mutex.Unlock()

	playerList, exists := tm.playerLists[playerID]
	if !exists {
		playerList = NewPlayerTitlesList(playerID, tm.masterList)
		tm.playerLists[playerID] = playerList
	}

	return playerList
}

// GrantTitle grants a title to a player
func (tm *TitleManager) GrantTitle(playerID, titleID int32, sourceAchievementID, sourceQuestID uint32) error {
	playerList := tm.GetPlayerTitles(playerID)

	err := playerList.AddTitle(titleID, sourceAchievementID, sourceQuestID)
	if err == nil {
		tm.mutex.Lock()
		tm.totalTitlesGranted++
		tm.mutex.Unlock()
	}

	return err
}

// RevokeTitle removes a title from a player
func (tm *TitleManager) RevokeTitle(playerID, titleID int32) error {
	tm.mutex.RLock()
	playerList, exists := tm.playerLists[playerID]
	tm.mutex.RUnlock()

	if !exists {
		return fmt.Errorf("player %d has no titles", playerID)
	}

	return playerList.RemoveTitle(titleID)
}

// SetPlayerActivePrefix sets a player's active prefix title
func (tm *TitleManager) SetPlayerActivePrefix(playerID, titleID int32) error {
	playerList := tm.GetPlayerTitles(playerID)
	return playerList.SetActivePrefix(titleID)
}

// SetPlayerActiveSuffix sets a player's active suffix title
func (tm *TitleManager) SetPlayerActiveSuffix(playerID, titleID int32) error {
	playerList := tm.GetPlayerTitles(playerID)
	return playerList.SetActiveSuffix(titleID)
}

// GetPlayerFormattedName returns a player's name with active titles
func (tm *TitleManager) GetPlayerFormattedName(playerID int32, playerName string) string {
	tm.mutex.RLock()
	playerList, exists := tm.playerLists[playerID]
	tm.mutex.RUnlock()

	if !exists {
		return playerName
	}

	return playerList.GetFormattedName(playerName)
}

// ProcessAchievementCompletion handles title grants from achievement completion
func (tm *TitleManager) ProcessAchievementCompletion(playerID int32, achievementID uint32) error {
	playerList := tm.GetPlayerTitles(playerID)
	return playerList.GrantTitleFromAchievement(achievementID)
}

// CreateTitle creates a new title and adds it to the master list
func (tm *TitleManager) CreateTitle(name, description, category string, position, source, rarity int32) (*Title, error) {
	title := NewTitle(0, name) // ID will be assigned automatically
	title.SetDescription(description)
	title.SetCategory(category)
	title.Position = position
	title.Source = source
	title.SetRarity(rarity)

	err := tm.masterList.AddTitle(title)
	if err != nil {
		return nil, err
	}

	return title, nil
}

// CreateAchievementTitle creates a title tied to a specific achievement
func (tm *TitleManager) CreateAchievementTitle(name, description string, achievementID uint32, position, rarity int32) (*Title, error) {
	title := NewTitle(0, name)
	title.SetDescription(description)
	title.SetCategory(CategoryAchievement)
	title.Position = position
	title.Source = TitleSourceAchievement
	title.SetRarity(rarity)
	title.AchievementID = achievementID

	err := tm.masterList.AddTitle(title)
	if err != nil {
		return nil, err
	}

	return title, nil
}

// CreateTemporaryTitle creates a title that expires after a certain time
func (tm *TitleManager) CreateTemporaryTitle(name, description string, hours int32, position, source, rarity int32) (*Title, error) {
	title := NewTitle(0, name)
	title.SetDescription(description)
	title.Position = position
	title.Source = source
	title.SetRarity(rarity)
	title.ExpirationHours = hours
	title.SetFlag(FlagTemporary)

	err := tm.masterList.AddTitle(title)
	if err != nil {
		return nil, err
	}

	return title, nil
}

// CreateUniqueTitle creates a title that only one player can have
func (tm *TitleManager) CreateUniqueTitle(name, description string, position, source int32) (*Title, error) {
	title := NewTitle(0, name)
	title.SetDescription(description)
	title.Position = position
	title.Source = source
	title.SetRarity(TitleRarityUnique)
	title.SetFlag(FlagUnique)

	err := tm.masterList.AddTitle(title)
	if err != nil {
		return nil, err
	}

	return title, nil
}

// GetTitlesByCategory retrieves all titles in a category
func (tm *TitleManager) GetTitlesByCategory(category string) []*Title {
	return tm.masterList.GetTitlesByCategory(category)
}

// GetTitlesBySource retrieves all titles from a specific source
func (tm *TitleManager) GetTitlesBySource(source int32) []*Title {
	return tm.masterList.GetTitlesBySource(source)
}

// GetTitlesByRarity retrieves all titles of a specific rarity
func (tm *TitleManager) GetTitlesByRarity(rarity int32) []*Title {
	return tm.masterList.GetTitlesByRarity(rarity)
}

// SearchTitles searches for titles by name (case-insensitive partial match)
func (tm *TitleManager) SearchTitles(query string) []*Title {
	allTitles := tm.masterList.GetAllTitles(false) // Exclude hidden
	result := make([]*Title, 0)

	// Simple case-insensitive contains search
	// TODO: Implement more sophisticated search with fuzzy matching
	for _, title := range allTitles {
		if contains(title.Name, query) || contains(title.Description, query) {
			result = append(result, title)
		}
	}

	return result
}

// contains performs case-insensitive substring search
func contains(s, substr string) bool {
	// Simple implementation - could be improved with proper Unicode handling
	sLower := []rune(s)
	substrLower := []rune(substr)

	for i := range sLower {
		if sLower[i] >= 'A' && sLower[i] <= 'Z' {
			sLower[i] = sLower[i] + 32
		}
	}

	for i := range substrLower {
		if substrLower[i] >= 'A' && substrLower[i] <= 'Z' {
			substrLower[i] = substrLower[i] + 32
		}
	}

	sStr := string(sLower)
	subStr := string(substrLower)

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

	return false
}

// GetPlayerTitlePacketData builds packet data for a player's titles
func (tm *TitleManager) GetPlayerTitlePacketData(playerID int32, playerName string) *TitlePacketData {
	tm.mutex.RLock()
	playerList, exists := tm.playerLists[playerID]
	tm.mutex.RUnlock()

	if !exists {
		// Create basic packet data with default titles
		return &TitlePacketData{
			PlayerID:      uint32(playerID),
			PlayerName:    playerName,
			PrefixTitle:   "",
			SuffixTitle:   "Citizen",
			SubTitle:      "",
			LastName:      "",
			Titles:        []TitleEntry{{"Citizen", false}},
			NumTitles:     1,
			CurrentPrefix: int16(TitleIDNone),
			CurrentSuffix: int16(TitleIDCitizen),
		}
	}

	return playerList.BuildPacketData(playerName)
}

// startCleanupProcess begins the background cleanup of expired titles
func (tm *TitleManager) startCleanupProcess() {
	tm.cleanupTicker = time.NewTicker(1 * time.Hour) // Run cleanup every hour
	tm.stopCleanup = make(chan bool)

	go func() {
		for {
			select {
			case <-tm.cleanupTicker.C:
				tm.cleanupExpiredTitles()
			case <-tm.stopCleanup:
				tm.cleanupTicker.Stop()
				return
			}
		}
	}()
}

// StopCleanupProcess stops the background cleanup
func (tm *TitleManager) StopCleanupProcess() {
	if tm.stopCleanup != nil {
		tm.stopCleanup <- true
	}
}

// cleanupExpiredTitles removes expired titles from all players
func (tm *TitleManager) cleanupExpiredTitles() {
	tm.mutex.RLock()
	playerLists := make([]*PlayerTitlesList, 0, len(tm.playerLists))
	for _, list := range tm.playerLists {
		playerLists = append(playerLists, list)
	}
	tm.mutex.RUnlock()

	totalExpired := 0
	for _, playerList := range playerLists {
		expired := playerList.CleanupExpiredTitles()
		totalExpired += expired
	}

	if totalExpired > 0 {
		tm.mutex.Lock()
		tm.totalTitlesExpired += int64(totalExpired)
		tm.mutex.Unlock()
	}
}

// GetStatistics returns title system statistics
func (tm *TitleManager) GetStatistics() map[string]interface{} {
	tm.mutex.RLock()
	defer tm.mutex.RUnlock()

	return map[string]interface{}{
		"total_titles":         tm.masterList.GetTitleCount(),
		"total_players":        len(tm.playerLists),
		"titles_granted":       tm.totalTitlesGranted,
		"titles_expired":       tm.totalTitlesExpired,
		"available_categories": tm.masterList.GetAvailableCategories(),
	}
}

// RemovePlayerFromMemory removes a player's title data from memory (when they log out)
func (tm *TitleManager) RemovePlayerFromMemory(playerID int32) {
	tm.mutex.Lock()
	defer tm.mutex.Unlock()

	delete(tm.playerLists, playerID)
}

// LoadPlayerTitles loads a player's titles from database
func (tm *TitleManager) LoadPlayerTitles(playerID int32, db *database.DB) error {
	playerList := tm.GetPlayerTitles(playerID)
	return playerList.LoadFromDatabase(db)
}

// SavePlayerTitles saves a player's titles to database
func (tm *TitleManager) SavePlayerTitles(playerID int32, db *database.DB) error {
	tm.mutex.RLock()
	playerList, exists := tm.playerLists[playerID]
	tm.mutex.RUnlock()

	if !exists {
		return fmt.Errorf("player %d has no title data to save", playerID)
	}

	return playerList.SaveToDatabase(db)
}

// LoadMasterTitles loads all titles from database
func (tm *TitleManager) LoadMasterTitles(db *database.DB) error {
	return tm.masterList.LoadFromDatabase(db)
}

// SaveMasterTitles saves all titles to database
func (tm *TitleManager) SaveMasterTitles(db *database.DB) error {
	return tm.masterList.SaveToDatabase(db)
}

// ValidateTitle validates a title before adding it
func (tm *TitleManager) ValidateTitle(title *Title) error {
	return tm.masterList.ValidateTitle(title)
}

// Shutdown gracefully shuts down the title manager
func (tm *TitleManager) Shutdown() {
	tm.StopCleanupProcess()
}
