package entity

import (
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

// TestEntityConcurrencyStress performs intensive concurrent operations to identify race conditions
func TestEntityConcurrencyStress(t *testing.T) {
	entity := NewEntity()
	info := entity.GetInfoStruct()
	info.SetMaxConcentration(1000) // Large concentration pool

	var wg sync.WaitGroup
	numGoroutines := 100
	operationsPerGoroutine := 100

	// Test 1: Concurrent combat and casting state changes
	t.Run("CombatCastingStates", func(t *testing.T) {
		var combatOps, castingOps int64
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func() {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine; j++ {
					// Combat state operations
					entity.SetInCombat(true)
					if entity.IsInCombat() {
						atomic.AddInt64(&combatOps, 1)
					}
					entity.SetInCombat(false)
					
					// Casting state operations
					entity.SetCasting(true)
					if entity.IsCasting() {
						atomic.AddInt64(&castingOps, 1)
					}
					entity.SetCasting(false)
				}
			}()
		}
		wg.Wait()
		
		t.Logf("Combat operations: %d, Casting operations: %d", combatOps, castingOps)
	})

	// Test 2: Concurrent spell effect operations
	t.Run("SpellEffects", func(t *testing.T) {
		var addOps, removeOps int64
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine; j++ {
					spellID := int32(goroutineID*1000 + j)
					
					if entity.AddSpellEffect(spellID, int32(goroutineID), 30.0) {
						atomic.AddInt64(&addOps, 1)
					}
					
					if entity.RemoveSpellEffect(spellID) {
						atomic.AddInt64(&removeOps, 1)
					}
				}
			}(i)
		}
		wg.Wait()
		
		t.Logf("Spell effect adds: %d, removes: %d", addOps, removeOps)
	})

	// Test 3: Concurrent maintained spell operations with concentration management
	t.Run("MaintainedSpells", func(t *testing.T) {
		var addOps, removeOps, concentrationFailures int64
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine/10; j++ { // Fewer ops due to concentration limits
					spellID := int32(goroutineID*100 + j + 10000)
					
					if entity.AddMaintainedSpell("Stress Test Spell", spellID, 60.0, 1) {
						atomic.AddInt64(&addOps, 1)
						
						// Small delay to increase contention
						time.Sleep(time.Microsecond)
						
						if entity.RemoveMaintainedSpell(spellID) {
							atomic.AddInt64(&removeOps, 1)
						}
					} else {
						atomic.AddInt64(&concentrationFailures, 1)
					}
				}
			}(i)
		}
		wg.Wait()
		
		t.Logf("Maintained spell adds: %d, removes: %d, concentration failures: %d", 
			addOps, removeOps, concentrationFailures)
		
		// Verify concentration was properly managed
		currentConc := info.GetCurConcentration()
		if currentConc != 0 {
			t.Errorf("Expected concentration to be 0 after all operations, got %d", currentConc)
		}
	})

	// Test 4: Concurrent stat calculations with bonuses
	t.Run("StatCalculations", func(t *testing.T) {
		var statReads int64
		
		// Set some base stats
		info.SetStr(100.0)
		info.SetSta(100.0)
		info.SetAgi(100.0)
		info.SetWis(100.0)
		info.SetIntel(100.0)
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine; j++ {
					// Add some stat bonuses
					entity.AddStatBonus(int32(goroutineID*1000+j), 1, float32(j%10))
					
					// Read stats (these involve bonus calculations)
					_ = entity.GetStr()
					_ = entity.GetSta()
					_ = entity.GetAgi()
					_ = entity.GetWis()
					_ = entity.GetIntel()
					_ = entity.GetPrimaryStat()
					
					atomic.AddInt64(&statReads, 6)
					
					// Trigger bonus recalculation
					entity.CalculateBonuses()
				}
			}(i)
		}
		wg.Wait()
		
		t.Logf("Stat reads: %d", statReads)
	})

	// Test 5: Concurrent pet management
	t.Run("PetManagement", func(t *testing.T) {
		var petOps int64
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func() {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine/10; j++ {
					pet := NewEntity()
					
					// Test different pet types
					switch j % 4 {
					case 0:
						entity.SetPet(pet)
						_ = entity.GetPet()
						entity.SetPet(nil)
					case 1:
						entity.SetCharmedPet(pet)
						_ = entity.GetCharmedPet()
						entity.SetCharmedPet(nil)
					case 2:
						entity.SetDeityPet(pet)
						_ = entity.GetDeityPet()
						entity.SetDeityPet(nil)
					case 3:
						entity.SetCosmeticPet(pet)
						_ = entity.GetCosmeticPet()
						entity.SetCosmeticPet(nil)
					}
					
					atomic.AddInt64(&petOps, 1)
				}
			}()
		}
		wg.Wait()
		
		t.Logf("Pet operations: %d", petOps)
	})
}

// TestInfoStructConcurrencyStress performs intensive concurrent operations on InfoStruct
func TestInfoStructConcurrencyStress(t *testing.T) {
	info := NewInfoStruct()
	info.SetMaxConcentration(1000)

	var wg sync.WaitGroup
	numGoroutines := 100
	operationsPerGoroutine := 100

	// Test 1: Concurrent basic property access
	t.Run("BasicProperties", func(t *testing.T) {
		var nameOps, levelOps, statOps int64
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine; j++ {
					// Name operations
					info.SetName("TestChar" + string(rune('A'+goroutineID%26)))
					_ = info.GetName()
					atomic.AddInt64(&nameOps, 1)
					
					// Level operations
					info.SetLevel(int16(j % 100))
					_ = info.GetLevel()
					atomic.AddInt64(&levelOps, 1)
					
					// Stat operations
					info.SetStr(float32(j))
					_ = info.GetStr()
					atomic.AddInt64(&statOps, 1)
				}
			}(i)
		}
		wg.Wait()
		
		t.Logf("Name ops: %d, Level ops: %d, Stat ops: %d", nameOps, levelOps, statOps)
	})

	// Test 2: Concurrent concentration management
	t.Run("ConcentrationManagement", func(t *testing.T) {
		var addSuccesses, addFailures, removes int64
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func() {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine; j++ {
					amount := int16(j%5 + 1) // 1-5 concentration points
					
					if info.AddConcentration(amount) {
						atomic.AddInt64(&addSuccesses, 1)
						
						// Small delay to increase contention
						time.Sleep(time.Microsecond)
						
						info.RemoveConcentration(amount)
						atomic.AddInt64(&removes, 1)
					} else {
						atomic.AddInt64(&addFailures, 1)
					}
				}
			}()
		}
		wg.Wait()
		
		t.Logf("Concentration adds: %d, failures: %d, removes: %d", 
			addSuccesses, addFailures, removes)
		
		// Verify final state
		finalConc := info.GetCurConcentration()
		if finalConc < 0 || finalConc > info.GetMaxConcentration() {
			t.Errorf("Invalid final concentration: %d (max: %d)", finalConc, info.GetMaxConcentration())
		}
	})

	// Test 3: Concurrent coin operations
	t.Run("CoinOperations", func(t *testing.T) {
		var addOps, removeSuccesses, removeFailures int64
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine; j++ {
					amount := int32((goroutineID*1000 + j) % 10000)
					
					// Add coins
					info.AddCoins(amount)
					atomic.AddInt64(&addOps, 1)
					
					// Try to remove some coins
					removeAmount := amount / 2
					if info.RemoveCoins(removeAmount) {
						atomic.AddInt64(&removeSuccesses, 1)
					} else {
						atomic.AddInt64(&removeFailures, 1)
					}
					
					// Read total coins
					_ = info.GetCoins()
				}
			}(i)
		}
		wg.Wait()
		
		t.Logf("Coin adds: %d, remove successes: %d, failures: %d", 
			addOps, removeSuccesses, removeFailures)
		
		// Verify coins are non-negative
		finalCoins := info.GetCoins()
		if finalCoins < 0 {
			t.Errorf("Coins became negative: %d", finalCoins)
		}
	})

	// Test 4: Concurrent resistance operations
	t.Run("ResistanceOperations", func(t *testing.T) {
		var resistOps int64
		resistTypes := []string{"heat", "cold", "magic", "mental", "divine", "disease", "poison"}
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine; j++ {
					resistType := resistTypes[j%len(resistTypes)]
					value := int16(j % 100)
					
					info.SetResistance(resistType, value)
					_ = info.GetResistance(resistType)
					atomic.AddInt64(&resistOps, 1)
				}
			}(i)
		}
		wg.Wait()
		
		t.Logf("Resistance operations: %d", resistOps)
	})

	// Test 5: Concurrent clone operations
	t.Run("CloneOperations", func(t *testing.T) {
		var cloneOps int64
		
		// Set some initial state
		info.SetName("Original")
		info.SetLevel(50)
		info.SetStr(100.0)
		info.AddConcentration(5)
		
		wg.Add(numGoroutines)
		for i := 0; i < numGoroutines; i++ {
			go func() {
				defer wg.Done()
				
				for j := 0; j < operationsPerGoroutine/10; j++ { // Fewer clones as they're expensive
					clone := info.Clone()
					if clone != nil {
						// Verify clone independence
						clone.SetName("Clone")
						if info.GetName() == "Clone" {
							t.Errorf("Clone modified original")
						}
						atomic.AddInt64(&cloneOps, 1)
					}
				}
			}()
		}
		wg.Wait()
		
		t.Logf("Clone operations: %d", cloneOps)
	})
}

// TestRaceConditionDetection uses the race detector to find potential issues
func TestRaceConditionDetection(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping race condition test in short mode")
	}

	entity := NewEntity()
	info := entity.GetInfoStruct()
	info.SetMaxConcentration(100)

	var wg sync.WaitGroup
	numGoroutines := 50

	// Create a scenario designed to trigger race conditions
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		go func(id int) {
			defer wg.Done()
			
			for j := 0; j < 50; j++ {
				// Mix of read and write operations that could race
				entity.SetInCombat(id%2 == 0)
				isInCombat := entity.IsInCombat()
				
				entity.SetCasting(j%2 == 0)
				isCasting := entity.IsCasting()
				
				// Stats with bonus calculations
				info.SetStr(float32(id + j))
				str := entity.GetStr()
				
				// Concentration with potential for contention
				if info.AddConcentration(1) {
					info.RemoveConcentration(1)
				}
				
				// Use the values to prevent optimization
				_ = isInCombat
				_ = isCasting
				_ = str
			}
		}(i)
	}
	wg.Wait()
}

// Cleanup test to run after stress tests
func TestConcurrencyCleanup(t *testing.T) {
	entity := NewEntity()
	info := entity.GetInfoStruct()
	
	// Verify entity is in clean state after stress tests
	if entity.IsInCombat() {
		t.Error("Entity should not be in combat after tests")
	}
	
	if entity.IsCasting() {
		t.Error("Entity should not be casting after tests")
	}
	
	if info.GetCurConcentration() < 0 {
		t.Error("Concentration should not be negative")
	}
	
	if info.GetCoins() < 0 {
		t.Error("Coins should not be negative")
	}
}