package entity

import (
	"math"
	"sync"
	"sync/atomic"

	"eq2emu/internal/common"
	"eq2emu/internal/spawn"
)

// Combat and pet types
const (
	PetTypeSummon   = 1
	PetTypeCharm    = 2
	PetTypeDeity    = 3
	PetTypeCosmetic = 4
)

// Entity represents a combat-capable spawn (NPCs and Players)
// Extends the base Spawn with combat, magic, and equipment systems
type Entity struct {
	*spawn.Spawn // Embedded spawn for basic functionality

	// Core entity information
	infoStruct *InfoStruct // All entity statistics and information

	// Combat state
	inCombat        atomic.Bool // Whether currently in combat
	casting         atomic.Bool // Whether currently casting
	maxSpeed        float32     // Maximum movement speed
	baseSpeed       float32     // Base movement speed
	speedMultiplier float32     // Speed multiplier

	// Position tracking for movement
	lastX       float32 // Last known X position
	lastY       float32 // Last known Y position
	lastZ       float32 // Last known Z position
	lastHeading float32 // Last known heading

	// Regeneration
	regenHpRate    int16 // Health regeneration rate
	regenPowerRate int16 // Power regeneration rate

	// Spell and effect management
	spellEffectManager *SpellEffectManager

	// Pet system
	pet           *Entity     // Summon pet
	charmedPet    *Entity     // Charmed pet
	deityPet      *Entity     // Deity pet
	cosmeticPet   *Entity     // Cosmetic pet
	owner         int32       // Owner entity ID (if this is a pet)
	petType       int8        // Type of pet
	petSpellID    int32       // Spell ID used to create/control pet
	petSpellTier  int8        // Tier of pet spell
	petDismissing atomic.Bool // Whether pet is being dismissed

	// Group and social
	// TODO: Add GroupMemberInfo when group system is implemented
	// groupMemberInfo *GroupMemberInfo

	// Trading
	// TODO: Add Trade when trade system is implemented
	// trade *Trade

	// Deity and alignment
	deity int8 // Deity ID

	// Equipment and appearance
	features  common.CharFeatures // Character appearance features
	equipment common.EQ2Equipment // Equipment appearance

	// Threat and hate management
	threatTransferPercent float32 // Percentage of threat to transfer

	// Spell-related flags
	hasSeeInvisSpell atomic.Bool // Has see invisible spell
	hasSeeHideSpell  atomic.Bool // Has see hidden spell

	// Proc system
	// TODO: Add proc list when implemented
	// procList []Proc

	// Thread safety
	spellEffectMutex      sync.RWMutex
	maintainedMutex       sync.RWMutex
	detrimentalMutex      sync.RWMutex
	commandMutex          sync.Mutex
	bonusCalculationMutex sync.RWMutex
}

// NewEntity creates a new Entity with default values
// Must be called by subclasses (NPC, Player) for proper initialization
func NewEntity() *Entity {
	e := &Entity{
		Spawn:                 spawn.NewSpawn(),
		infoStruct:            NewInfoStruct(),
		maxSpeed:              6.0,
		baseSpeed:             0.0,
		speedMultiplier:       1.0,
		lastX:                 -1,
		lastY:                 -1,
		lastZ:                 -1,
		lastHeading:           -1,
		regenHpRate:           0,
		regenPowerRate:        0,
		spellEffectManager:    NewSpellEffectManager(),
		pet:                   nil,
		charmedPet:            nil,
		deityPet:              nil,
		cosmeticPet:           nil,
		owner:                 0,
		petType:               0,
		petSpellID:            0,
		petSpellTier:          0,
		deity:                 0,
		threatTransferPercent: 0.0,
	}

	// Initialize features and equipment
	e.features = common.CharFeatures{}
	e.equipment = common.EQ2Equipment{}

	// Set initial state
	e.inCombat.Store(false)
	e.casting.Store(false)
	e.petDismissing.Store(false)
	e.hasSeeInvisSpell.Store(false)
	e.hasSeeHideSpell.Store(false)

	return e
}

// IsEntity returns true (implements Spawn interface)
func (e *Entity) IsEntity() bool {
	return true
}

// GetInfoStruct returns the entity's info structure
func (e *Entity) GetInfoStruct() *InfoStruct {
	return e.infoStruct
}

// SetInfoStruct updates the entity's info structure
func (e *Entity) SetInfoStruct(info *InfoStruct) {
	if info != nil {
		e.infoStruct = info
	}
}

// GetClient returns the client for this entity (overridden by Player)
func (e *Entity) GetClient() interface{} {
	return nil
}

// Combat state methods

// IsInCombat returns whether the entity is currently in combat
func (e *Entity) IsInCombat() bool {
	return e.inCombat.Load()
}

// SetInCombat updates the combat state
func (e *Entity) SetInCombat(inCombat bool) {
	e.inCombat.Store(inCombat)
}

// IsCasting returns whether the entity is currently casting
func (e *Entity) IsCasting() bool {
	return e.casting.Load()
}

// SetCasting updates the casting state
func (e *Entity) SetCasting(casting bool) {
	e.casting.Store(casting)
}

// Speed and movement methods

// GetMaxSpeed returns the maximum movement speed
func (e *Entity) GetMaxSpeed() float32 {
	return e.maxSpeed
}

// SetMaxSpeed updates the maximum movement speed
func (e *Entity) SetMaxSpeed(speed float32) {
	e.maxSpeed = speed
}

// GetBaseSpeed returns the base movement speed
func (e *Entity) GetBaseSpeed() float32 {
	return e.baseSpeed
}

// SetBaseSpeed updates the base movement speed
func (e *Entity) SetBaseSpeed(speed float32) {
	e.baseSpeed = speed
}

// GetSpeedMultiplier returns the speed multiplier
func (e *Entity) GetSpeedMultiplier() float32 {
	return e.speedMultiplier
}

// SetSpeedMultiplier updates the speed multiplier
func (e *Entity) SetSpeedMultiplier(multiplier float32) {
	e.speedMultiplier = multiplier
}

// CalculateEffectiveSpeed calculates the current effective speed
func (e *Entity) CalculateEffectiveSpeed() float32 {
	baseSpeed := e.baseSpeed
	if baseSpeed <= 0 {
		baseSpeed = e.maxSpeed
	}

	return baseSpeed * e.speedMultiplier
}

// Position tracking methods

// GetLastPosition returns the last known position
func (e *Entity) GetLastPosition() (float32, float32, float32, float32) {
	return e.lastX, e.lastY, e.lastZ, e.lastHeading
}

// SetLastPosition updates the last known position
func (e *Entity) SetLastPosition(x, y, z, heading float32) {
	e.lastX = x
	e.lastY = y
	e.lastZ = z
	e.lastHeading = heading
}

// HasMoved checks if the entity has moved since last position update
func (e *Entity) HasMoved() bool {
	currentX := e.GetX()
	currentY := e.GetY()
	currentZ := e.GetZ()
	currentHeading := e.GetHeading()

	const epsilon = 0.01 // Small threshold for floating point comparison

	return math.Abs(float64(currentX-e.lastX)) > epsilon ||
		math.Abs(float64(currentY-e.lastY)) > epsilon ||
		math.Abs(float64(currentZ-e.lastZ)) > epsilon ||
		math.Abs(float64(currentHeading-e.lastHeading)) > epsilon
}

// Stat calculation methods

// GetStr returns the effective strength stat
func (e *Entity) GetStr() int16 {
	base := int16(e.infoStruct.GetStr())
	bonus := int16(e.spellEffectManager.GetBonusValue(1, 0, e.infoStruct.GetRace(), 0)) // Stat type 1 = STR
	return base + bonus
}

// GetSta returns the effective stamina stat
func (e *Entity) GetSta() int16 {
	base := int16(e.infoStruct.GetSta())
	bonus := int16(e.spellEffectManager.GetBonusValue(2, 0, e.infoStruct.GetRace(), 0)) // Stat type 2 = STA
	return base + bonus
}

// GetAgi returns the effective agility stat
func (e *Entity) GetAgi() int16 {
	base := int16(e.infoStruct.GetAgi())
	bonus := int16(e.spellEffectManager.GetBonusValue(3, 0, e.infoStruct.GetRace(), 0)) // Stat type 3 = AGI
	return base + bonus
}

// GetWis returns the effective wisdom stat
func (e *Entity) GetWis() int16 {
	base := int16(e.infoStruct.GetWis())
	bonus := int16(e.spellEffectManager.GetBonusValue(4, 0, e.infoStruct.GetRace(), 0)) // Stat type 4 = WIS
	return base + bonus
}

// GetIntel returns the effective intelligence stat
func (e *Entity) GetIntel() int16 {
	base := int16(e.infoStruct.GetIntel())
	bonus := int16(e.spellEffectManager.GetBonusValue(5, 0, e.infoStruct.GetRace(), 0)) // Stat type 5 = INT
	return base + bonus
}

// GetPrimaryStat returns the highest primary stat value
func (e *Entity) GetPrimaryStat() int16 {
	str := e.GetStr()
	sta := e.GetSta()
	agi := e.GetAgi()
	wis := e.GetWis()
	intel := e.GetIntel()

	primary := str
	if sta > primary {
		primary = sta
	}
	if agi > primary {
		primary = agi
	}
	if wis > primary {
		primary = wis
	}
	if intel > primary {
		primary = intel
	}

	return primary
}

// Resistance methods

// GetHeatResistance returns heat resistance
func (e *Entity) GetHeatResistance() int16 {
	return e.infoStruct.GetResistance("heat")
}

// GetColdResistance returns cold resistance
func (e *Entity) GetColdResistance() int16 {
	return e.infoStruct.GetResistance("cold")
}

// GetMagicResistance returns magic resistance
func (e *Entity) GetMagicResistance() int16 {
	return e.infoStruct.GetResistance("magic")
}

// GetMentalResistance returns mental resistance
func (e *Entity) GetMentalResistance() int16 {
	return e.infoStruct.GetResistance("mental")
}

// GetDivineResistance returns divine resistance
func (e *Entity) GetDivineResistance() int16 {
	return e.infoStruct.GetResistance("divine")
}

// GetDiseaseResistance returns disease resistance
func (e *Entity) GetDiseaseResistance() int16 {
	return e.infoStruct.GetResistance("disease")
}

// GetPoisonResistance returns poison resistance
func (e *Entity) GetPoisonResistance() int16 {
	return e.infoStruct.GetResistance("poison")
}

// Spell effect management methods

// AddMaintainedSpell adds a maintained spell effect
func (e *Entity) AddMaintainedSpell(name string, spellID int32, duration float32, concentration int8) bool {
	// Check if we have enough concentration
	if !e.infoStruct.AddConcentration(int16(concentration)) {
		return false
	}

	effect := NewMaintainedEffects(name, spellID, duration)
	effect.ConcUsed = concentration

	e.maintainedMutex.Lock()
	defer e.maintainedMutex.Unlock()

	if !e.spellEffectManager.AddMaintainedEffect(effect) {
		// Failed to add, return concentration
		e.infoStruct.RemoveConcentration(int16(concentration))
		return false
	}

	return true
}

// RemoveMaintainedSpell removes a maintained spell effect
func (e *Entity) RemoveMaintainedSpell(spellID int32) bool {
	e.maintainedMutex.Lock()
	defer e.maintainedMutex.Unlock()

	// Get the effect to check concentration usage
	effect := e.spellEffectManager.GetMaintainedEffect(spellID)
	if effect == nil {
		return false
	}

	// Return concentration
	e.infoStruct.RemoveConcentration(int16(effect.ConcUsed))

	return e.spellEffectManager.RemoveMaintainedEffect(spellID)
}

// GetMaintainedSpell retrieves a maintained spell effect
func (e *Entity) GetMaintainedSpell(spellID int32) *MaintainedEffects {
	e.maintainedMutex.RLock()
	defer e.maintainedMutex.RUnlock()

	return e.spellEffectManager.GetMaintainedEffect(spellID)
}

// AddSpellEffect adds a temporary spell effect
func (e *Entity) AddSpellEffect(spellID int32, casterID int32, duration float32) bool {
	effect := NewSpellEffects(spellID, casterID, duration)

	e.spellEffectMutex.Lock()
	defer e.spellEffectMutex.Unlock()

	return e.spellEffectManager.AddSpellEffect(effect)
}

// RemoveSpellEffect removes a spell effect
func (e *Entity) RemoveSpellEffect(spellID int32) bool {
	e.spellEffectMutex.Lock()
	defer e.spellEffectMutex.Unlock()

	return e.spellEffectManager.RemoveSpellEffect(spellID)
}

// AddDetrimentalSpell adds a detrimental effect
func (e *Entity) AddDetrimentalSpell(spellID int32, casterID int32, duration float32, detType int8) {
	effect := NewDetrimentalEffects(spellID, casterID, duration)
	effect.DetType = detType

	e.detrimentalMutex.Lock()
	defer e.detrimentalMutex.Unlock()

	e.spellEffectManager.AddDetrimentalEffect(*effect)
}

// RemoveDetrimentalSpell removes a detrimental effect
func (e *Entity) RemoveDetrimentalSpell(spellID int32, casterID int32) bool {
	e.detrimentalMutex.Lock()
	defer e.detrimentalMutex.Unlock()

	return e.spellEffectManager.RemoveDetrimentalEffect(spellID, casterID)
}

// GetDetrimentalEffect retrieves a detrimental effect
func (e *Entity) GetDetrimentalEffect(spellID int32, casterID int32) *DetrimentalEffects {
	e.detrimentalMutex.RLock()
	defer e.detrimentalMutex.RUnlock()

	return e.spellEffectManager.GetDetrimentalEffect(spellID, casterID)
}

// HasControlEffect checks if the entity has a specific control effect
func (e *Entity) HasControlEffect(controlType int8) bool {
	return e.spellEffectManager.HasControlEffect(controlType)
}

// Bonus system methods

// AddSkillBonus adds a skill-related bonus
func (e *Entity) AddSkillBonus(spellID int32, skillID int32, value float32) {
	bonus := NewBonusValues(spellID, int16(skillID+100), value) // Skill bonuses use type 100+
	e.spellEffectManager.AddBonus(bonus)
}

// AddStatBonus adds a stat bonus
func (e *Entity) AddStatBonus(spellID int32, statType int16, value float32) {
	bonus := NewBonusValues(spellID, statType, value)
	e.spellEffectManager.AddBonus(bonus)
}

// CalculateBonuses recalculates all stat bonuses and updates InfoStruct
func (e *Entity) CalculateBonuses() {
	e.bonusCalculationMutex.Lock()
	defer e.bonusCalculationMutex.Unlock()

	// Reset effects to base values
	e.infoStruct.ResetEffects()

	entityClass := int64(1 << e.infoStruct.GetClass1()) // Convert class to bitmask
	race := e.infoStruct.GetRace()
	factionID := int32(e.GetFactionID())

	// Apply stat bonuses
	strBonus := e.spellEffectManager.GetBonusValue(1, entityClass, race, factionID)
	staBonus := e.spellEffectManager.GetBonusValue(2, entityClass, race, factionID)
	agiBonus := e.spellEffectManager.GetBonusValue(3, entityClass, race, factionID)
	wisBonus := e.spellEffectManager.GetBonusValue(4, entityClass, race, factionID)
	intelBonus := e.spellEffectManager.GetBonusValue(5, entityClass, race, factionID)

	// Update InfoStruct with bonuses
	e.infoStruct.SetStr(e.infoStruct.GetStr() + strBonus)
	e.infoStruct.SetSta(e.infoStruct.GetSta() + staBonus)
	e.infoStruct.SetAgi(e.infoStruct.GetAgi() + agiBonus)
	e.infoStruct.SetWis(e.infoStruct.GetWis() + wisBonus)
	e.infoStruct.SetIntel(e.infoStruct.GetIntel() + intelBonus)

	// Apply resistance bonuses
	heatBonus := int16(e.spellEffectManager.GetBonusValue(10, entityClass, race, factionID))
	coldBonus := int16(e.spellEffectManager.GetBonusValue(11, entityClass, race, factionID))
	magicBonus := int16(e.spellEffectManager.GetBonusValue(12, entityClass, race, factionID))
	mentalBonus := int16(e.spellEffectManager.GetBonusValue(13, entityClass, race, factionID))
	divineBonus := int16(e.spellEffectManager.GetBonusValue(14, entityClass, race, factionID))
	diseaseBonus := int16(e.spellEffectManager.GetBonusValue(15, entityClass, race, factionID))
	poisonBonus := int16(e.spellEffectManager.GetBonusValue(16, entityClass, race, factionID))

	e.infoStruct.SetResistance("heat", e.infoStruct.GetResistance("heat")+heatBonus)
	e.infoStruct.SetResistance("cold", e.infoStruct.GetResistance("cold")+coldBonus)
	e.infoStruct.SetResistance("magic", e.infoStruct.GetResistance("magic")+magicBonus)
	e.infoStruct.SetResistance("mental", e.infoStruct.GetResistance("mental")+mentalBonus)
	e.infoStruct.SetResistance("divine", e.infoStruct.GetResistance("divine")+divineBonus)
	e.infoStruct.SetResistance("disease", e.infoStruct.GetResistance("disease")+diseaseBonus)
	e.infoStruct.SetResistance("poison", e.infoStruct.GetResistance("poison")+poisonBonus)
}

// Pet management methods

// GetPet returns the summon pet
func (e *Entity) GetPet() *Entity {
	return e.pet
}

// SetPet sets the summon pet
func (e *Entity) SetPet(pet *Entity) {
	e.pet = pet
	if pet != nil {
		pet.owner = e.GetID()
		pet.petType = PetTypeSummon
	}
}

// GetCharmedPet returns the charmed pet
func (e *Entity) GetCharmedPet() *Entity {
	return e.charmedPet
}

// SetCharmedPet sets the charmed pet
func (e *Entity) SetCharmedPet(pet *Entity) {
	e.charmedPet = pet
	if pet != nil {
		pet.owner = e.GetID()
		pet.petType = PetTypeCharm
	}
}

// GetDeityPet returns the deity pet
func (e *Entity) GetDeityPet() *Entity {
	return e.deityPet
}

// SetDeityPet sets the deity pet
func (e *Entity) SetDeityPet(pet *Entity) {
	e.deityPet = pet
	if pet != nil {
		pet.owner = e.GetID()
		pet.petType = PetTypeDeity
	}
}

// GetCosmeticPet returns the cosmetic pet
func (e *Entity) GetCosmeticPet() *Entity {
	return e.cosmeticPet
}

// SetCosmeticPet sets the cosmetic pet
func (e *Entity) SetCosmeticPet(pet *Entity) {
	e.cosmeticPet = pet
	if pet != nil {
		pet.owner = e.GetID()
		pet.petType = PetTypeCosmetic
	}
}

// GetOwner returns the owner entity ID (if this is a pet)
func (e *Entity) GetOwner() int32 {
	return e.owner
}

// GetPetType returns the type of pet this entity is
func (e *Entity) GetPetType() int8 {
	return e.petType
}

// IsPetDismissing returns whether the pet is being dismissed
func (e *Entity) IsPetDismissing() bool {
	return e.petDismissing.Load()
}

// SetPetDismissing sets whether the pet is being dismissed
func (e *Entity) SetPetDismissing(dismissing bool) {
	e.petDismissing.Store(dismissing)
}

// Utility methods

// GetDeity returns the deity ID
func (e *Entity) GetDeity() int8 {
	return e.deity
}

// SetDeity updates the deity ID
func (e *Entity) SetDeity(deity int8) {
	e.deity = deity
}

// GetDodgeChance calculates the dodge chance (to be overridden by subclasses)
func (e *Entity) GetDodgeChance() float32 {
	// Base implementation, should be overridden
	return 5.0 // 5% base dodge chance
}

// HasSeeInvisSpell returns whether the entity has see invisible
func (e *Entity) HasSeeInvisSpell() bool {
	return e.hasSeeInvisSpell.Load()
}

// SetSeeInvisSpell updates the see invisible state
func (e *Entity) SetSeeInvisSpell(hasSpell bool) {
	e.hasSeeInvisSpell.Store(hasSpell)
}

// HasSeeHideSpell returns whether the entity has see hidden
func (e *Entity) HasSeeHideSpell() bool {
	return e.hasSeeHideSpell.Load()
}

// SetSeeHideSpell updates the see hidden state
func (e *Entity) SetSeeHideSpell(hasSpell bool) {
	e.hasSeeHideSpell.Store(hasSpell)
}

// Cleanup methods

// DeleteSpellEffects removes all spell effects
func (e *Entity) DeleteSpellEffects(removeClient bool) {
	e.spellEffectMutex.Lock()
	e.maintainedMutex.Lock()
	e.detrimentalMutex.Lock()
	defer e.spellEffectMutex.Unlock()
	defer e.maintainedMutex.Unlock()
	defer e.detrimentalMutex.Unlock()

	// Clear all maintained effects and return concentration
	effects := e.spellEffectManager.GetAllMaintainedEffects()
	for _, effect := range effects {
		e.infoStruct.RemoveConcentration(int16(effect.ConcUsed))
	}

	e.spellEffectManager.ClearAllEffects()
}

// RemoveSpells removes spell effects, optionally only unfriendly ones
func (e *Entity) RemoveSpells(unfriendlyOnly bool) {
	// TODO: Implement when we can determine friendly vs unfriendly spells
	if !unfriendlyOnly {
		e.DeleteSpellEffects(false)
	}
}

// Update methods

// ProcessEffects handles periodic effect processing
func (e *Entity) ProcessEffects() {
	// Clean up expired effects
	e.spellEffectManager.CleanupExpiredEffects()

	// TODO: Apply periodic effect damage/healing
	// TODO: Handle effect-based stat changes
}

// Class system integration adapters

// GetClass returns the entity's primary class (ClassAware interface compatibility)
func (e *Entity) GetClass() int8 {
	return e.infoStruct.GetClass1()
}

// SetClass sets the entity's primary class (ClassAware interface compatibility)
func (e *Entity) SetClass(classID int8) {
	e.infoStruct.SetClass1(classID)
}

// GetLevel returns the entity's level (EntityWithClass interface compatibility)
func (e *Entity) GetLevel() int8 {
	return int8(e.infoStruct.GetLevel())
}

// TODO: Additional methods to implement:
// - Combat calculation methods (damage, healing, etc.)
// - Equipment bonus application methods
// - Spell casting methods
// - Threat and hate management methods
// - Group combat methods
// - Many more combat and magic related methods
