package titles

import (
	"fmt"
	"sync"
	"time"

	"eq2emu/internal/database"
)

// PlayerTitlesList manages titles owned by a specific player
type PlayerTitlesList struct {
	playerID       int32                  // Character ID
	titles         map[int32]*PlayerTitle // Owned titles indexed by title ID
	activePrefixID int32                  // Currently active prefix title ID (0 = none)
	activeSuffixID int32                  // Currently active suffix title ID (0 = none)
	masterList     *MasterTitlesList      // Reference to master titles list
	mutex          sync.RWMutex           // Thread safety
}

// TitlePacketData represents title data for network packets
type TitlePacketData struct {
	PlayerID      uint32       `json:"player_id"`
	PlayerName    string       `json:"player_name"`
	PrefixTitle   string       `json:"prefix_title"`
	SuffixTitle   string       `json:"suffix_title"`
	SubTitle      string       `json:"sub_title"`
	LastName      string       `json:"last_name"`
	Titles        []TitleEntry `json:"titles"`
	NumTitles     uint16       `json:"num_titles"`
	CurrentPrefix int16        `json:"current_prefix"`
	CurrentSuffix int16        `json:"current_suffix"`
}

// TitleEntry represents a single title for packet transmission
type TitleEntry struct {
	Name     string `json:"name"`
	IsPrefix bool   `json:"is_prefix"`
}

// NewPlayerTitlesList creates a new player titles list
func NewPlayerTitlesList(playerID int32, masterList *MasterTitlesList) *PlayerTitlesList {
	ptl := &PlayerTitlesList{
		playerID:       playerID,
		titles:         make(map[int32]*PlayerTitle),
		activePrefixID: TitleIDNone,
		activeSuffixID: TitleIDCitizen, // Default suffix title
		masterList:     masterList,
	}

	// Grant default citizen title
	ptl.grantDefaultTitle()

	return ptl
}

// grantDefaultTitle grants the basic citizen title to new players
func (ptl *PlayerTitlesList) grantDefaultTitle() {
	citizenTitle := NewPlayerTitle(TitleIDCitizen, ptl.playerID)
	citizenTitle.IsActive = true
	citizenTitle.IsPrefix = false // Suffix title
	ptl.titles[TitleIDCitizen] = citizenTitle
}

// AddTitle grants a title to the player
func (ptl *PlayerTitlesList) AddTitle(titleID int32, sourceAchievementID, sourceQuestID uint32) error {
	ptl.mutex.Lock()
	defer ptl.mutex.Unlock()

	// Check if player already has this title
	if _, exists := ptl.titles[titleID]; exists {
		return fmt.Errorf("player %d already has title %d", ptl.playerID, titleID)
	}

	// Verify title exists in master list
	masterTitle, exists := ptl.masterList.GetTitle(titleID)
	if !exists {
		return fmt.Errorf("title %d does not exist in master list", titleID)
	}

	// Check if we've hit the maximum title limit
	if len(ptl.titles) >= MaxPlayerTitles {
		return fmt.Errorf("player %d has reached maximum title limit of %d", ptl.playerID, MaxPlayerTitles)
	}

	// Check for unique title restrictions
	if masterTitle.IsUnique() {
		// TODO: Check database to ensure no other player has this unique title
	}

	// Create player title entry
	playerTitle := NewPlayerTitle(titleID, ptl.playerID)
	playerTitle.AchievementID = sourceAchievementID
	playerTitle.QuestID = sourceQuestID

	// Set expiration if it's a temporary title
	if masterTitle.ExpirationHours > 0 {
		playerTitle.SetExpiration(masterTitle.ExpirationHours)
	}

	ptl.titles[titleID] = playerTitle

	return nil
}

// RemoveTitle removes a title from the player
func (ptl *PlayerTitlesList) RemoveTitle(titleID int32) error {
	ptl.mutex.Lock()
	defer ptl.mutex.Unlock()

	playerTitle, exists := ptl.titles[titleID]
	if !exists {
		return fmt.Errorf("player %d does not have title %d", ptl.playerID, titleID)
	}

	// If this title is currently active, deactivate it
	if playerTitle.IsActive {
		if playerTitle.IsPrefix && ptl.activePrefixID == titleID {
			ptl.activePrefixID = TitleIDNone
		} else if !playerTitle.IsPrefix && ptl.activeSuffixID == titleID {
			ptl.activeSuffixID = TitleIDCitizen // Revert to default
		}
	}

	delete(ptl.titles, titleID)

	return nil
}

// HasTitle checks if the player owns a specific title
func (ptl *PlayerTitlesList) HasTitle(titleID int32) bool {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	_, exists := ptl.titles[titleID]
	return exists
}

// GetTitle retrieves a player's title information
func (ptl *PlayerTitlesList) GetTitle(titleID int32) (*PlayerTitle, bool) {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	title, exists := ptl.titles[titleID]
	if !exists {
		return nil, false
	}

	return title.Clone(), true
}

// SetActivePrefix sets the active prefix title
func (ptl *PlayerTitlesList) SetActivePrefix(titleID int32) error {
	ptl.mutex.Lock()
	defer ptl.mutex.Unlock()

	// Allow clearing prefix title
	if titleID == TitleIDNone {
		// Deactivate current prefix if any
		if ptl.activePrefixID != TitleIDNone {
			if currentTitle, exists := ptl.titles[ptl.activePrefixID]; exists {
				currentTitle.IsActive = false
			}
		}
		ptl.activePrefixID = TitleIDNone
		return nil
	}

	// Check if player owns the title
	playerTitle, exists := ptl.titles[titleID]
	if !exists {
		return fmt.Errorf("player %d does not own title %d", ptl.playerID, titleID)
	}

	// Verify title can be used as prefix
	masterTitle, exists := ptl.masterList.GetTitle(titleID)
	if !exists {
		return fmt.Errorf("title %d not found in master list", titleID)
	}

	if masterTitle.Position != TitlePositionPrefix {
		return fmt.Errorf("title %d cannot be used as prefix", titleID)
	}

	// Check if title has expired
	if playerTitle.IsExpired() {
		return fmt.Errorf("title %d has expired", titleID)
	}

	// Deactivate current prefix
	if ptl.activePrefixID != TitleIDNone {
		if currentTitle, exists := ptl.titles[ptl.activePrefixID]; exists {
			currentTitle.IsActive = false
		}
	}

	// Activate new prefix
	playerTitle.Activate(true)
	ptl.activePrefixID = titleID

	return nil
}

// SetActiveSuffix sets the active suffix title
func (ptl *PlayerTitlesList) SetActiveSuffix(titleID int32) error {
	ptl.mutex.Lock()
	defer ptl.mutex.Unlock()

	// Check if player owns the title
	playerTitle, exists := ptl.titles[titleID]
	if !exists {
		return fmt.Errorf("player %d does not own title %d", ptl.playerID, titleID)
	}

	// Verify title can be used as suffix
	masterTitle, exists := ptl.masterList.GetTitle(titleID)
	if !exists {
		return fmt.Errorf("title %d not found in master list", titleID)
	}

	if masterTitle.Position != TitlePositionSuffix {
		return fmt.Errorf("title %d cannot be used as suffix", titleID)
	}

	// Check if title has expired
	if playerTitle.IsExpired() {
		return fmt.Errorf("title %d has expired", titleID)
	}

	// Deactivate current suffix
	if ptl.activeSuffixID != TitleIDNone {
		if currentTitle, exists := ptl.titles[ptl.activeSuffixID]; exists {
			currentTitle.IsActive = false
		}
	}

	// Activate new suffix
	playerTitle.Activate(false)
	ptl.activeSuffixID = titleID

	return nil
}

// GetActivePrefixTitle returns the currently active prefix title
func (ptl *PlayerTitlesList) GetActivePrefixTitle() (*Title, bool) {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	if ptl.activePrefixID == TitleIDNone {
		return nil, false
	}

	return ptl.masterList.GetTitle(ptl.activePrefixID)
}

// GetActiveSuffixTitle returns the currently active suffix title
func (ptl *PlayerTitlesList) GetActiveSuffixTitle() (*Title, bool) {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	if ptl.activeSuffixID == TitleIDNone {
		return nil, false
	}

	return ptl.masterList.GetTitle(ptl.activeSuffixID)
}

// GetAllTitles returns all titles owned by the player
func (ptl *PlayerTitlesList) GetAllTitles() []*PlayerTitle {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	result := make([]*PlayerTitle, 0, len(ptl.titles))
	for _, title := range ptl.titles {
		result = append(result, title.Clone())
	}

	return result
}

// GetAvailablePrefixTitles returns all titles that can be used as prefix
func (ptl *PlayerTitlesList) GetAvailablePrefixTitles() []*Title {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	result := make([]*Title, 0)

	for titleID := range ptl.titles {
		if masterTitle, exists := ptl.masterList.GetTitle(titleID); exists {
			if masterTitle.Position == TitlePositionPrefix {
				// Check if not expired
				if playerTitle := ptl.titles[titleID]; !playerTitle.IsExpired() {
					result = append(result, masterTitle)
				}
			}
		}
	}

	return result
}

// GetAvailableSuffixTitles returns all titles that can be used as suffix
func (ptl *PlayerTitlesList) GetAvailableSuffixTitles() []*Title {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	result := make([]*Title, 0)

	for titleID := range ptl.titles {
		if masterTitle, exists := ptl.masterList.GetTitle(titleID); exists {
			if masterTitle.Position == TitlePositionSuffix {
				// Check if not expired
				if playerTitle := ptl.titles[titleID]; !playerTitle.IsExpired() {
					result = append(result, masterTitle)
				}
			}
		}
	}

	return result
}

// CleanupExpiredTitles removes expired temporary titles
func (ptl *PlayerTitlesList) CleanupExpiredTitles() int {
	ptl.mutex.Lock()
	defer ptl.mutex.Unlock()

	expiredCount := 0
	expiredTitles := make([]int32, 0)

	// Find expired titles
	for titleID, playerTitle := range ptl.titles {
		if playerTitle.IsExpired() {
			expiredTitles = append(expiredTitles, titleID)
			expiredCount++
		}
	}

	// Remove expired titles
	for _, titleID := range expiredTitles {
		playerTitle := ptl.titles[titleID]

		// If this expired title is currently active, deactivate it
		if playerTitle.IsActive {
			if playerTitle.IsPrefix && ptl.activePrefixID == titleID {
				ptl.activePrefixID = TitleIDNone
			} else if !playerTitle.IsPrefix && ptl.activeSuffixID == titleID {
				ptl.activeSuffixID = TitleIDCitizen // Revert to default
			}
		}

		delete(ptl.titles, titleID)
	}

	return expiredCount
}

// GetTitleCount returns the number of titles owned by the player
func (ptl *PlayerTitlesList) GetTitleCount() int {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	return len(ptl.titles)
}

// BuildPacketData creates title data for network transmission
func (ptl *PlayerTitlesList) BuildPacketData(playerName string) *TitlePacketData {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	data := &TitlePacketData{
		PlayerID:      uint32(ptl.playerID),
		PlayerName:    playerName,
		PrefixTitle:   "",
		SuffixTitle:   "",
		SubTitle:      "", // TODO: Implement subtitle system
		LastName:      "", // TODO: Implement last name system
		Titles:        make([]TitleEntry, 0, len(ptl.titles)),
		CurrentPrefix: int16(ptl.activePrefixID),
		CurrentSuffix: int16(ptl.activeSuffixID),
	}

	// Get active prefix title name
	if prefixTitle, exists := ptl.GetActivePrefixTitle(); exists {
		data.PrefixTitle = prefixTitle.GetDisplayName()
	}

	// Get active suffix title name
	if suffixTitle, exists := ptl.GetActiveSuffixTitle(); exists {
		data.SuffixTitle = suffixTitle.GetDisplayName()
	}

	// Build title array for UI
	for titleID := range ptl.titles {
		if masterTitle, exists := ptl.masterList.GetTitle(titleID); exists {
			// Skip hidden titles unless it's a GM viewing
			// TODO: Add GM check parameter
			if masterTitle.IsHidden() {
				continue
			}

			// Skip expired titles
			if ptl.titles[titleID].IsExpired() {
				continue
			}

			entry := TitleEntry{
				Name:     masterTitle.GetDisplayName(),
				IsPrefix: masterTitle.Position == TitlePositionPrefix,
			}
			data.Titles = append(data.Titles, entry)
		}
	}

	data.NumTitles = uint16(len(data.Titles))

	return data
}

// GrantTitleFromAchievement grants a title when an achievement is completed
func (ptl *PlayerTitlesList) GrantTitleFromAchievement(achievementID uint32) error {
	// Find title associated with this achievement
	title, exists := ptl.masterList.GetTitleByAchievement(achievementID)
	if !exists {
		return nil // No title associated with this achievement
	}

	// Grant the title
	return ptl.AddTitle(title.ID, achievementID, 0)
}

// LoadFromDatabase loads player titles from the database
func (ptl *PlayerTitlesList) LoadFromDatabase(db *database.DB) error {
	ptl.mutex.Lock()
	defer ptl.mutex.Unlock()

	// Create player_titles table if it doesn't exist
	if err := db.Exec(`
		CREATE TABLE IF NOT EXISTS player_titles (
			player_id INTEGER NOT NULL,
			title_id INTEGER NOT NULL,
			achievement_id INTEGER,
			granted_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
			expiration_date TIMESTAMP,
			is_active INTEGER DEFAULT 0,
			PRIMARY KEY (player_id, title_id),
			FOREIGN KEY (title_id) REFERENCES titles(id)
		)
	`); err != nil {
		return fmt.Errorf("failed to create player_titles table: %w", err)
	}

	// Load all titles for this player
	err := db.Query("SELECT title_id, achievement_id, granted_date, expiration_date, is_active FROM player_titles WHERE player_id = ?", 
		func(row *database.Row) error {
			playerTitle := &PlayerTitle{
				TitleID:    int32(row.Int64(0)),
				PlayerID:   ptl.playerID,
				EarnedDate: time.Unix(row.Int64(2), 0),
			}

			// Handle nullable achievement_id
			if !row.IsNull(1) {
				playerTitle.AchievementID = uint32(row.Int64(1))
			}

			// Handle nullable expiration_date
			if !row.IsNull(3) {
				playerTitle.ExpiresAt = time.Unix(row.Int64(3), 0)
			}

			ptl.titles[playerTitle.TitleID] = playerTitle

			// Set active title if this one is active
			if row.Bool(4) {
				ptl.activePrefixID = playerTitle.TitleID
			}

			return nil
		}, ptl.playerID)

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

	return nil
}

// SaveToDatabase saves player titles to the database
func (ptl *PlayerTitlesList) SaveToDatabase(db *database.DB) error {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	return db.Transaction(func(txDB *database.DB) error {
		// Clear existing titles for this player
		if err := txDB.Exec("DELETE FROM player_titles WHERE player_id = ?", ptl.playerID); err != nil {
			return fmt.Errorf("failed to clear player titles: %w", err)
		}

		// Insert all current titles
		for _, playerTitle := range ptl.titles {
			var achievementID interface{}
			if playerTitle.AchievementID != 0 {
				achievementID = playerTitle.AchievementID
			}

			var expirationDate interface{}
			if !playerTitle.ExpiresAt.IsZero() {
				expirationDate = playerTitle.ExpiresAt.Unix()
			}

			isActive := 0
			if ptl.activePrefixID == playerTitle.TitleID {
				isActive = 1
			} else if ptl.activeSuffixID == playerTitle.TitleID {
				isActive = 1
			}

			err := txDB.Exec(`
				INSERT INTO player_titles (player_id, title_id, achievement_id, granted_date, expiration_date, is_active)
				VALUES (?, ?, ?, ?, ?, ?)
			`, ptl.playerID, playerTitle.TitleID, achievementID, 
			   playerTitle.EarnedDate.Unix(), expirationDate, isActive)

			if err != nil {
				return fmt.Errorf("failed to insert player title %d: %w", playerTitle.TitleID, err)
			}
		}

		return nil
	})
}

// GetFormattedName returns the player name with active titles applied
func (ptl *PlayerTitlesList) GetFormattedName(playerName string) string {
	ptl.mutex.RLock()
	defer ptl.mutex.RUnlock()

	result := playerName

	// Add prefix if active
	if prefixTitle, exists := ptl.GetActivePrefixTitle(); exists {
		result = prefixTitle.GetDisplayName() + " " + result
	}

	// Add suffix if active
	if suffixTitle, exists := ptl.GetActiveSuffixTitle(); exists {
		result = result + " " + suffixTitle.GetDisplayName()
	}

	return result
}
