package spells

import (
	"sync"
	"time"
)

// BonusValues represents a stat bonus from equipment, spells, or other sources
// Moved from entity package to centralize spell-related structures
type BonusValues struct {
	SpellID    int32   // ID of spell providing this bonus
	Tier       int8    // Tier of the bonus
	Type       int16   // Type of bonus (stat type)
	Value      float32 // Bonus value
	ClassReq   int64   // Required class bitmask
	RaceReq    []int16 // Required race IDs
	FactionReq []int16 // Required faction IDs
	// TODO: Add LuaSpell reference when spell system is implemented
	// LuaSpell   *LuaSpell // Associated Lua spell
}

// NewBonusValues creates a new bonus value entry
func NewBonusValues(spellID int32, bonusType int16, value float32) *BonusValues {
	return &BonusValues{
		SpellID:    spellID,
		Tier:       1,
		Type:       bonusType,
		Value:      value,
		ClassReq:   0,
		RaceReq:    make([]int16, 0),
		FactionReq: make([]int16, 0),
	}
}

// MeetsRequirements checks if an entity meets the requirements for this bonus
func (bv *BonusValues) MeetsRequirements(entityClass int64, race int16, factionID int32) bool {
	// Check class requirement
	if bv.ClassReq != 0 && (entityClass&bv.ClassReq) == 0 {
		return false
	}

	// Check race requirement
	if len(bv.RaceReq) > 0 {
		raceMatch := false
		for _, reqRace := range bv.RaceReq {
			if reqRace == race {
				raceMatch = true
				break
			}
		}
		if !raceMatch {
			return false
		}
	}

	// Check faction requirement
	if len(bv.FactionReq) > 0 {
		factionMatch := false
		for _, reqFaction := range bv.FactionReq {
			if reqFaction == int16(factionID) {
				factionMatch = true
				break
			}
		}
		if !factionMatch {
			return false
		}
	}

	return true
}

// MaintainedEffects represents a buff that is actively maintained on an entity
// Moved from entity package to centralize spell-related structures
type MaintainedEffects struct {
	Name             [60]byte // Name of the spell
	Target           int32    // Target entity ID
	TargetType       int8     // Type of target
	SpellID          int32    // Spell ID
	InheritedSpellID int32    // Inherited spell ID (for spell upgrades)
	SlotPos          int32    // Slot position in maintained effects
	Icon             int16    // Icon to display
	IconBackdrop     int16    // Icon backdrop
	ConcUsed         int8     // Concentration used
	Tier             int8     // Spell tier
	TotalTime        float32  // Total duration of effect
	ExpireTimestamp  int32    // When the effect expires
	// TODO: Add LuaSpell reference when spell system is implemented
	// Spell            *LuaSpell // Associated Lua spell
}

// NewMaintainedEffects creates a new maintained effect
func NewMaintainedEffects(name string, spellID int32, duration float32) *MaintainedEffects {
	effect := &MaintainedEffects{
		Target:           0,
		TargetType:       0,
		SpellID:          spellID,
		InheritedSpellID: 0,
		SlotPos:          0,
		Icon:             0,
		IconBackdrop:     0,
		ConcUsed:         0,
		Tier:             1,
		TotalTime:        duration,
		ExpireTimestamp:  int32(time.Now().Unix()) + int32(duration),
	}

	// Copy name to fixed-size array
	nameBytes := []byte(name)
	copy(effect.Name[:], nameBytes)

	return effect
}

// GetName returns the spell name as a string
func (me *MaintainedEffects) GetName() string {
	// Find the null terminator
	nameBytes := me.Name[:]
	for i, b := range nameBytes {
		if b == 0 {
			return string(nameBytes[:i])
		}
	}
	return string(nameBytes)
}

// IsExpired checks if the maintained effect has expired
func (me *MaintainedEffects) IsExpired() bool {
	if me.TotalTime <= 0 {
		return false // Permanent effect
	}
	return int32(time.Now().Unix()) >= me.ExpireTimestamp
}

// GetTimeRemaining returns the time remaining for this effect
func (me *MaintainedEffects) GetTimeRemaining() float32 {
	if me.TotalTime <= 0 {
		return -1 // Permanent effect
	}

	remaining := float32(me.ExpireTimestamp - int32(time.Now().Unix()))
	if remaining < 0 {
		return 0
	}
	return remaining
}

// SpellEffects represents a temporary spell effect on an entity
// Moved from entity package to centralize spell-related structures
type SpellEffects struct {
	SpellID          int32 // Spell ID
	InheritedSpellID int32 // Inherited spell ID
	// TODO: Add Entity reference when implemented
	// Caster           *Entity // Entity that cast the spell
	CasterID        int32   // ID of caster entity (temporary)
	TotalTime       float32 // Total duration
	ExpireTimestamp int32   // When effect expires
	Icon            int16   // Icon to display
	IconBackdrop    int16   // Icon backdrop
	Tier            int8    // Spell tier
	// TODO: Add LuaSpell reference when spell system is implemented
	// Spell            *LuaSpell // Associated Lua spell
}

// NewSpellEffects creates a new spell effect
func NewSpellEffects(spellID int32, casterID int32, duration float32) *SpellEffects {
	return &SpellEffects{
		SpellID:          spellID,
		InheritedSpellID: 0,
		CasterID:         casterID,
		TotalTime:        duration,
		ExpireTimestamp:  int32(time.Now().Unix()) + int32(duration),
		Icon:             0,
		IconBackdrop:     0,
		Tier:             1,
	}
}

// IsExpired checks if the spell effect has expired
func (se *SpellEffects) IsExpired() bool {
	if se.TotalTime <= 0 {
		return false // Permanent effect
	}
	return int32(time.Now().Unix()) >= se.ExpireTimestamp
}

// GetTimeRemaining returns the time remaining for this effect
func (se *SpellEffects) GetTimeRemaining() float32 {
	if se.TotalTime <= 0 {
		return -1 // Permanent effect
	}

	remaining := float32(se.ExpireTimestamp - int32(time.Now().Unix()))
	if remaining < 0 {
		return 0
	}
	return remaining
}

// DetrimentalEffects represents a debuff or harmful effect on an entity
// Moved from entity package to centralize spell-related structures
type DetrimentalEffects struct {
	SpellID          int32 // Spell ID
	InheritedSpellID int32 // Inherited spell ID
	// TODO: Add Entity reference when implemented
	// Caster           *Entity // Entity that cast the spell
	CasterID        int32   // ID of caster entity (temporary)
	ExpireTimestamp int32   // When effect expires
	Icon            int16   // Icon to display
	IconBackdrop    int16   // Icon backdrop
	Tier            int8    // Spell tier
	DetType         int8    // Detrimental type
	Incurable       bool    // Cannot be cured
	ControlEffect   int8    // Control effect type
	TotalTime       float32 // Total duration
	// TODO: Add LuaSpell reference when spell system is implemented
	// Spell            *LuaSpell // Associated Lua spell
}

// NewDetrimentalEffects creates a new detrimental effect
func NewDetrimentalEffects(spellID int32, casterID int32, duration float32) *DetrimentalEffects {
	return &DetrimentalEffects{
		SpellID:          spellID,
		InheritedSpellID: 0,
		CasterID:         casterID,
		ExpireTimestamp:  int32(time.Now().Unix()) + int32(duration),
		Icon:             0,
		IconBackdrop:     0,
		Tier:             1,
		DetType:          0,
		Incurable:        false,
		ControlEffect:    0,
		TotalTime:        duration,
	}
}

// IsExpired checks if the detrimental effect has expired
func (de *DetrimentalEffects) IsExpired() bool {
	if de.TotalTime <= 0 {
		return false // Permanent effect
	}
	return int32(time.Now().Unix()) >= de.ExpireTimestamp
}

// GetTimeRemaining returns the time remaining for this effect
func (de *DetrimentalEffects) GetTimeRemaining() float32 {
	if de.TotalTime <= 0 {
		return -1 // Permanent effect
	}

	remaining := float32(de.ExpireTimestamp - int32(time.Now().Unix()))
	if remaining < 0 {
		return 0
	}
	return remaining
}

// IsControlEffect checks if this detrimental is a control effect
func (de *DetrimentalEffects) IsControlEffect() bool {
	return de.ControlEffect > 0
}

// SpellEffectManager manages all spell effects for an entity
// Moved from entity package to centralize spell-related structures
type SpellEffectManager struct {
	// Maintained effects (buffs that use concentration)
	maintainedEffects [30]*MaintainedEffects
	maintainedMutex   sync.RWMutex

	// Temporary spell effects (buffs/debuffs with durations)
	spellEffects [45]*SpellEffects
	effectsMutex sync.RWMutex

	// Detrimental effects (debuffs)
	detrimentalEffects []DetrimentalEffects
	detrimentalMutex   sync.RWMutex

	// Control effects organized by type
	controlEffects map[int8][]*DetrimentalEffects
	controlMutex   sync.RWMutex

	// Detrimental count by type for stacking limits
	detCountList map[int8]int8
	countMutex   sync.RWMutex

	// Bonus list for stat modifications
	bonusList  []*BonusValues
	bonusMutex sync.RWMutex

	// Immunity list organized by effect type
	immunities    map[int8][]*DetrimentalEffects
	immunityMutex sync.RWMutex
}

// NewSpellEffectManager creates a new spell effect manager
func NewSpellEffectManager() *SpellEffectManager {
	return &SpellEffectManager{
		maintainedEffects:  [30]*MaintainedEffects{},
		spellEffects:       [45]*SpellEffects{},
		detrimentalEffects: make([]DetrimentalEffects, 0),
		controlEffects:     make(map[int8][]*DetrimentalEffects),
		detCountList:       make(map[int8]int8),
		bonusList:          make([]*BonusValues, 0),
		immunities:         make(map[int8][]*DetrimentalEffects),
	}
}

// AddMaintainedEffect adds a maintained effect to an available slot
func (sem *SpellEffectManager) AddMaintainedEffect(effect *MaintainedEffects) bool {
	sem.maintainedMutex.Lock()
	defer sem.maintainedMutex.Unlock()

	// Find an empty slot
	for i := 0; i < len(sem.maintainedEffects); i++ {
		if sem.maintainedEffects[i] == nil {
			effect.SlotPos = int32(i)
			sem.maintainedEffects[i] = effect
			return true
		}
	}

	return false // No available slots
}

// RemoveMaintainedEffect removes a maintained effect by spell ID
func (sem *SpellEffectManager) RemoveMaintainedEffect(spellID int32) bool {
	sem.maintainedMutex.Lock()
	defer sem.maintainedMutex.Unlock()

	for i := 0; i < len(sem.maintainedEffects); i++ {
		if sem.maintainedEffects[i] != nil && sem.maintainedEffects[i].SpellID == spellID {
			sem.maintainedEffects[i] = nil
			return true
		}
	}

	return false
}

// GetMaintainedEffect retrieves a maintained effect by spell ID
func (sem *SpellEffectManager) GetMaintainedEffect(spellID int32) *MaintainedEffects {
	sem.maintainedMutex.RLock()
	defer sem.maintainedMutex.RUnlock()

	for _, effect := range sem.maintainedEffects {
		if effect != nil && effect.SpellID == spellID {
			return effect
		}
	}

	return nil
}

// GetAllMaintainedEffects returns a copy of all active maintained effects
func (sem *SpellEffectManager) GetAllMaintainedEffects() []*MaintainedEffects {
	sem.maintainedMutex.RLock()
	defer sem.maintainedMutex.RUnlock()

	effects := make([]*MaintainedEffects, 0)
	for _, effect := range sem.maintainedEffects {
		if effect != nil {
			effects = append(effects, effect)
		}
	}

	return effects
}

// AddSpellEffect adds a temporary spell effect to an available slot
func (sem *SpellEffectManager) AddSpellEffect(effect *SpellEffects) bool {
	sem.effectsMutex.Lock()
	defer sem.effectsMutex.Unlock()

	// Find an empty slot
	for i := 0; i < len(sem.spellEffects); i++ {
		if sem.spellEffects[i] == nil {
			sem.spellEffects[i] = effect
			return true
		}
	}

	return false // No available slots
}

// RemoveSpellEffect removes a spell effect by spell ID
func (sem *SpellEffectManager) RemoveSpellEffect(spellID int32) bool {
	sem.effectsMutex.Lock()
	defer sem.effectsMutex.Unlock()

	for i := 0; i < len(sem.spellEffects); i++ {
		if sem.spellEffects[i] != nil && sem.spellEffects[i].SpellID == spellID {
			sem.spellEffects[i] = nil
			return true
		}
	}

	return false
}

// GetSpellEffect retrieves a spell effect by spell ID
func (sem *SpellEffectManager) GetSpellEffect(spellID int32) *SpellEffects {
	sem.effectsMutex.RLock()
	defer sem.effectsMutex.RUnlock()

	for _, effect := range sem.spellEffects {
		if effect != nil && effect.SpellID == spellID {
			return effect
		}
	}

	return nil
}

// AddDetrimentalEffect adds a detrimental effect
func (sem *SpellEffectManager) AddDetrimentalEffect(effect DetrimentalEffects) {
	sem.detrimentalMutex.Lock()
	defer sem.detrimentalMutex.Unlock()

	sem.detrimentalEffects = append(sem.detrimentalEffects, effect)

	// Update detrimental count
	sem.countMutex.Lock()
	sem.detCountList[effect.DetType]++
	sem.countMutex.Unlock()

	// Add to control effects if applicable
	if effect.IsControlEffect() {
		sem.controlMutex.Lock()
		if sem.controlEffects[effect.ControlEffect] == nil {
			sem.controlEffects[effect.ControlEffect] = make([]*DetrimentalEffects, 0)
		}
		sem.controlEffects[effect.ControlEffect] = append(sem.controlEffects[effect.ControlEffect], &effect)
		sem.controlMutex.Unlock()
	}
}

// RemoveDetrimentalEffect removes a detrimental effect by spell ID and caster
func (sem *SpellEffectManager) RemoveDetrimentalEffect(spellID int32, casterID int32) bool {
	sem.detrimentalMutex.Lock()
	defer sem.detrimentalMutex.Unlock()

	for i, effect := range sem.detrimentalEffects {
		if effect.SpellID == spellID && effect.CasterID == casterID {
			// Remove from slice
			sem.detrimentalEffects = append(sem.detrimentalEffects[:i], sem.detrimentalEffects[i+1:]...)

			// Update detrimental count
			sem.countMutex.Lock()
			sem.detCountList[effect.DetType]--
			if sem.detCountList[effect.DetType] <= 0 {
				delete(sem.detCountList, effect.DetType)
			}
			sem.countMutex.Unlock()

			// Remove from control effects if applicable
			if effect.IsControlEffect() {
				sem.removeFromControlEffects(effect.ControlEffect, spellID, casterID)
			}

			return true
		}
	}

	return false
}

// removeFromControlEffects removes a control effect from the control effects map
func (sem *SpellEffectManager) removeFromControlEffects(controlType int8, spellID int32, casterID int32) {
	sem.controlMutex.Lock()
	defer sem.controlMutex.Unlock()

	if effects, exists := sem.controlEffects[controlType]; exists {
		for i, effect := range effects {
			if effect.SpellID == spellID && effect.CasterID == casterID {
				sem.controlEffects[controlType] = append(effects[:i], effects[i+1:]...)
				if len(sem.controlEffects[controlType]) == 0 {
					delete(sem.controlEffects, controlType)
				}
				break
			}
		}
	}
}

// GetDetrimentalEffect retrieves a detrimental effect by spell ID and caster
func (sem *SpellEffectManager) GetDetrimentalEffect(spellID int32, casterID int32) *DetrimentalEffects {
	sem.detrimentalMutex.RLock()
	defer sem.detrimentalMutex.RUnlock()

	for i, effect := range sem.detrimentalEffects {
		if effect.SpellID == spellID && effect.CasterID == casterID {
			return &sem.detrimentalEffects[i]
		}
	}

	return nil
}

// HasControlEffect checks if the entity has a specific control effect active
func (sem *SpellEffectManager) HasControlEffect(controlType int8) bool {
	sem.controlMutex.RLock()
	defer sem.controlMutex.RUnlock()

	effects, exists := sem.controlEffects[controlType]
	return exists && len(effects) > 0
}

// AddBonus adds a stat bonus
func (sem *SpellEffectManager) AddBonus(bonus *BonusValues) {
	sem.bonusMutex.Lock()
	defer sem.bonusMutex.Unlock()

	sem.bonusList = append(sem.bonusList, bonus)
}

// RemoveBonus removes a stat bonus by spell ID
func (sem *SpellEffectManager) RemoveBonus(spellID int32) bool {
	sem.bonusMutex.Lock()
	defer sem.bonusMutex.Unlock()

	for i, bonus := range sem.bonusList {
		if bonus.SpellID == spellID {
			sem.bonusList = append(sem.bonusList[:i], sem.bonusList[i+1:]...)
			return true
		}
	}

	return false
}

// GetBonusValue calculates the total bonus value for a specific stat type
func (sem *SpellEffectManager) GetBonusValue(bonusType int16, entityClass int64, race int16, factionID int32) float32 {
	sem.bonusMutex.RLock()
	defer sem.bonusMutex.RUnlock()

	var total float32 = 0

	for _, bonus := range sem.bonusList {
		if bonus.Type == bonusType && bonus.MeetsRequirements(entityClass, race, factionID) {
			total += bonus.Value
		}
	}

	return total
}

// CleanupExpiredEffects removes all expired effects
func (sem *SpellEffectManager) CleanupExpiredEffects() {
	// Clean maintained effects
	sem.maintainedMutex.Lock()
	for i, effect := range sem.maintainedEffects {
		if effect != nil && effect.IsExpired() {
			sem.maintainedEffects[i] = nil
		}
	}
	sem.maintainedMutex.Unlock()

	// Clean spell effects
	sem.effectsMutex.Lock()
	for i, effect := range sem.spellEffects {
		if effect != nil && effect.IsExpired() {
			sem.spellEffects[i] = nil
		}
	}
	sem.effectsMutex.Unlock()

	// Clean detrimental effects
	sem.detrimentalMutex.Lock()
	newDetrimentals := make([]DetrimentalEffects, 0)
	for _, effect := range sem.detrimentalEffects {
		if !effect.IsExpired() {
			newDetrimentals = append(newDetrimentals, effect)
		} else {
			// Update count
			sem.countMutex.Lock()
			sem.detCountList[effect.DetType]--
			if sem.detCountList[effect.DetType] <= 0 {
				delete(sem.detCountList, effect.DetType)
			}
			sem.countMutex.Unlock()

			// Remove from control effects
			if effect.IsControlEffect() {
				sem.removeFromControlEffects(effect.ControlEffect, effect.SpellID, effect.CasterID)
			}
		}
	}
	sem.detrimentalEffects = newDetrimentals
	sem.detrimentalMutex.Unlock()
}

// ClearAllEffects removes all spell effects
func (sem *SpellEffectManager) ClearAllEffects() {
	sem.maintainedMutex.Lock()
	for i := range sem.maintainedEffects {
		sem.maintainedEffects[i] = nil
	}
	sem.maintainedMutex.Unlock()

	sem.effectsMutex.Lock()
	for i := range sem.spellEffects {
		sem.spellEffects[i] = nil
	}
	sem.effectsMutex.Unlock()

	sem.detrimentalMutex.Lock()
	sem.detrimentalEffects = make([]DetrimentalEffects, 0)
	sem.detrimentalMutex.Unlock()

	sem.controlMutex.Lock()
	sem.controlEffects = make(map[int8][]*DetrimentalEffects)
	sem.controlMutex.Unlock()

	sem.countMutex.Lock()
	sem.detCountList = make(map[int8]int8)
	sem.countMutex.Unlock()

	sem.bonusMutex.Lock()
	sem.bonusList = make([]*BonusValues, 0)
	sem.bonusMutex.Unlock()
}
