package skills

import (
	"math/rand"
	"sync"
)

// PlayerSkillList manages skills for a specific player
type PlayerSkillList struct {
	skills         map[int32]*Skill      // Player's skills by ID
	nameSkillMap   map[string]*Skill     // Skills by name for quick lookup
	skillUpdates   []*Skill              // Skills needing updates
	skillBonusList map[int32]*SkillBonus // Skill bonuses by spell ID

	// Packet data for skill updates
	origPacket     []byte
	xorPacket      []byte
	origPacketSize int16
	packetCount    int16

	hasUpdates   bool
	mutex        sync.RWMutex // Thread safety for skills/nameMap
	updatesMutex sync.Mutex   // Thread safety for updates
	bonusMutex   sync.RWMutex // Thread safety for bonuses
}

// NewPlayerSkillList creates a new player skill list
func NewPlayerSkillList() *PlayerSkillList {
	return &PlayerSkillList{
		skills:         make(map[int32]*Skill),
		nameSkillMap:   make(map[string]*Skill),
		skillUpdates:   make([]*Skill, 0),
		skillBonusList: make(map[int32]*SkillBonus),
		hasUpdates:     false,
	}
}

// AddSkill adds a skill to the player's skill list
func (psl *PlayerSkillList) AddSkill(newSkill *Skill) {
	if newSkill == nil {
		return
	}

	psl.mutex.Lock()
	defer psl.mutex.Unlock()

	// Remove old skill if it exists
	if oldSkill, exists := psl.skills[newSkill.SkillID]; exists {
		// TODO: Set Lua user data stale when LuaInterface is integrated
		_ = oldSkill
	}

	psl.skills[newSkill.SkillID] = newSkill

	// Clear name map cache so it gets rebuilt
	psl.nameSkillMap = make(map[string]*Skill)
}

// RemoveSkill removes a skill from the player's skill list
func (psl *PlayerSkillList) RemoveSkill(skill *Skill) {
	if skill == nil {
		return
	}

	psl.mutex.Lock()
	defer psl.mutex.Unlock()

	// TODO: Set Lua user data stale when LuaInterface is integrated
	skill.ActiveSkill = false

	// Clear name map cache
	psl.nameSkillMap = make(map[string]*Skill)
}

// GetAllSkills returns all player skills
func (psl *PlayerSkillList) GetAllSkills() map[int32]*Skill {
	psl.mutex.RLock()
	defer psl.mutex.RUnlock()

	// Return a copy to prevent external modification
	skills := make(map[int32]*Skill)
	for id, skill := range psl.skills {
		skills[id] = skill
	}

	return skills
}

// HasSkill checks if player has a specific skill
func (psl *PlayerSkillList) HasSkill(skillID int32) bool {
	psl.mutex.RLock()
	defer psl.mutex.RUnlock()

	skill, exists := psl.skills[skillID]
	return exists && skill.ActiveSkill
}

// GetSkill returns a skill by ID
func (psl *PlayerSkillList) GetSkill(skillID int32) *Skill {
	psl.mutex.RLock()
	defer psl.mutex.RUnlock()

	if skill, exists := psl.skills[skillID]; exists && skill.ActiveSkill {
		return skill
	}

	return nil
}

// GetSkillByName returns a skill by name
func (psl *PlayerSkillList) GetSkillByName(name string) *Skill {
	psl.mutex.Lock()
	defer psl.mutex.Unlock()

	// Build name map if empty
	if len(psl.nameSkillMap) == 0 {
		for _, skill := range psl.skills {
			if skill.ActiveSkill {
				psl.nameSkillMap[skill.Name.Data] = skill
			}
		}
	}

	if skill, exists := psl.nameSkillMap[name]; exists {
		return skill
	}

	return nil
}

// IncreaseSkill increases a skill's current value
func (psl *PlayerSkillList) IncreaseSkill(skill *Skill, amount int16) {
	if skill == nil {
		return
	}

	skill.PreviousVal = skill.CurrentVal
	skill.CurrentVal += amount

	if skill.CurrentVal > skill.MaxVal {
		skill.MaxVal = skill.CurrentVal
	}

	psl.AddSkillUpdateNeeded(skill)
	skill.SaveNeeded = true
}

// IncreaseSkillByID increases a skill's current value by ID
func (psl *PlayerSkillList) IncreaseSkillByID(skillID int32, amount int16) {
	skill := psl.GetSkill(skillID)
	psl.IncreaseSkill(skill, amount)
}

// DecreaseSkill decreases a skill's current value
func (psl *PlayerSkillList) DecreaseSkill(skill *Skill, amount int16) {
	if skill == nil {
		return
	}

	skill.PreviousVal = skill.CurrentVal

	if skill.CurrentVal < amount {
		skill.CurrentVal = 0
	} else {
		skill.CurrentVal -= amount
	}

	skill.SaveNeeded = true
	psl.AddSkillUpdateNeeded(skill)
}

// DecreaseSkillByID decreases a skill's current value by ID
func (psl *PlayerSkillList) DecreaseSkillByID(skillID int32, amount int16) {
	skill := psl.GetSkill(skillID)
	psl.DecreaseSkill(skill, amount)
}

// SetSkill sets a skill's current value
func (psl *PlayerSkillList) SetSkill(skill *Skill, value int16, sendUpdate bool) {
	if skill == nil {
		return
	}

	skill.PreviousVal = skill.CurrentVal
	skill.CurrentVal = value

	if skill.CurrentVal > skill.MaxVal {
		skill.MaxVal = skill.CurrentVal
	}

	skill.SaveNeeded = true

	if sendUpdate {
		psl.AddSkillUpdateNeeded(skill)
	}
}

// SetSkillByID sets a skill's current value by ID
func (psl *PlayerSkillList) SetSkillByID(skillID int32, value int16, sendUpdate bool) {
	skill := psl.GetSkill(skillID)
	psl.SetSkill(skill, value, sendUpdate)
}

// IncreaseSkillCap increases a skill's maximum value
func (psl *PlayerSkillList) IncreaseSkillCap(skill *Skill, amount int16) {
	if skill == nil {
		return
	}

	skill.MaxVal += amount
	skill.SaveNeeded = true
}

// IncreaseSkillCapByID increases a skill's maximum value by ID
func (psl *PlayerSkillList) IncreaseSkillCapByID(skillID int32, amount int16) {
	skill := psl.GetSkill(skillID)
	psl.IncreaseSkillCap(skill, amount)
}

// DecreaseSkillCap decreases a skill's maximum value
func (psl *PlayerSkillList) DecreaseSkillCap(skill *Skill, amount int16) {
	if skill == nil {
		return
	}

	if skill.MaxVal < amount {
		skill.MaxVal = 0
	} else {
		skill.MaxVal -= amount
	}

	// Adjust current value if it exceeds new max
	if skill.CurrentVal > skill.MaxVal {
		skill.PreviousVal = skill.CurrentVal
		skill.CurrentVal = skill.MaxVal
	}

	psl.AddSkillUpdateNeeded(skill)
	skill.SaveNeeded = true
}

// DecreaseSkillCapByID decreases a skill's maximum value by ID
func (psl *PlayerSkillList) DecreaseSkillCapByID(skillID int32, amount int16) {
	skill := psl.GetSkill(skillID)
	psl.DecreaseSkillCap(skill, amount)
}

// SetSkillCap sets a skill's maximum value
func (psl *PlayerSkillList) SetSkillCap(skill *Skill, value int16) {
	if skill == nil {
		return
	}

	skill.MaxVal = value

	// Adjust current value if it exceeds new max
	if skill.CurrentVal > skill.MaxVal {
		skill.PreviousVal = skill.CurrentVal
		skill.CurrentVal = skill.MaxVal
	}

	psl.AddSkillUpdateNeeded(skill)
	skill.SaveNeeded = true
}

// SetSkillCapByID sets a skill's maximum value by ID
func (psl *PlayerSkillList) SetSkillCapByID(skillID int32, value int16) {
	skill := psl.GetSkill(skillID)
	psl.SetSkillCap(skill, value)
}

// SetSkillValuesByType sets all skills of a type to a specific value
func (psl *PlayerSkillList) SetSkillValuesByType(skillType int8, value int16, sendUpdate bool) {
	psl.mutex.RLock()
	defer psl.mutex.RUnlock()

	for _, skill := range psl.skills {
		if skill != nil && skill.SkillType == int32(skillType) {
			psl.SetSkill(skill, value, sendUpdate)
		}
	}
}

// SetSkillCapsByType sets all skill caps of a type to a specific value
func (psl *PlayerSkillList) SetSkillCapsByType(skillType int8, value int16) {
	psl.mutex.RLock()
	defer psl.mutex.RUnlock()

	for _, skill := range psl.skills {
		if skill != nil && skill.SkillType == int32(skillType) {
			psl.SetSkillCap(skill, value)
		}
	}
}

// IncreaseSkillCapsByType increases all skill caps of a type
func (psl *PlayerSkillList) IncreaseSkillCapsByType(skillType int8, value int16) {
	psl.mutex.RLock()
	defer psl.mutex.RUnlock()

	for _, skill := range psl.skills {
		if skill != nil && skill.SkillType == int32(skillType) {
			psl.IncreaseSkillCap(skill, value)
		}
	}
}

// IncreaseAllSkillCaps increases all skill caps
func (psl *PlayerSkillList) IncreaseAllSkillCaps(value int16) {
	psl.mutex.RLock()
	defer psl.mutex.RUnlock()

	for _, skill := range psl.skills {
		if skill != nil {
			psl.IncreaseSkillCap(skill, value)
		}
	}
}

// CheckSkillIncrease checks if a skill should increase and does so if successful
func (psl *PlayerSkillList) CheckSkillIncrease(skill *Skill) bool {
	if skill == nil || skill.CurrentVal >= skill.MaxVal {
		return false
	}

	// Calculate increase chance: skill level 1 = 20%, 100 = 10%, 400 = 4%
	percent := int8((100.0 / float32(50+skill.CurrentVal)) * 10.0)

	if rand.Intn(100) < int(percent) {
		psl.IncreaseSkill(skill, 1)
		return true
	}

	return false
}

// AddSkillUpdateNeeded marks a skill as needing an update packet
func (psl *PlayerSkillList) AddSkillUpdateNeeded(skill *Skill) {
	if skill == nil {
		return
	}

	psl.updatesMutex.Lock()
	defer psl.updatesMutex.Unlock()

	psl.skillUpdates = append(psl.skillUpdates, skill)
	psl.hasUpdates = true
}

// HasSkillUpdates returns whether there are pending skill updates
func (psl *PlayerSkillList) HasSkillUpdates() bool {
	psl.updatesMutex.Lock()
	defer psl.updatesMutex.Unlock()

	return psl.hasUpdates
}

// GetSkillUpdates returns and clears pending skill updates
func (psl *PlayerSkillList) GetSkillUpdates() []*Skill {
	psl.updatesMutex.Lock()
	defer psl.updatesMutex.Unlock()

	if len(psl.skillUpdates) == 0 {
		return nil
	}

	updates := make([]*Skill, len(psl.skillUpdates))
	copy(updates, psl.skillUpdates)

	// Clear the updates
	psl.skillUpdates = psl.skillUpdates[:0]
	psl.hasUpdates = false

	return updates
}

// GetSaveNeededSkills returns skills that need to be saved to database
func (psl *PlayerSkillList) GetSaveNeededSkills() []*Skill {
	psl.mutex.RLock()
	defer psl.mutex.RUnlock()

	var saveNeeded []*Skill

	for _, skill := range psl.skills {
		if skill.SaveNeeded {
			saveNeeded = append(saveNeeded, skill)
			skill.SaveNeeded = false // Clear the flag
		}
	}

	return saveNeeded
}

// ResetPackets clears cached packet data
func (psl *PlayerSkillList) ResetPackets() {
	psl.updatesMutex.Lock()
	defer psl.updatesMutex.Unlock()

	psl.origPacket = nil
	psl.xorPacket = nil
	psl.origPacketSize = 0
	psl.packetCount = 0
}

// GetSkillPacket builds a skill update packet for the client
func (psl *PlayerSkillList) GetSkillPacket(version int16) ([]byte, error) {
	psl.mutex.Lock()
	defer psl.mutex.Unlock()

	// This is a placeholder implementation
	// In the full implementation, this would use the PacketStruct system
	// to build a WS_UpdateSkillBook packet with all player skills

	// TODO: Implement packet building using PacketStruct system
	// packet := configReader.getStruct("WS_UpdateSkillBook", version)
	// [complex packet building logic here]

	// For now, return empty packet
	return make([]byte, 0), nil
}
