package titles

import (
	"fmt"
	"sync"

	"eq2emu/internal/database"
)

// MasterTitlesList manages all available titles in the game
type MasterTitlesList struct {
	titles        map[int32]*Title    // All titles indexed by ID
	categorized   map[string][]*Title // Titles grouped by category
	bySource      map[int32][]*Title  // Titles grouped by source
	byRarity      map[int32][]*Title  // Titles grouped by rarity
	byAchievement map[uint32]*Title   // Titles indexed by achievement ID
	nextID        int32               // Next available title ID
	mutex         sync.RWMutex        // Thread safety
}

// NewMasterTitlesList creates a new master titles list
func NewMasterTitlesList() *MasterTitlesList {
	mtl := &MasterTitlesList{
		titles:        make(map[int32]*Title),
		categorized:   make(map[string][]*Title),
		bySource:      make(map[int32][]*Title),
		byRarity:      make(map[int32][]*Title),
		byAchievement: make(map[uint32]*Title),
		nextID:        1,
	}

	// Initialize default titles
	mtl.initializeDefaultTitles()

	return mtl
}

// initializeDefaultTitles creates the basic system titles
func (mtl *MasterTitlesList) initializeDefaultTitles() {
	// System titles with negative IDs
	citizen := NewTitle(TitleIDCitizen, "Citizen")
	citizen.SetDescription("Default citizen title")
	citizen.SetFlag(FlagStarter)
	citizen.Position = TitlePositionSuffix
	mtl.addTitleInternal(citizen)

	visitor := NewTitle(TitleIDVisitor, "Visitor")
	visitor.SetDescription("Temporary visitor status")
	visitor.SetFlag(FlagTemporary)
	visitor.Position = TitlePositionSuffix
	mtl.addTitleInternal(visitor)

	newcomer := NewTitle(TitleIDNewcomer, "Newcomer")
	newcomer.SetDescription("New player welcome title")
	newcomer.SetFlag(FlagStarter)
	newcomer.ExpirationHours = 168 // 1 week
	newcomer.Position = TitlePositionPrefix
	mtl.addTitleInternal(newcomer)

	returning := NewTitle(TitleIDReturning, "Returning")
	returning.SetDescription("Welcome back title for returning players")
	returning.SetFlag(FlagTemporary)
	returning.ExpirationHours = 72 // 3 days
	returning.Position = TitlePositionPrefix
	mtl.addTitleInternal(returning)
}

// AddTitle adds a new title to the master list
func (mtl *MasterTitlesList) AddTitle(title *Title) error {
	mtl.mutex.Lock()
	defer mtl.mutex.Unlock()

	if title == nil {
		return fmt.Errorf("cannot add nil title")
	}

	// Assign ID if not set
	if title.ID == 0 {
		title.ID = mtl.nextID
		mtl.nextID++
	}

	// Check for duplicate ID
	if _, exists := mtl.titles[title.ID]; exists {
		return fmt.Errorf("title with ID %d already exists", title.ID)
	}

	// Validate title name length
	if len(title.Name) > MaxTitleNameLength {
		return fmt.Errorf("title name exceeds maximum length of %d characters", MaxTitleNameLength)
	}

	// Validate description length
	if len(title.Description) > MaxTitleDescriptionLength {
		return fmt.Errorf("title description exceeds maximum length of %d characters", MaxTitleDescriptionLength)
	}

	// Check for unique titles
	if title.IsUnique() {
		// TODO: Check if any player already has this unique title
	}

	return mtl.addTitleInternal(title)
}

// addTitleInternal adds a title without validation (used internally)
func (mtl *MasterTitlesList) addTitleInternal(title *Title) error {
	// Add to main map
	mtl.titles[title.ID] = title

	// Add to category index
	if mtl.categorized[title.Category] == nil {
		mtl.categorized[title.Category] = make([]*Title, 0)
	}
	mtl.categorized[title.Category] = append(mtl.categorized[title.Category], title)

	// Add to source index
	if mtl.bySource[title.Source] == nil {
		mtl.bySource[title.Source] = make([]*Title, 0)
	}
	mtl.bySource[title.Source] = append(mtl.bySource[title.Source], title)

	// Add to rarity index
	if mtl.byRarity[title.Rarity] == nil {
		mtl.byRarity[title.Rarity] = make([]*Title, 0)
	}
	mtl.byRarity[title.Rarity] = append(mtl.byRarity[title.Rarity], title)

	// Add to achievement index if applicable
	if title.AchievementID > 0 {
		mtl.byAchievement[title.AchievementID] = title
	}

	// Update next ID if necessary
	if title.ID >= mtl.nextID {
		mtl.nextID = title.ID + 1
	}

	return nil
}

// GetTitle retrieves a title by ID
func (mtl *MasterTitlesList) GetTitle(id int32) (*Title, bool) {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

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

	return title.Clone(), true
}

// GetTitleByName retrieves a title by name (case-sensitive)
func (mtl *MasterTitlesList) GetTitleByName(name string) (*Title, bool) {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

	for _, title := range mtl.titles {
		if title.Name == name {
			return title.Clone(), true
		}
	}

	return nil, false
}

// GetTitleByAchievement retrieves a title associated with an achievement
func (mtl *MasterTitlesList) GetTitleByAchievement(achievementID uint32) (*Title, bool) {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

	title, exists := mtl.byAchievement[achievementID]
	if !exists {
		return nil, false
	}

	return title.Clone(), true
}

// GetTitlesByCategory retrieves all titles in a specific category
func (mtl *MasterTitlesList) GetTitlesByCategory(category string) []*Title {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

	titles := mtl.categorized[category]
	if titles == nil {
		return make([]*Title, 0)
	}

	// Return clones to prevent external modification
	result := make([]*Title, len(titles))
	for i, title := range titles {
		result[i] = title.Clone()
	}

	return result
}

// GetTitlesBySource retrieves all titles from a specific source
func (mtl *MasterTitlesList) GetTitlesBySource(source int32) []*Title {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

	titles := mtl.bySource[source]
	if titles == nil {
		return make([]*Title, 0)
	}

	// Return clones to prevent external modification
	result := make([]*Title, len(titles))
	for i, title := range titles {
		result[i] = title.Clone()
	}

	return result
}

// GetTitlesByRarity retrieves all titles of a specific rarity
func (mtl *MasterTitlesList) GetTitlesByRarity(rarity int32) []*Title {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

	titles := mtl.byRarity[rarity]
	if titles == nil {
		return make([]*Title, 0)
	}

	// Return clones to prevent external modification
	result := make([]*Title, len(titles))
	for i, title := range titles {
		result[i] = title.Clone()
	}

	return result
}

// GetAllTitles retrieves all titles (excluding hidden ones by default)
func (mtl *MasterTitlesList) GetAllTitles(includeHidden bool) []*Title {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

	result := make([]*Title, 0, len(mtl.titles))

	for _, title := range mtl.titles {
		if !includeHidden && title.IsHidden() {
			continue
		}
		result = append(result, title.Clone())
	}

	return result
}

// GetAvailableCategories returns all categories that have titles
func (mtl *MasterTitlesList) GetAvailableCategories() []string {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

	categories := make([]string, 0, len(mtl.categorized))
	for category := range mtl.categorized {
		categories = append(categories, category)
	}

	return categories
}

// RemoveTitle removes a title from the master list
func (mtl *MasterTitlesList) RemoveTitle(id int32) error {
	mtl.mutex.Lock()
	defer mtl.mutex.Unlock()

	title, exists := mtl.titles[id]
	if !exists {
		return fmt.Errorf("title with ID %d does not exist", id)
	}

	// Remove from main map
	delete(mtl.titles, id)

	// Remove from category index
	categorySlice := mtl.categorized[title.Category]
	mtl.removeFromSlice(&categorySlice, title)
	mtl.categorized[title.Category] = categorySlice
	if len(mtl.categorized[title.Category]) == 0 {
		delete(mtl.categorized, title.Category)
	}

	// Remove from source index
	sourceSlice := mtl.bySource[title.Source]
	mtl.removeFromSlice(&sourceSlice, title)
	mtl.bySource[title.Source] = sourceSlice
	if len(mtl.bySource[title.Source]) == 0 {
		delete(mtl.bySource, title.Source)
	}

	// Remove from rarity index
	raritySlice := mtl.byRarity[title.Rarity]
	mtl.removeFromSlice(&raritySlice, title)
	mtl.byRarity[title.Rarity] = raritySlice
	if len(mtl.byRarity[title.Rarity]) == 0 {
		delete(mtl.byRarity, title.Rarity)
	}

	// Remove from achievement index if applicable
	if title.AchievementID > 0 {
		delete(mtl.byAchievement, title.AchievementID)
	}

	return nil
}

// removeFromSlice removes a title from a slice
func (mtl *MasterTitlesList) removeFromSlice(slice *[]*Title, title *Title) {
	for i, t := range *slice {
		if t.ID == title.ID {
			*slice = append((*slice)[:i], (*slice)[i+1:]...)
			break
		}
	}
}

// UpdateTitle updates an existing title
func (mtl *MasterTitlesList) UpdateTitle(title *Title) error {
	mtl.mutex.Lock()
	defer mtl.mutex.Unlock()

	if title == nil {
		return fmt.Errorf("cannot update with nil title")
	}

	existing, exists := mtl.titles[title.ID]
	if !exists {
		return fmt.Errorf("title with ID %d does not exist", title.ID)
	}

	// Remove old title from indices
	categorySlice := mtl.categorized[existing.Category]
	mtl.removeFromSlice(&categorySlice, existing)
	mtl.categorized[existing.Category] = categorySlice
	
	sourceSlice := mtl.bySource[existing.Source]
	mtl.removeFromSlice(&sourceSlice, existing)
	mtl.bySource[existing.Source] = sourceSlice
	
	raritySlice := mtl.byRarity[existing.Rarity]
	mtl.removeFromSlice(&raritySlice, existing)
	mtl.byRarity[existing.Rarity] = raritySlice

	if existing.AchievementID > 0 {
		delete(mtl.byAchievement, existing.AchievementID)
	}

	// Update the title
	mtl.titles[title.ID] = title

	// Re-add to indices with new values
	if mtl.categorized[title.Category] == nil {
		mtl.categorized[title.Category] = make([]*Title, 0)
	}
	mtl.categorized[title.Category] = append(mtl.categorized[title.Category], title)

	if mtl.bySource[title.Source] == nil {
		mtl.bySource[title.Source] = make([]*Title, 0)
	}
	mtl.bySource[title.Source] = append(mtl.bySource[title.Source], title)

	if mtl.byRarity[title.Rarity] == nil {
		mtl.byRarity[title.Rarity] = make([]*Title, 0)
	}
	mtl.byRarity[title.Rarity] = append(mtl.byRarity[title.Rarity], title)

	if title.AchievementID > 0 {
		mtl.byAchievement[title.AchievementID] = title
	}

	return nil
}

// GetTitleCount returns the total number of titles
func (mtl *MasterTitlesList) GetTitleCount() int {
	mtl.mutex.RLock()
	defer mtl.mutex.RUnlock()

	return len(mtl.titles)
}

// ValidateTitle checks if a title meets all requirements
func (mtl *MasterTitlesList) ValidateTitle(title *Title) error {
	if title == nil {
		return fmt.Errorf("title cannot be nil")
	}

	if len(title.Name) == 0 {
		return fmt.Errorf("title name cannot be empty")
	}

	if len(title.Name) > MaxTitleNameLength {
		return fmt.Errorf("title name exceeds maximum length of %d characters", MaxTitleNameLength)
	}

	if len(title.Description) > MaxTitleDescriptionLength {
		return fmt.Errorf("title description exceeds maximum length of %d characters", MaxTitleDescriptionLength)
	}

	if len(title.Requirements) > MaxTitleRequirements {
		return fmt.Errorf("title has too many requirements (max %d)", MaxTitleRequirements)
	}

	// Validate position
	if title.Position != TitlePositionPrefix && title.Position != TitlePositionSuffix {
		return fmt.Errorf("invalid title position: %d", title.Position)
	}

	// Validate rarity
	if title.Rarity < TitleRarityCommon || title.Rarity > TitleRarityUnique {
		return fmt.Errorf("invalid title rarity: %d", title.Rarity)
	}

	return nil
}

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

	// Create titles table if it doesn't exist
	if err := db.Exec(`
		CREATE TABLE IF NOT EXISTS titles (
			id INTEGER PRIMARY KEY,
			name TEXT NOT NULL,
			description TEXT,
			category TEXT,
			position INTEGER NOT NULL DEFAULT 0,
			source INTEGER NOT NULL DEFAULT 0,
			rarity INTEGER NOT NULL DEFAULT 0,
			flags INTEGER NOT NULL DEFAULT 0,
			achievement_id INTEGER,
			created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
			updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
		)
	`); err != nil {
		return fmt.Errorf("failed to create titles table: %w", err)
	}

	// Load all titles from database
	err := db.Query("SELECT id, name, description, category, position, source, rarity, flags, achievement_id FROM titles", func(row *database.Row) error {
		title := &Title{
			ID:          int32(row.Int64(0)),
			Name:        row.Text(1),
			Description: row.Text(2),
			Category:    row.Text(3),
			Position:    int32(row.Int(4)),
			Source:      int32(row.Int(5)),
			Rarity:      int32(row.Int(6)),
			Flags:       uint32(row.Int64(7)),
		}

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

		mtl.addTitleInternal(title)
		return nil
	})

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

	return nil
}

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

	return db.Transaction(func(txDB *database.DB) error {
		// Clear existing titles (this is a full sync)
		if err := txDB.Exec("DELETE FROM titles"); err != nil {
			return fmt.Errorf("failed to clear titles table: %w", err)
		}

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

			err := txDB.Exec(`
				INSERT INTO titles (id, name, description, category, position, source, rarity, flags, achievement_id)
				VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
			`, title.ID, title.Name, title.Description, title.Category, 
			   int(title.Position), int(title.Source), int(title.Rarity), 
			   int64(title.Flags), achievementID)

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

		return nil
	})
}
