package traits

import (
	"sync"
)

// TraitData represents a single trait that can be learned by players.
// Converted from the C++ TraitData struct with all original fields preserved.
type TraitData struct {
	SpellID       uint32 // ID of the spell associated with this trait
	Level         int8   // Required level to learn this trait
	ClassReq      int8   // Required class (255 = any class)
	RaceReq       int8   // Required race (255 = any race)
	IsTrait       bool   // Whether this is a regular trait
	IsInnate      bool   // Whether this is an innate racial ability
	IsFocusEffect bool   // Whether this is a focus effect
	IsTraining    bool   // Whether this is a training ability
	Tier          int8   // Spell tier for this trait
	Group         int8   // Trait group/category (TRAITS_* constants)
	ItemID        uint32 // Associated item ID (if any)
}

// MasterTraitList manages all available traits in the game.
// Converted from the C++ MasterTraitList class with thread-safe operations.
type MasterTraitList struct {
	traitList []TraitData  // List of all available traits
	mutex     sync.RWMutex // Protects concurrent access to trait list
}

// TraitLists contains organized trait data for a specific player.
// Used to categorize traits by type for efficient processing.
type TraitLists struct {
	// SortedTraitList organizes character traits by group and level
	// Map structure: [group][level] -> []*TraitData
	SortedTraitList map[int8]map[int8][]*TraitData

	// ClassTraining contains training abilities for the player's class
	// Map structure: [level] -> []*TraitData
	ClassTraining map[int8][]*TraitData

	// RaceTraits contains racial abilities (non-innate)
	// Map structure: [group] -> []*TraitData
	RaceTraits map[int8][]*TraitData

	// InnateRaceTraits contains innate racial abilities
	// Map structure: [group] -> []*TraitData
	InnateRaceTraits map[int8][]*TraitData

	// FocusEffects contains focus effect abilities
	// Map structure: [group] -> []*TraitData
	FocusEffects map[int8][]*TraitData
}

// TraitSelectionContext holds state during trait selection processing.
type TraitSelectionContext struct {
	CollectTraits         []*TraitData    // All potential traits for selection
	TieredTraits          []*TraitData    // Traits in current tier selection
	PreviousMatchedSpells map[uint32]int8 // Previously matched spells and their groups
	FoundSpellMatch       bool            // Whether a spell match was found in current group
	GroupToApply          int8            // Current group being processed
	TieredSelection       bool            // Whether tiered selection is enabled
}

// TraitPacketData contains data needed to build trait list packets.
type TraitPacketData struct {
	// Character traits organized by level
	CharacterTraits []TraitLevelData

	// Class training abilities organized by level
	ClassTraining []TraitLevelData

	// Racial traits organized by group
	RacialTraits []RacialTraitGroup

	// Innate racial abilities
	InnateAbilities []TraitInfo

	// Focus effects (client version >= 1188)
	FocusEffects []TraitInfo

	// Selection availability
	RacialSelectionsAvailable int8
	FocusSelectionsAvailable  int8
}

// TraitLevelData represents traits available at a specific level.
type TraitLevelData struct {
	Level        int8        // Required level for these traits
	SelectedLine int8        // Which trait is selected (255 = none, 0-4 = trait index)
	Traits       []TraitInfo // Available traits at this level (max 5)
}

// RacialTraitGroup represents a group of racial traits.
type RacialTraitGroup struct {
	GroupName string      // Display name for this group
	Traits    []TraitInfo // Traits in this group
}

// TraitInfo contains display information for a single trait.
type TraitInfo struct {
	SpellID  uint32 // Spell ID for this trait
	Name     string // Display name
	Icon     uint16 // Icon ID for display
	Icon2    uint16 // Secondary icon ID (backdrop)
	Selected bool   // Whether player has selected this trait
	Unknown1 uint32 // Unknown field 1 (usually 1)
	Unknown2 uint32 // Unknown field 2 (usually 1)
}

// TraitSelectionRequest represents a request to select traits.
type TraitSelectionRequest struct {
	PlayerID    uint32   // ID of player making selection
	TraitSpells []uint32 // Spell IDs of traits being selected
	PacketType  int8     // Type of trait selection packet
}

// TraitValidationResult contains the result of trait validation.
type TraitValidationResult struct {
	Allowed    bool   // Whether the trait selection is allowed
	Reason     string // Reason why not allowed (if applicable)
	UsedSlots  int16  // Number of trait slots currently used
	MaxSlots   int16  // Maximum trait slots available
	ClassicReq int16  // Classic level requirement (if applicable)
}

// TraitManagerStats tracks statistics for the trait system.
type TraitManagerStats struct {
	TotalTraits       int32            // Total number of traits in system
	TraitsByType      map[string]int32 // Number of traits by type
	TraitsByGroup     map[int8]int32   // Number of traits by group
	TraitsByLevel     map[int8]int32   // Number of traits by level requirement
	PlayersWithTraits int32            // Number of players with trait selections
}

// PlayerTraitState represents a player's current trait selections and availability.
type PlayerTraitState struct {
	PlayerID        uint32           // Player ID
	Level           int16            // Current player level
	Class           int8             // Player's adventure class
	Race            int8             // Player's race
	NeedTraitUpdate bool             // Whether trait lists need refresh
	TraitLists      *TraitLists      // Organized trait lists
	SelectedTraits  map[uint32]bool  // Currently selected traits (spellID -> selected)
	AvailableSlots  map[string]int16 // Available slots by trait type
	UsedSlots       map[string]int16 // Used slots by trait type
	LastUpdate      int64            // Timestamp of last update
}

// TraitSystemConfig holds configuration for the trait system.
type TraitSystemConfig struct {
	TieringSelection     bool  // Enable tiered trait selection
	UseClassicLevelTable bool  // Use classic EQ2 level requirements
	FocusSelectLevel     int32 // Level interval for focus effects
	TrainingSelectLevel  int32 // Level interval for training abilities
	RaceSelectLevel      int32 // Level interval for racial abilities
	CharacterSelectLevel int32 // Level interval for character traits
}

// Copy creates a deep copy of a TraitData.
func (td *TraitData) Copy() *TraitData {
	if td == nil {
		return nil
	}

	return &TraitData{
		SpellID:       td.SpellID,
		Level:         td.Level,
		ClassReq:      td.ClassReq,
		RaceReq:       td.RaceReq,
		IsTrait:       td.IsTrait,
		IsInnate:      td.IsInnate,
		IsFocusEffect: td.IsFocusEffect,
		IsTraining:    td.IsTraining,
		Tier:          td.Tier,
		Group:         td.Group,
		ItemID:        td.ItemID,
	}
}

// IsUniversalTrait checks if this trait is available to all classes and races.
func (td *TraitData) IsUniversalTrait() bool {
	return td.ClassReq == UniversalClassReq && td.RaceReq == UniversalRaceReq && td.IsTrait
}

// IsForClass checks if this trait is available for the specified class.
func (td *TraitData) IsForClass(classID int8) bool {
	return td.ClassReq == UniversalClassReq || td.ClassReq == classID
}

// IsForRace checks if this trait is available for the specified race.
func (td *TraitData) IsForRace(raceID int8) bool {
	return td.RaceReq == UniversalRaceReq || td.RaceReq == raceID
}

// GetTraitType returns a string description of the trait type.
func (td *TraitData) GetTraitType() string {
	if td.IsFocusEffect {
		return "Focus Effect"
	}
	if td.IsTraining {
		return "Training"
	}
	if td.IsInnate {
		return "Innate Racial"
	}
	if td.RaceReq != UniversalRaceReq {
		return "Racial"
	}
	if td.IsTrait {
		return "Character Trait"
	}
	return "Unknown"
}

// Validate checks if the trait data is valid.
func (td *TraitData) Validate() error {
	if td.SpellID == 0 {
		return ErrInvalidSpellID
	}
	if td.Level < 0 {
		return ErrInvalidLevel
	}
	if td.Group < 0 || td.Group > TraitsTradeskill {
		return ErrInvalidGroup
	}
	return nil
}

// NewTraitLists creates a new initialized TraitLists structure.
func NewTraitLists() *TraitLists {
	return &TraitLists{
		SortedTraitList:  make(map[int8]map[int8][]*TraitData),
		ClassTraining:    make(map[int8][]*TraitData),
		RaceTraits:       make(map[int8][]*TraitData),
		InnateRaceTraits: make(map[int8][]*TraitData),
		FocusEffects:     make(map[int8][]*TraitData),
	}
}

// Clear removes all trait data from the lists.
func (tl *TraitLists) Clear() {
	tl.SortedTraitList = make(map[int8]map[int8][]*TraitData)
	tl.ClassTraining = make(map[int8][]*TraitData)
	tl.RaceTraits = make(map[int8][]*TraitData)
	tl.InnateRaceTraits = make(map[int8][]*TraitData)
	tl.FocusEffects = make(map[int8][]*TraitData)
}

// NewTraitSelectionContext creates a new trait selection context.
func NewTraitSelectionContext(tieredSelection bool) *TraitSelectionContext {
	return &TraitSelectionContext{
		CollectTraits:         make([]*TraitData, 0),
		TieredTraits:          make([]*TraitData, 0),
		PreviousMatchedSpells: make(map[uint32]int8),
		GroupToApply:          UnassignedGroupID,
		TieredSelection:       tieredSelection,
	}
}

// Reset clears the context for reuse.
func (tsc *TraitSelectionContext) Reset() {
	tsc.CollectTraits = tsc.CollectTraits[:0]
	tsc.TieredTraits = tsc.TieredTraits[:0]
	tsc.PreviousMatchedSpells = make(map[uint32]int8)
	tsc.FoundSpellMatch = false
	tsc.GroupToApply = UnassignedGroupID
}

// NewPlayerTraitState creates a new player trait state.
func NewPlayerTraitState(playerID uint32, level int16, classID, raceID int8) *PlayerTraitState {
	return &PlayerTraitState{
		PlayerID:        playerID,
		Level:           level,
		Class:           classID,
		Race:            raceID,
		NeedTraitUpdate: true,
		TraitLists:      NewTraitLists(),
		SelectedTraits:  make(map[uint32]bool),
		AvailableSlots:  make(map[string]int16),
		UsedSlots:       make(map[string]int16),
	}
}

// UpdateLevel updates the player's level and marks traits for refresh.
func (pts *PlayerTraitState) UpdateLevel(newLevel int16) {
	if pts.Level != newLevel {
		pts.Level = newLevel
		pts.NeedTraitUpdate = true
	}
}

// SelectTrait marks a trait as selected.
func (pts *PlayerTraitState) SelectTrait(spellID uint32) {
	pts.SelectedTraits[spellID] = true
}

// UnselectTrait marks a trait as not selected.
func (pts *PlayerTraitState) UnselectTrait(spellID uint32) {
	delete(pts.SelectedTraits, spellID)
}

// HasTrait checks if a trait is selected.
func (pts *PlayerTraitState) HasTrait(spellID uint32) bool {
	return pts.SelectedTraits[spellID]
}

// GetSelectedTraitCount returns the number of selected traits.
func (pts *PlayerTraitState) GetSelectedTraitCount() int {
	return len(pts.SelectedTraits)
}

// Common error types for trait system
var (
	ErrInvalidSpellID    = NewTraitError("invalid spell ID")
	ErrInvalidLevel      = NewTraitError("invalid level")
	ErrInvalidGroup      = NewTraitError("invalid trait group")
	ErrInvalidPlayer     = NewTraitError("invalid player")
	ErrTraitNotFound     = NewTraitError("trait not found")
	ErrNotAllowed        = NewTraitError("trait selection not allowed")
	ErrInsufficientLevel = NewTraitError("insufficient level")
	ErrMaxTraitsReached  = NewTraitError("maximum traits reached")
)

// TraitError represents an error in the trait system.
type TraitError struct {
	message string
}

// NewTraitError creates a new trait error.
func NewTraitError(message string) *TraitError {
	return &TraitError{message: message}
}

// Error returns the error message.
func (e *TraitError) Error() string {
	return e.message
}

// IsTraitError checks if an error is a trait error.
func IsTraitError(err error) bool {
	_, ok := err.(*TraitError)
	return ok
}
