package entity

import (
	"sync"
	"testing"
	"time"
)

func TestNewEntity(t *testing.T) {
	entity := NewEntity()
	if entity == nil {
		t.Fatal("NewEntity returned nil")
	}

	if entity.Spawn == nil {
		t.Error("Expected Spawn to be initialized")
	}

	if entity.infoStruct == nil {
		t.Error("Expected InfoStruct to be initialized")
	}

	if entity.spellEffectManager == nil {
		t.Error("Expected SpellEffectManager to be initialized")
	}

	// Check default values
	if entity.GetMaxSpeed() != 6.0 {
		t.Errorf("Expected max speed 6.0, got %f", entity.GetMaxSpeed())
	}

	if entity.GetBaseSpeed() != 0.0 {
		t.Errorf("Expected base speed 0.0, got %f", entity.GetBaseSpeed())
	}

	if entity.GetSpeedMultiplier() != 1.0 {
		t.Errorf("Expected speed multiplier 1.0, got %f", entity.GetSpeedMultiplier())
	}

	// Check initial states
	if entity.IsInCombat() {
		t.Error("Expected entity to not be in combat initially")
	}

	if entity.IsCasting() {
		t.Error("Expected entity to not be casting initially")
	}

	if entity.IsPetDismissing() {
		t.Error("Expected pet to not be dismissing initially")
	}

	if entity.HasSeeInvisSpell() {
		t.Error("Expected entity to not have see invisible spell initially")
	}

	if entity.HasSeeHideSpell() {
		t.Error("Expected entity to not have see hidden spell initially")
	}
}

func TestEntityIsEntity(t *testing.T) {
	entity := NewEntity()
	if !entity.IsEntity() {
		t.Error("Expected IsEntity to return true")
	}
}

func TestEntityInfoStruct(t *testing.T) {
	entity := NewEntity()
	
	stats := entity.GetInfoStruct()
	if stats == nil {
		t.Error("Expected InfoStruct to be initialized")
	}

	// Test setting a new info struct
	newInfo := NewInfoStruct()
	newInfo.SetName("Test Entity")
	entity.SetInfoStruct(newInfo)

	if entity.GetInfoStruct().GetName() != "Test Entity" {
		t.Error("Expected info struct to be updated")
	}

	// Test that nil info struct is ignored
	entity.SetInfoStruct(nil)
	if entity.GetInfoStruct().GetName() != "Test Entity" {
		t.Error("Expected info struct to remain unchanged when setting nil")
	}
}

func TestEntityClient(t *testing.T) {
	entity := NewEntity()
	
	// Base Entity should return nil for GetClient
	client := entity.GetClient()
	if client != nil {
		t.Error("Expected GetClient to return nil for base Entity")
	}
}

func TestEntityCombatState(t *testing.T) {
	entity := NewEntity()

	// Initial state should be false
	if entity.IsInCombat() {
		t.Error("Expected entity to not be in combat initially")
	}

	// Set combat state to true
	entity.SetInCombat(true)
	if !entity.IsInCombat() {
		t.Error("Expected entity to be in combat after setting to true")
	}

	// Set combat state to false
	entity.SetInCombat(false)
	if entity.IsInCombat() {
		t.Error("Expected entity to not be in combat after setting to false")
	}
}

func TestEntityCastingState(t *testing.T) {
	entity := NewEntity()

	// Initial state should be false
	if entity.IsCasting() {
		t.Error("Expected entity to not be casting initially")
	}

	// Set casting state to true
	entity.SetCasting(true)
	if !entity.IsCasting() {
		t.Error("Expected entity to be casting after setting to true")
	}

	// Set casting state to false
	entity.SetCasting(false)
	if entity.IsCasting() {
		t.Error("Expected entity to not be casting after setting to false")
	}
}

func TestEntitySpeedMethods(t *testing.T) {
	entity := NewEntity()

	// Test max speed
	entity.SetMaxSpeed(10.0)
	if entity.GetMaxSpeed() != 10.0 {
		t.Errorf("Expected max speed 10.0, got %f", entity.GetMaxSpeed())
	}

	// Test base speed
	entity.SetBaseSpeed(5.0)
	if entity.GetBaseSpeed() != 5.0 {
		t.Errorf("Expected base speed 5.0, got %f", entity.GetBaseSpeed())
	}

	// Test speed multiplier
	entity.SetSpeedMultiplier(2.0)
	if entity.GetSpeedMultiplier() != 2.0 {
		t.Errorf("Expected speed multiplier 2.0, got %f", entity.GetSpeedMultiplier())
	}

	// Test effective speed calculation with base speed set
	effectiveSpeed := entity.CalculateEffectiveSpeed()
	if effectiveSpeed != 10.0 { // 5.0 * 2.0
		t.Errorf("Expected effective speed 10.0, got %f", effectiveSpeed)
	}

	// Test effective speed calculation with zero base speed (should use max speed)
	entity.SetBaseSpeed(0.0)
	effectiveSpeed = entity.CalculateEffectiveSpeed()
	if effectiveSpeed != 20.0 { // 10.0 * 2.0
		t.Errorf("Expected effective speed 20.0, got %f", effectiveSpeed)
	}
}

func TestEntityPositionTracking(t *testing.T) {
	entity := NewEntity()

	// Test setting and getting last position
	entity.SetLastPosition(100.0, 200.0, 300.0, 1.5)
	x, y, z, heading := entity.GetLastPosition()

	if x != 100.0 || y != 200.0 || z != 300.0 || heading != 1.5 {
		t.Errorf("Expected position (100.0, 200.0, 300.0, 1.5), got (%f, %f, %f, %f)", x, y, z, heading)
	}

	// Test HasMoved with different positions
	// Note: This would require setting the spawn's actual position, which depends on the spawn implementation
	// For now, we just test that the method doesn't panic
	moved := entity.HasMoved()
	_ = moved // We can't easily test the actual movement detection without setting up spawn positions
}

func TestEntityStatCalculation(t *testing.T) {
	entity := NewEntity()
	info := entity.GetInfoStruct()

	// Set base stats
	info.SetStr(10.0)
	info.SetSta(12.0)
	info.SetAgi(8.0)
	info.SetWis(15.0)
	info.SetIntel(20.0)

	// Test individual stat getters (these include bonuses from spell effects)
	str := entity.GetStr()
	if str < 10 {
		t.Errorf("Expected strength >= 10, got %d", str)
	}

	sta := entity.GetSta()
	if sta < 12 {
		t.Errorf("Expected stamina >= 12, got %d", sta)
	}

	agi := entity.GetAgi()
	if agi < 8 {
		t.Errorf("Expected agility >= 8, got %d", agi)
	}

	wis := entity.GetWis()
	if wis < 15 {
		t.Errorf("Expected wisdom >= 15, got %d", wis)
	}

	intel := entity.GetIntel()
	if intel < 20 {
		t.Errorf("Expected intelligence >= 20, got %d", intel)
	}

	// Test primary stat calculation
	primaryStat := entity.GetPrimaryStat()
	if primaryStat < 20 {
		t.Errorf("Expected primary stat >= 20 (intelligence is highest), got %d", primaryStat)
	}
}

func TestEntityResistances(t *testing.T) {
	entity := NewEntity()
	info := entity.GetInfoStruct()

	// Set base resistances
	info.SetResistance("heat", 10)
	info.SetResistance("cold", 15)
	info.SetResistance("magic", 20)
	info.SetResistance("mental", 25)
	info.SetResistance("divine", 30)
	info.SetResistance("disease", 35)
	info.SetResistance("poison", 40)

	// Test resistance getters
	if entity.GetHeatResistance() != 10 {
		t.Errorf("Expected heat resistance 10, got %d", entity.GetHeatResistance())
	}

	if entity.GetColdResistance() != 15 {
		t.Errorf("Expected cold resistance 15, got %d", entity.GetColdResistance())
	}

	if entity.GetMagicResistance() != 20 {
		t.Errorf("Expected magic resistance 20, got %d", entity.GetMagicResistance())
	}

	if entity.GetMentalResistance() != 25 {
		t.Errorf("Expected mental resistance 25, got %d", entity.GetMentalResistance())
	}

	if entity.GetDivineResistance() != 30 {
		t.Errorf("Expected divine resistance 30, got %d", entity.GetDivineResistance())
	}

	if entity.GetDiseaseResistance() != 35 {
		t.Errorf("Expected disease resistance 35, got %d", entity.GetDiseaseResistance())
	}

	if entity.GetPoisonResistance() != 40 {
		t.Errorf("Expected poison resistance 40, got %d", entity.GetPoisonResistance())
	}
}

func TestEntityMaintainedSpells(t *testing.T) {
	entity := NewEntity()
	info := entity.GetInfoStruct()

	// Set max concentration
	info.SetMaxConcentration(10)

	// Test adding maintained spell
	success := entity.AddMaintainedSpell("Test Spell", 123, 60.0, 3)
	if !success {
		t.Error("Expected AddMaintainedSpell to succeed")
	}

	// Check concentration usage
	if info.GetCurConcentration() != 3 {
		t.Errorf("Expected current concentration 3, got %d", info.GetCurConcentration())
	}

	// Test getting maintained spell
	effect := entity.GetMaintainedSpell(123)
	if effect == nil {
		t.Error("Expected to find maintained spell effect")
	}

	// Test adding spell that would exceed concentration
	success = entity.AddMaintainedSpell("Another Spell", 124, 60.0, 8)
	if success {
		t.Error("Expected AddMaintainedSpell to fail when concentration exceeded")
	}

	// Test removing maintained spell
	success = entity.RemoveMaintainedSpell(123)
	if !success {
		t.Error("Expected RemoveMaintainedSpell to succeed")
	}

	// Check concentration was returned
	if info.GetCurConcentration() != 0 {
		t.Errorf("Expected current concentration 0, got %d", info.GetCurConcentration())
	}

	// Test removing non-existent spell
	success = entity.RemoveMaintainedSpell(999)
	if success {
		t.Error("Expected RemoveMaintainedSpell to fail for non-existent spell")
	}
}

func TestEntitySpellEffects(t *testing.T) {
	entity := NewEntity()

	// Test adding spell effect
	success := entity.AddSpellEffect(456, 789, 30.0)
	if !success {
		t.Error("Expected AddSpellEffect to succeed")
	}

	// Test removing spell effect
	success = entity.RemoveSpellEffect(456)
	if !success {
		t.Error("Expected RemoveSpellEffect to succeed")
	}

	// Test removing non-existent spell effect
	success = entity.RemoveSpellEffect(999)
	if success {
		t.Error("Expected RemoveSpellEffect to fail for non-existent spell")
	}
}

func TestEntityDetrimentalSpells(t *testing.T) {
	entity := NewEntity()

	// Test adding detrimental spell
	entity.AddDetrimentalSpell(789, 456, 45.0, 1)

	// Test getting detrimental effect
	effect := entity.GetDetrimentalEffect(789, 456)
	if effect == nil {
		t.Error("Expected to find detrimental spell effect")
	}

	// Test removing detrimental spell
	success := entity.RemoveDetrimentalSpell(789, 456)
	if !success {
		t.Error("Expected RemoveDetrimentalSpell to succeed")
	}

	// Test removing non-existent detrimental spell
	success = entity.RemoveDetrimentalSpell(999, 888)
	if success {
		t.Error("Expected RemoveDetrimentalSpell to fail for non-existent spell")
	}
}

func TestEntityBonusSystem(t *testing.T) {
	entity := NewEntity()

	// Test adding skill bonus
	entity.AddSkillBonus(123, 5, 15.0) // Skill ID 5, value 15.0

	// Test adding stat bonus
	entity.AddStatBonus(124, 1, 5.0) // Stat type 1 (STR), value 5.0

	// Test calculating bonuses
	entity.CalculateBonuses()

	// The actual bonus calculation depends on the spell effect manager implementation
	// We just test that the method doesn't panic
}

func TestEntityPetManagement(t *testing.T) {
	entity := NewEntity()
	pet := NewEntity()

	// Test setting and getting summon pet
	entity.SetPet(pet)
	if entity.GetPet() != pet {
		t.Error("Expected pet to be set correctly")
	}

	if pet.GetOwner() != entity.GetID() {
		t.Error("Expected pet owner to be set correctly")
	}

	if pet.GetPetType() != PetTypeSummon {
		t.Error("Expected pet type to be summon")
	}

	// Test charmed pet
	charmedPet := NewEntity()
	entity.SetCharmedPet(charmedPet)
	if entity.GetCharmedPet() != charmedPet {
		t.Error("Expected charmed pet to be set correctly")
	}

	if charmedPet.GetPetType() != PetTypeCharm {
		t.Error("Expected pet type to be charm")
	}

	// Test deity pet
	deityPet := NewEntity()
	entity.SetDeityPet(deityPet)
	if entity.GetDeityPet() != deityPet {
		t.Error("Expected deity pet to be set correctly")
	}

	if deityPet.GetPetType() != PetTypeDeity {
		t.Error("Expected pet type to be deity")
	}

	// Test cosmetic pet
	cosmeticPet := NewEntity()
	entity.SetCosmeticPet(cosmeticPet)
	if entity.GetCosmeticPet() != cosmeticPet {
		t.Error("Expected cosmetic pet to be set correctly")
	}

	if cosmeticPet.GetPetType() != PetTypeCosmetic {
		t.Error("Expected pet type to be cosmetic")
	}

	// Test pet dismissing state
	entity.SetPetDismissing(true)
	if !entity.IsPetDismissing() {
		t.Error("Expected pet to be dismissing")
	}

	entity.SetPetDismissing(false)
	if entity.IsPetDismissing() {
		t.Error("Expected pet to not be dismissing")
	}
}

func TestEntityDeity(t *testing.T) {
	entity := NewEntity()

	// Test setting and getting deity
	entity.SetDeity(5)
	if entity.GetDeity() != 5 {
		t.Errorf("Expected deity 5, got %d", entity.GetDeity())
	}
}

func TestEntityDodgeChance(t *testing.T) {
	entity := NewEntity()

	// Test base dodge chance
	dodgeChance := entity.GetDodgeChance()
	if dodgeChance != 5.0 {
		t.Errorf("Expected base dodge chance 5.0, got %f", dodgeChance)
	}
}

func TestEntitySeeSpells(t *testing.T) {
	entity := NewEntity()

	// Test see invisible spell
	entity.SetSeeInvisSpell(true)
	if !entity.HasSeeInvisSpell() {
		t.Error("Expected entity to have see invisible spell")
	}

	entity.SetSeeInvisSpell(false)
	if entity.HasSeeInvisSpell() {
		t.Error("Expected entity to not have see invisible spell")
	}

	// Test see hidden spell
	entity.SetSeeHideSpell(true)
	if !entity.HasSeeHideSpell() {
		t.Error("Expected entity to have see hidden spell")
	}

	entity.SetSeeHideSpell(false)
	if entity.HasSeeHideSpell() {
		t.Error("Expected entity to not have see hidden spell")
	}
}

func TestEntityCleanupMethods(t *testing.T) {
	entity := NewEntity()
	info := entity.GetInfoStruct()

	// Set up some spell effects and concentration usage
	info.SetMaxConcentration(10)
	entity.AddMaintainedSpell("Test Spell 1", 123, 60.0, 3)
	entity.AddMaintainedSpell("Test Spell 2", 124, 60.0, 2)
	entity.AddSpellEffect(456, 789, 30.0)

	// Verify concentration is used
	if info.GetCurConcentration() != 5 {
		t.Errorf("Expected current concentration 5, got %d", info.GetCurConcentration())
	}

	// Test deleting all spell effects
	entity.DeleteSpellEffects(false)

	// Verify concentration was returned
	if info.GetCurConcentration() != 0 {
		t.Errorf("Expected current concentration 0 after cleanup, got %d", info.GetCurConcentration())
	}

	// Test RemoveSpells
	entity.AddMaintainedSpell("Test Spell 3", 125, 60.0, 2)
	entity.RemoveSpells(false) // Remove all spells

	if info.GetCurConcentration() != 0 {
		t.Errorf("Expected current concentration 0 after RemoveSpells, got %d", info.GetCurConcentration())
	}
}

func TestEntityProcessEffects(t *testing.T) {
	entity := NewEntity()

	// Test that ProcessEffects doesn't panic
	entity.ProcessEffects()
}

func TestEntityClassSystemIntegration(t *testing.T) {
	entity := NewEntity()
	info := entity.GetInfoStruct()

	// Test class getter/setter
	info.SetClass1(5)
	if entity.GetClass() != 5 {
		t.Errorf("Expected class 5, got %d", entity.GetClass())
	}

	entity.SetClass(10)
	if entity.GetClass() != 10 {
		t.Errorf("Expected class 10, got %d", entity.GetClass())
	}

	// Test level getter
	info.SetLevel(25)
	if entity.GetLevel() != 25 {
		t.Errorf("Expected level 25, got %d", entity.GetLevel())
	}
}

func TestEntityConcurrency(t *testing.T) {
	entity := NewEntity()
	info := entity.GetInfoStruct()
	info.SetMaxConcentration(20)

	var wg sync.WaitGroup
	numGoroutines := 10

	// Test concurrent access to combat state
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		go func() {
			defer wg.Done()
			entity.SetInCombat(true)
			_ = entity.IsInCombat()
			entity.SetInCombat(false)
		}()
	}
	wg.Wait()

	// Test concurrent access to casting state
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		go func() {
			defer wg.Done()
			entity.SetCasting(true)
			_ = entity.IsCasting()
			entity.SetCasting(false)
		}()
	}
	wg.Wait()

	// Test concurrent spell effect operations
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		spellID := int32(1000 + i)
		go func(id int32) {
			defer wg.Done()
			entity.AddSpellEffect(id, 123, 30.0)
			time.Sleep(time.Millisecond) // Small delay to ensure some overlap
			entity.RemoveSpellEffect(id)
		}(spellID)
	}
	wg.Wait()

	// Test concurrent maintained spell operations
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		spellID := int32(2000 + i)
		go func(id int32) {
			defer wg.Done()
			if entity.AddMaintainedSpell("Concurrent Spell", id, 60.0, 1) {
				time.Sleep(time.Millisecond)
				entity.RemoveMaintainedSpell(id)
			}
		}(spellID)
	}
	wg.Wait()
}

func TestEntityControlEffects(t *testing.T) {
	entity := NewEntity()

	// Test has control effect - should work without panicking
	// The actual implementation depends on the spell effect manager
	hasStun := entity.HasControlEffect(ControlEffectStun)
	_ = hasStun // We can't easily test the actual value without setting up effects
}

func TestEntityConstants(t *testing.T) {
	// Test pet type constants
	if PetTypeSummon != 1 {
		t.Errorf("Expected PetTypeSummon to be 1, got %d", PetTypeSummon)
	}

	if PetTypeCharm != 2 {
		t.Errorf("Expected PetTypeCharm to be 2, got %d", PetTypeCharm)
	}

	if PetTypeDeity != 3 {
		t.Errorf("Expected PetTypeDeity to be 3, got %d", PetTypeDeity)
	}

	if PetTypeCosmetic != 4 {
		t.Errorf("Expected PetTypeCosmetic to be 4, got %d", PetTypeCosmetic)
	}

	// Test control effect constants (re-exported from spells package)
	if ControlEffectStun == 0 && ControlEffectRoot == 0 {
		t.Error("Control effect constants should be non-zero")
	}
}