package entity

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

func TestNewInfoStruct(t *testing.T) {
	info := NewInfoStruct()
	if info == nil {
		t.Fatal("NewInfoStruct returned nil")
	}

	// Test default values
	if info.GetName() != "" {
		t.Errorf("Expected empty name, got %s", info.GetName())
	}

	if info.GetLevel() != 0 {
		t.Errorf("Expected level 0, got %d", info.GetLevel())
	}

	if info.GetMaxConcentration() != 5 {
		t.Errorf("Expected max concentration 5, got %d", info.GetMaxConcentration())
	}

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

func TestInfoStructBasicProperties(t *testing.T) {
	info := NewInfoStruct()

	// Test name
	info.SetName("Test Character")
	if info.GetName() != "Test Character" {
		t.Errorf("Expected name 'Test Character', got %s", info.GetName())
	}

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

	// Test effective level
	info.SetEffectiveLevel(30)
	if info.GetEffectiveLevel() != 30 {
		t.Errorf("Expected effective level 30, got %d", info.GetEffectiveLevel())
	}

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

	// Test race
	info.SetRace(3)
	if info.GetRace() != 3 {
		t.Errorf("Expected race 3, got %d", info.GetRace())
	}

	// Test gender
	info.SetGender(1)
	if info.GetGender() != 1 {
		t.Errorf("Expected gender 1, got %d", info.GetGender())
	}
}

func TestInfoStructStats(t *testing.T) {
	info := NewInfoStruct()

	// Test strength
	info.SetStr(15.5)
	if info.GetStr() != 15.5 {
		t.Errorf("Expected strength 15.5, got %f", info.GetStr())
	}

	// Test stamina
	info.SetSta(20.0)
	if info.GetSta() != 20.0 {
		t.Errorf("Expected stamina 20.0, got %f", info.GetSta())
	}

	// Test agility
	info.SetAgi(12.75)
	if info.GetAgi() != 12.75 {
		t.Errorf("Expected agility 12.75, got %f", info.GetAgi())
	}

	// Test wisdom
	info.SetWis(18.25)
	if info.GetWis() != 18.25 {
		t.Errorf("Expected wisdom 18.25, got %f", info.GetWis())
	}

	// Test intelligence
	info.SetIntel(22.5)
	if info.GetIntel() != 22.5 {
		t.Errorf("Expected intelligence 22.5, got %f", info.GetIntel())
	}
}

func TestInfoStructConcentration(t *testing.T) {
	info := NewInfoStruct()

	// Test setting max concentration
	info.SetMaxConcentration(15)
	if info.GetMaxConcentration() != 15 {
		t.Errorf("Expected max concentration 15, got %d", info.GetMaxConcentration())
	}

	// Test adding concentration
	success := info.AddConcentration(5)
	if !success {
		t.Error("Expected AddConcentration to succeed")
	}

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

	// Test adding concentration that would exceed maximum
	success = info.AddConcentration(12)
	if success {
		t.Error("Expected AddConcentration to fail when exceeding maximum")
	}

	if info.GetCurConcentration() != 5 {
		t.Errorf("Expected current concentration to remain 5, got %d", info.GetCurConcentration())
	}

	// Test adding concentration that exactly reaches maximum
	success = info.AddConcentration(10)
	if !success {
		t.Error("Expected AddConcentration to succeed when exactly reaching maximum")
	}

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

	// Test removing concentration
	info.RemoveConcentration(7)
	if info.GetCurConcentration() != 8 {
		t.Errorf("Expected current concentration 8, got %d", info.GetCurConcentration())
	}

	// Test removing more concentration than available
	info.RemoveConcentration(20)
	if info.GetCurConcentration() != 0 {
		t.Errorf("Expected current concentration to be clamped to 0, got %d", info.GetCurConcentration())
	}
}

func TestInfoStructCoins(t *testing.T) {
	info := NewInfoStruct()

	// Test initial coins
	if info.GetCoins() != 0 {
		t.Errorf("Expected initial coins 0, got %d", info.GetCoins())
	}

	// Test adding copper
	info.AddCoins(150) // 1 silver and 50 copper
	totalCopper := info.GetCoins()
	if totalCopper != 150 {
		t.Errorf("Expected total coins 150, got %d", totalCopper)
	}

	// Test adding large amount that converts to higher denominations
	info.AddCoins(1234567) // Should convert to plat, gold, silver, copper
	expectedTotal := 150 + 1234567
	totalCopper = info.GetCoins()
	if totalCopper != int32(expectedTotal) {
		t.Errorf("Expected total coins %d, got %d", expectedTotal, totalCopper)
	}

	// Test removing coins
	success := info.RemoveCoins(100)
	if !success {
		t.Error("Expected RemoveCoins to succeed")
	}

	expectedTotal -= 100
	totalCopper = info.GetCoins()
	if totalCopper != int32(expectedTotal) {
		t.Errorf("Expected total coins %d after removal, got %d", expectedTotal, totalCopper)
	}

	// Test removing more coins than available
	success = info.RemoveCoins(9999999)
	if success {
		t.Error("Expected RemoveCoins to fail when removing more than available")
	}

	// Total should remain unchanged
	totalCopper = info.GetCoins()
	if totalCopper != int32(expectedTotal) {
		t.Errorf("Expected total coins to remain %d, got %d", expectedTotal, totalCopper)
	}
}

func TestInfoStructResistances(t *testing.T) {
	info := NewInfoStruct()

	// Test all resistance types
	resistanceTypes := []string{"heat", "cold", "magic", "mental", "divine", "disease", "poison"}
	expectedValues := []int16{10, 15, 20, 25, 30, 35, 40}

	for i, resistType := range resistanceTypes {
		expectedValue := expectedValues[i]

		// Set resistance
		info.SetResistance(resistType, expectedValue)

		// Get resistance
		actualValue := info.GetResistance(resistType)
		if actualValue != expectedValue {
			t.Errorf("Expected %s resistance %d, got %d", resistType, expectedValue, actualValue)
		}
	}

	// Test invalid resistance type
	unknownResist := info.GetResistance("unknown")
	if unknownResist != 0 {
		t.Errorf("Expected unknown resistance type to return 0, got %d", unknownResist)
	}

	// Setting invalid resistance type should not panic
	info.SetResistance("unknown", 50) // Should be ignored
}

func TestInfoStructResetEffects(t *testing.T) {
	info := NewInfoStruct()

	// Set some base values first (these would normally be set during character creation)
	info.str = 10.0
	info.strBase = 10.0
	info.sta = 12.0
	info.staBase = 12.0
	info.heat = 15
	info.heatBase = 15

	// Modify current values to simulate bonuses
	info.SetStr(20.0)
	info.SetSta(25.0)
	info.SetResistance("heat", 30)

	// Reset effects
	info.ResetEffects()

	// Check that values were reset to base
	if info.GetStr() != 10.0 {
		t.Errorf("Expected strength to reset to 10.0, got %f", info.GetStr())
	}

	if info.GetSta() != 12.0 {
		t.Errorf("Expected stamina to reset to 12.0, got %f", info.GetSta())
	}

	if info.GetResistance("heat") != 15 {
		t.Errorf("Expected heat resistance to reset to 15, got %d", info.GetResistance("heat"))
	}
}

func TestInfoStructCalculatePrimaryStat(t *testing.T) {
	info := NewInfoStruct()

	// Set all stats to different values
	info.SetStr(10.0)
	info.SetSta(15.0)
	info.SetAgi(8.0)
	info.SetWis(20.0) // This should be the highest
	info.SetIntel(12.0)

	primaryStat := info.CalculatePrimaryStat()
	if primaryStat != 20.0 {
		t.Errorf("Expected primary stat 20.0 (wisdom), got %f", primaryStat)
	}

	// Test with intelligence being highest
	info.SetIntel(25.0)
	primaryStat = info.CalculatePrimaryStat()
	if primaryStat != 25.0 {
		t.Errorf("Expected primary stat 25.0 (intelligence), got %f", primaryStat)
	}

	// Test with all stats equal
	info.SetStr(30.0)
	info.SetSta(30.0)
	info.SetAgi(30.0)
	info.SetWis(30.0)
	info.SetIntel(30.0)

	primaryStat = info.CalculatePrimaryStat()
	if primaryStat != 30.0 {
		t.Errorf("Expected primary stat 30.0 (all equal), got %f", primaryStat)
	}
}

func TestInfoStructClone(t *testing.T) {
	info := NewInfoStruct()

	// Set some values
	info.SetName("Original Character")
	info.SetLevel(15)
	info.SetStr(20.0)
	info.SetResistance("magic", 25)
	info.AddConcentration(3)

	// Clone the info struct
	clone := info.Clone()
	if clone == nil {
		t.Fatal("Clone returned nil")
	}

	// Verify clone has same values
	if clone.GetName() != "Original Character" {
		t.Errorf("Expected cloned name 'Original Character', got %s", clone.GetName())
	}

	if clone.GetLevel() != 15 {
		t.Errorf("Expected cloned level 15, got %d", clone.GetLevel())
	}

	if clone.GetStr() != 20.0 {
		t.Errorf("Expected cloned strength 20.0, got %f", clone.GetStr())
	}

	if clone.GetResistance("magic") != 25 {
		t.Errorf("Expected cloned magic resistance 25, got %d", clone.GetResistance("magic"))
	}

	if clone.GetCurConcentration() != 3 {
		t.Errorf("Expected cloned concentration 3, got %d", clone.GetCurConcentration())
	}

	// Verify that modifying the clone doesn't affect the original
	clone.SetName("Cloned Character")
	clone.SetLevel(20)

	if info.GetName() != "Original Character" {
		t.Error("Original name was modified when clone was changed")
	}

	if info.GetLevel() != 15 {
		t.Error("Original level was modified when clone was changed")
	}
}

func TestInfoStructGetUptime(t *testing.T) {
	info := NewInfoStruct()

	// Test uptime (currently returns 0 as it's not implemented)
	uptime := info.GetUptime()
	if uptime != time.Duration(0) {
		t.Errorf("Expected uptime to be 0 (not implemented), got %v", uptime)
	}
}

func TestInfoStructConcurrency(t *testing.T) {
	info := NewInfoStruct()
	info.SetMaxConcentration(100)

	var wg sync.WaitGroup
	numGoroutines := 20

	// Test concurrent access to basic properties
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		go func(index int) {
			defer wg.Done()
			
			// Each goroutine sets unique values
			info.SetName("Character" + string(rune('A'+index)))
			_ = info.GetName()
			
			info.SetLevel(int16(10 + index))
			_ = info.GetLevel()
			
			info.SetStr(float32(10.0 + float32(index)))
			_ = info.GetStr()
		}(i)
	}
	wg.Wait()

	// Test concurrent concentration operations
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		go func() {
			defer wg.Done()
			
			// Try to add concentration
			if info.AddConcentration(1) {
				// If successful, remove it after a short delay
				time.Sleep(time.Microsecond)
				info.RemoveConcentration(1)
			}
		}()
	}
	wg.Wait()

	// After all operations, concentration should be back to 0
	// (This might not always be true due to race conditions, but shouldn't crash)
	_ = info.GetCurConcentration()

	// Test concurrent coin operations
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		go func(amount int32) {
			defer wg.Done()
			
			info.AddCoins(amount)
			_ = info.GetCoins()
			
			// Try to remove some coins
			info.RemoveCoins(amount / 2)
		}(int32(100 + i))
	}
	wg.Wait()

	// Test concurrent resistance operations
	wg.Add(numGoroutines)
	for i := 0; i < numGoroutines; i++ {
		go func(value int16) {
			defer wg.Done()
			
			info.SetResistance("heat", value)
			_ = info.GetResistance("heat")
			
			info.SetResistance("cold", value+1)
			_ = info.GetResistance("cold")
		}(int16(i))
	}
	wg.Wait()
}

func TestInfoStructLargeValues(t *testing.T) {
	info := NewInfoStruct()

	// Test with large coin amounts
	largeCoinAmount := int32(2000000000) // 2 billion copper
	info.AddCoins(largeCoinAmount)
	
	totalCoins := info.GetCoins()
	if totalCoins != largeCoinAmount {
		t.Errorf("Expected large coin amount %d, got %d", largeCoinAmount, totalCoins)
	}

	// Test removing large amounts
	success := info.RemoveCoins(largeCoinAmount)
	if !success {
		t.Error("Expected to be able to remove large coin amount")
	}

	if info.GetCoins() != 0 {
		t.Errorf("Expected coins to be 0 after removing all, got %d", info.GetCoins())
	}

	// Test with maximum values
	info.SetMaxConcentration(32767) // Max int16
	info.SetLevel(32767)
	
	if info.GetMaxConcentration() != 32767 {
		t.Errorf("Expected max concentration 32767, got %d", info.GetMaxConcentration())
	}
	
	if info.GetLevel() != 32767 {
		t.Errorf("Expected level 32767, got %d", info.GetLevel())
	}
}

func TestInfoStructEdgeCases(t *testing.T) {
	info := NewInfoStruct()

	// Test negative values
	info.SetStr(-5.0)
	if info.GetStr() != -5.0 {
		t.Errorf("Expected negative strength -5.0, got %f", info.GetStr())
	}

	// Test zero values
	info.SetMaxConcentration(0)
	success := info.AddConcentration(1)
	if success {
		t.Error("Expected AddConcentration to fail with max concentration 0")
	}

	// Test very small float values
	info.SetAgi(0.001)
	if info.GetAgi() != 0.001 {
		t.Errorf("Expected small agility 0.001, got %f", info.GetAgi())
	}

	// Test empty string name
	info.SetName("")
	if info.GetName() != "" {
		t.Errorf("Expected empty name, got '%s'", info.GetName())
	}

	// Test very long name
	longName := string(make([]byte, 1000))
	for i := range longName {
		longName = longName[:i] + "A" + longName[i+1:]
	}
	info.SetName(longName)
	if info.GetName() != longName {
		t.Error("Expected to handle very long names")
	}
}