package ground_spawn

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

// Mock implementations are in test_utils.go

// Test core GroundSpawn concurrency patterns without dependencies
func TestGroundSpawnCoreConcurrency(t *testing.T) {
	config := GroundSpawnConfig{
		GroundSpawnID:      1,
		CollectionSkill:    SkillGathering,
		NumberHarvests:     10,
		AttemptsPerHarvest: 2,
		RandomizeHeading:   true,
		Location: SpawnLocation{
			X: 100.0, Y: 200.0, Z: 300.0, Heading: 45.0, GridID: 1,
		},
		Name:        "Test Node",
		Description: "A test harvestable node",
	}

	gs := NewGroundSpawn(config)

	const numGoroutines = 100
	const operationsPerGoroutine = 100

	var wg sync.WaitGroup

	t.Run("ConcurrentAccessors", func(t *testing.T) {
		wg.Add(numGoroutines)

		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()

				for j := 0; j < operationsPerGoroutine; j++ {
					switch j % 8 {
					case 0:
						gs.SetNumberHarvests(int8(goroutineID % 10))
					case 1:
						_ = gs.GetNumberHarvests()
					case 2:
						gs.SetAttemptsPerHarvest(int8(goroutineID % 5))
					case 3:
						_ = gs.GetAttemptsPerHarvest()
					case 4:
						gs.SetCollectionSkill(SkillMining)
					case 5:
						_ = gs.GetCollectionSkill()
					case 6:
						gs.SetRandomizeHeading(goroutineID%2 == 0)
					case 7:
						_ = gs.GetRandomizeHeading()
					}
				}
			}(i)
		}

		wg.Wait()
	})

	t.Run("ConcurrentStateChecks", func(t *testing.T) {
		wg.Add(numGoroutines)

		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()

				for j := 0; j < operationsPerGoroutine; j++ {
					switch j % 4 {
					case 0:
						_ = gs.IsDepleted()
					case 1:
						_ = gs.IsAvailable()
					case 2:
						_ = gs.GetHarvestMessageName(true, false)
					case 3:
						_ = gs.GetHarvestSpellType()
					}
				}
			}(i)
		}

		wg.Wait()
	})
}

// Test Manager core concurrency patterns
func TestManagerCoreConcurrency(t *testing.T) {
	manager := NewManager(nil, &mockLogger{})

	// Pre-populate with some ground spawns
	for i := int32(1); i <= 10; i++ {
		config := GroundSpawnConfig{
			GroundSpawnID:      i,
			CollectionSkill:    SkillGathering,
			NumberHarvests:     5,
			AttemptsPerHarvest: 1,
			Location: SpawnLocation{
				X: float32(i * 10), Y: float32(i * 20), Z: float32(i * 30),
				Heading: float32(i * 45), GridID: 1,
			},
			Name:        "Test Node",
			Description: "Test node",
		}
		gs := manager.CreateGroundSpawn(config)
		if gs == nil {
			t.Fatalf("Failed to create ground spawn %d", i)
		}
	}

	const numGoroutines = 50
	const operationsPerGoroutine = 50

	var wg sync.WaitGroup

	t.Run("ConcurrentAccess", func(t *testing.T) {
		wg.Add(numGoroutines)

		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()

				for j := 0; j < operationsPerGoroutine; j++ {
					spawnID := int32((goroutineID % 10) + 1)

					switch j % 5 {
					case 0:
						_ = manager.GetGroundSpawn(spawnID)
					case 1:
						_ = manager.GetGroundSpawnsByZone(1)
					case 2:
						_ = manager.GetGroundSpawnCount()
					case 3:
						_ = manager.GetActiveGroundSpawns()
					case 4:
						_ = manager.GetDepletedGroundSpawns()
					}
				}
			}(i)
		}

		wg.Wait()
	})

	t.Run("ConcurrentStatistics", func(t *testing.T) {
		wg.Add(numGoroutines)

		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()

				for j := 0; j < operationsPerGoroutine; j++ {
					switch j % 3 {
					case 0:
						_ = manager.GetStatistics()
					case 1:
						manager.ResetStatistics()
					case 2:
						// Simulate statistics updates
						manager.mutex.Lock()
						manager.totalHarvests++
						manager.successfulHarvests++
						skill := SkillGathering
						manager.harvestsBySkill[skill]++
						manager.mutex.Unlock()
					}
				}
			}(i)
		}

		wg.Wait()

		// Verify statistics consistency
		stats := manager.GetStatistics()
		if stats.TotalHarvests < 0 || stats.SuccessfulHarvests < 0 {
			t.Errorf("Invalid statistics: total=%d, successful=%d",
				stats.TotalHarvests, stats.SuccessfulHarvests)
		}
	})
}

// Test Copy() method thread safety
func TestCopyThreadSafety(t *testing.T) {
	config := GroundSpawnConfig{
		GroundSpawnID:      1,
		CollectionSkill:    SkillGathering,
		NumberHarvests:     5,
		AttemptsPerHarvest: 1,
		Location: SpawnLocation{
			X: 100, Y: 200, Z: 300, Heading: 45, GridID: 1,
		},
		Name:        "Copy Test Node",
		Description: "Test node for copy safety",
	}

	original := NewGroundSpawn(config)

	const numGoroutines = 50
	var wg sync.WaitGroup
	wg.Add(numGoroutines)

	// Test copying while modifying
	for i := 0; i < numGoroutines; i++ {
		go func(goroutineID int) {
			defer wg.Done()

			for j := 0; j < 100; j++ {
				if j%2 == 0 {
					// Copy operations
					copy := original.Copy()
					if copy == nil {
						t.Errorf("Goroutine %d: Copy returned nil", goroutineID)
						continue
					}

					// Verify copy is independent by setting different values
					newValue := int8(goroutineID%5 + 1) // Ensure non-zero value
					copy.SetNumberHarvests(newValue)

					// Copy should have the new value we just set
					if copy.GetNumberHarvests() != newValue {
						t.Errorf("Goroutine %d: Copy failed to set value correctly, expected %d got %d",
							goroutineID, newValue, copy.GetNumberHarvests())
					}
					// Note: We can't reliably test that original is unchanged due to concurrent modifications
				} else {
					// Modify original
					original.SetNumberHarvests(int8(goroutineID % 10))
					original.SetCollectionSkill(SkillMining)
					_ = original.GetRandomizeHeading()
				}
			}
		}(i)
	}

	wg.Wait()
}

// Test core deadlock prevention
func TestCoreDeadlockPrevention(t *testing.T) {
	manager := NewManager(nil, &mockLogger{})

	// Create test ground spawns
	for i := int32(1); i <= 5; i++ {
		config := GroundSpawnConfig{
			GroundSpawnID:      i,
			CollectionSkill:    SkillGathering,
			NumberHarvests:     5,
			AttemptsPerHarvest: 1,
			Location: SpawnLocation{
				X: float32(i * 10), Y: float32(i * 20), Z: float32(i * 30),
				Heading: 0, GridID: 1,
			},
			Name:        "Deadlock Test Node",
			Description: "Test node",
		}
		gs := manager.CreateGroundSpawn(config)
		if gs == nil {
			t.Fatalf("Failed to create ground spawn %d", i)
		}
	}

	const numGoroutines = 25
	var wg sync.WaitGroup

	// Test potential deadlock scenarios
	t.Run("MixedOperations", func(t *testing.T) {
		done := make(chan bool, 1)

		// Set a timeout to detect deadlocks
		go func() {
			time.Sleep(5 * time.Second)
			select {
			case <-done:
				return
			default:
				t.Error("Potential deadlock detected - test timed out")
			}
		}()

		wg.Add(numGoroutines)

		for i := 0; i < numGoroutines; i++ {
			go func(goroutineID int) {
				defer wg.Done()

				for j := 0; j < 50; j++ {
					spawnID := int32((goroutineID % 5) + 1)

					// Mix operations that could potentially deadlock
					switch j % 8 {
					case 0:
						gs := manager.GetGroundSpawn(spawnID)
						if gs != nil {
							_ = gs.GetNumberHarvests()
						}
					case 1:
						_ = manager.GetStatistics()
					case 2:
						_ = manager.GetGroundSpawnsByZone(1)
					case 3:
						gs := manager.GetGroundSpawn(spawnID)
						if gs != nil {
							gs.SetNumberHarvests(int8(j % 5))
						}
					case 4:
						manager.ProcessRespawns()
					case 5:
						_ = manager.GetActiveGroundSpawns()
					case 6:
						gs := manager.GetGroundSpawn(spawnID)
						if gs != nil {
							_ = gs.Copy()
						}
					case 7:
						gs := manager.GetGroundSpawn(spawnID)
						if gs != nil && gs.IsDepleted() {
							manager.scheduleRespawn(gs)
						}
					}
				}
			}(i)
		}

		wg.Wait()
		done <- true
	})
}
