package guilds

import (
	"fmt"
	"testing"
	"time"
)

// TestNewGuild tests guild creation with default values
func TestNewGuild(t *testing.T) {
	guild := NewGuild()

	// Test initial state
	if guild.GetLevel() != 1 {
		t.Errorf("Expected initial level 1, got %d", guild.GetLevel())
	}

	if guild.GetEXPCurrent() != 111 {
		t.Errorf("Expected initial expCurrent 111, got %d", guild.GetEXPCurrent())
	}

	if guild.GetEXPToNextLevel() != 2521 {
		t.Errorf("Expected initial expToNextLevel 2521, got %d", guild.GetEXPToNextLevel())
	}

	if guild.GetRecruitingMinLevel() != 1 {
		t.Errorf("Expected initial recruitingMinLevel 1, got %d", guild.GetRecruitingMinLevel())
	}

	if guild.GetRecruitingPlayStyle() != RecruitingPlayStyleNone {
		t.Errorf("Expected initial recruitingPlayStyle %d, got %d", RecruitingPlayStyleNone, guild.GetRecruitingPlayStyle())
	}

	if guild.GetNextEventID() != 1 {
		t.Errorf("Expected initial nextEventID 1, got %d", guild.GetNextEventID())
	}

	// Test recruiting flags are initialized
	if guild.GetRecruitingFlag(RecruitingFlagTraining) != 0 {
		t.Error("Training recruiting flag should be initialized to 0")
	}

	if guild.GetRecruitingFlag(RecruitingFlagFighters) != 0 {
		t.Error("Fighters recruiting flag should be initialized to 0")
	}

	// Test description tags are initialized
	if guild.GetRecruitingDescTag(0) != RecruitingDescTagNone {
		t.Error("Description tag 0 should be initialized to None")
	}

	// Test default rank names are set
	if guild.GetRankName(RankLeader) != "Leader" {
		t.Errorf("Expected leader rank name 'Leader', got '%s'", guild.GetRankName(RankLeader))
	}

	if guild.GetRankName(RankRecruit) != "Recruit" {
		t.Errorf("Expected recruit rank name 'Recruit', got '%s'", guild.GetRankName(RankRecruit))
	}
}

// TestGuildBasicOperations tests basic guild getter/setter operations
func TestGuildBasicOperations(t *testing.T) {
	guild := NewGuild()

	// Test ID operations
	testID := int32(12345)
	guild.SetID(testID)
	if guild.GetID() != testID {
		t.Errorf("Expected ID %d, got %d", testID, guild.GetID())
	}

	// Test name operations
	testName := "Test Guild"
	guild.SetName(testName, false)
	if guild.GetName() != testName {
		t.Errorf("Expected name '%s', got '%s'", testName, guild.GetName())
	}

	// Test level operations
	testLevel := int8(10)
	guild.SetLevel(testLevel, false)
	if guild.GetLevel() != testLevel {
		t.Errorf("Expected level %d, got %d", testLevel, guild.GetLevel())
	}

	// Test MOTD operations
	testMOTD := "Welcome to our guild!"
	guild.SetMOTD(testMOTD, false)
	if guild.GetMOTD() != testMOTD {
		t.Errorf("Expected MOTD '%s', got '%s'", testMOTD, guild.GetMOTD())
	}

	// Test formed date operations
	testDate := time.Now().Add(-24 * time.Hour)
	guild.SetFormedDate(testDate)
	if !guild.GetFormedDate().Equal(testDate) {
		t.Errorf("Expected formed date %v, got %v", testDate, guild.GetFormedDate())
	}

	// Test experience operations
	testExpCurrent := int64(5000)
	testExpNext := int64(10000)
	guild.SetEXPCurrent(testExpCurrent, false)
	guild.SetEXPToNextLevel(testExpNext, false)
	
	current := guild.GetEXPCurrent()
	next := guild.GetEXPToNextLevel()
	if current != testExpCurrent {
		t.Errorf("Expected current exp %d, got %d", testExpCurrent, current)
	}
	if next != testExpNext {
		t.Errorf("Expected next level exp %d, got %d", testExpNext, next)
	}
}

// TestGuildMemberOperations tests guild member management
func TestGuildMemberOperations(t *testing.T) {
	guild := NewGuild()
	guild.SetID(1)

	// Test adding members using the actual method
	characterID1 := int32(1)
	characterID2 := int32(2)
	inviterName := "TestInviter"
	joinDate := time.Now()

	// Add first member
	success := guild.AddNewGuildMember(characterID1, inviterName, joinDate, RankRecruit)
	if !success {
		t.Error("Should be able to add first member")
	}

	// Add second member
	success = guild.AddNewGuildMember(characterID2, inviterName, joinDate, RankMember)
	if !success {
		t.Error("Should be able to add second member")
	}

	// Test getting member by ID
	member1 := guild.GetGuildMember(characterID1)
	if member1 == nil {
		t.Error("Should be able to retrieve member by ID")
	}
	if member1.CharacterID != characterID1 {
		t.Errorf("Expected member ID %d, got %d", characterID1, member1.CharacterID)
	}

	// Test getting all members
	allMembers := guild.GetAllMembers()
	if len(allMembers) != 2 {
		t.Errorf("Expected 2 members, got %d", len(allMembers))
	}

	// Test removing member
	guild.RemoveGuildMember(characterID1, false)
	member1After := guild.GetGuildMember(characterID1)
	if member1After != nil {
		t.Error("Member should be nil after removal")
	}

	allMembersAfter := guild.GetAllMembers()
	if len(allMembersAfter) != 1 {
		t.Errorf("Expected 1 member after removal, got %d", len(allMembersAfter))
	}
}

// TestGuildEventOperations tests guild event management
func TestGuildEventOperations(t *testing.T) {
	guild := NewGuild()

	// Test adding events
	eventType := int32(EventMemberJoins)
	description := "Member joined guild"
	eventDate := time.Now()

	// Add first event (should get ID 1)
	guild.AddNewGuildEvent(eventType, description, eventDate, false)

	// Add another event (should get ID 2)
	guild.AddNewGuildEvent(EventMemberLeaves, "Member left guild", eventDate, false)

	// Test getting next event ID (should be 3 now)
	nextID := guild.GetNextEventID()
	if nextID != 3 {
		t.Errorf("Expected next event ID 3, got %d", nextID)
	}

	// Test getting specific event (first event should have ID 1)
	event := guild.GetGuildEvent(1)
	if event == nil {
		t.Error("Should be able to retrieve event by ID")
	}
	if event != nil && event.Description != description {
		t.Errorf("Expected event description '%s', got '%s'", description, event.Description)
	}
}

// TestGuildRankOperations tests guild rank management
func TestGuildRankOperations(t *testing.T) {
	guild := NewGuild()

	// Test setting custom rank name
	customRankName := "Elite Member"
	success := guild.SetRankName(RankMember, customRankName, false)
	if !success {
		t.Error("Should be able to set rank name")
	}
	
	rankName := guild.GetRankName(RankMember)
	if rankName != customRankName {
		t.Errorf("Expected rank name '%s', got '%s'", customRankName, rankName)
	}

	// Test getting default rank names
	leaderName := guild.GetRankName(RankLeader)
	if leaderName != "Leader" {
		t.Errorf("Expected leader rank name 'Leader', got '%s'", leaderName)
	}
}

// TestGuildRecruitingOperations tests guild recruiting settings
func TestGuildRecruitingOperations(t *testing.T) {
	guild := NewGuild()

	// Test recruiting descriptions
	shortDesc := "Looking for members"
	fullDesc := "We are a friendly guild looking for active members"
	guild.SetRecruitingShortDesc(shortDesc, false)
	guild.SetRecruitingFullDesc(fullDesc, false)

	short := guild.GetRecruitingShortDesc()
	full := guild.GetRecruitingFullDesc()
	if short != shortDesc {
		t.Errorf("Expected short description '%s', got '%s'", shortDesc, short)
	}
	if full != fullDesc {
		t.Errorf("Expected full description '%s', got '%s'", fullDesc, full)
	}

	// Test recruiting settings
	minLevel := int8(20)
	playStyle := int8(2)
	guild.SetRecruitingMinLevel(minLevel, false)
	guild.SetRecruitingPlayStyle(playStyle, false)

	getMinLevel := guild.GetRecruitingMinLevel()
	getPlayStyle := guild.GetRecruitingPlayStyle()
	if getMinLevel != minLevel {
		t.Errorf("Expected min level %d, got %d", minLevel, getMinLevel)
	}
	if getPlayStyle != playStyle {
		t.Errorf("Expected play style %d, got %d", playStyle, getPlayStyle)
	}

	// Test recruiting flags
	success := guild.SetRecruitingFlag(RecruitingFlagFighters, 1, false)
	if !success {
		t.Error("Should be able to set recruiting flag")
	}
	flag := guild.GetRecruitingFlag(RecruitingFlagFighters)
	if flag != 1 {
		t.Errorf("Expected recruiting flag 1, got %d", flag)
	}

	// Test recruiting description tags
	success = guild.SetRecruitingDescTag(0, RecruitingDescTagRoleplay, false)
	if !success {
		t.Error("Should be able to set recruiting desc tag")
	}
	tag := guild.GetRecruitingDescTag(0)
	if tag != RecruitingDescTagRoleplay {
		t.Errorf("Expected description tag %d, got %d", RecruitingDescTagRoleplay, tag)
	}
}

// TestGuildPermissions tests guild permission system
func TestGuildPermissions(t *testing.T) {
	guild := NewGuild()

	// Test setting permissions
	rank := int8(RankMember)
	permission := int8(PermissionInvite)
	value := int8(1)
	
	success := guild.SetPermission(rank, permission, value, false, false)
	if !success {
		t.Error("Should be able to set permission")
	}
	
	getValue := guild.GetPermission(rank, permission)
	if getValue != value {
		t.Errorf("Expected permission value %d, got %d", value, getValue)
	}

	// Test removing permission
	success = guild.SetPermission(rank, permission, 0, false, false)
	if !success {
		t.Error("Should be able to remove permission")
	}
	
	getValue = guild.GetPermission(rank, permission)
	if getValue != 0 {
		t.Errorf("Expected permission value 0 after removal, got %d", getValue)
	}
}

// TestGuildSaveFlags tests the save flag system
func TestGuildSaveFlags(t *testing.T) {
	guild := NewGuild()

	// Test initial state
	if guild.GetSaveNeeded() {
		t.Error("Guild should not need save initially")
	}

	// Test marking save needed
	guild.SetSaveNeeded(true)
	if !guild.GetSaveNeeded() {
		t.Error("Guild should need save after marking")
	}

	// Test clearing save needed
	guild.SetSaveNeeded(false)
	if guild.GetSaveNeeded() {
		t.Error("Guild should not need save after clearing")
	}
}

// TestGuildMemberPromotionDemotion tests member rank changes
func TestGuildMemberPromotionDemotion(t *testing.T) {
	guild := NewGuild()
	guild.SetID(1)

	// Add a member
	characterID := int32(1)
	inviterName := "TestInviter"
	joinDate := time.Now()
	
	success := guild.AddNewGuildMember(characterID, inviterName, joinDate, RankRecruit)
	if !success {
		t.Error("Should be able to add member")
	}

	// Get member and check initial rank
	member := guild.GetGuildMember(characterID)
	if member == nil {
		t.Fatal("Member should exist")
	}
	if member.Rank != RankRecruit {
		t.Errorf("Expected initial rank %d, got %d", RankRecruit, member.Rank)
	}

	// Test promotion
	promoterName := "TestPromoter"
	success = guild.PromoteGuildMember(characterID, promoterName, false)
	if !success {
		t.Error("Should be able to promote member")
	}

	member = guild.GetGuildMember(characterID)
	if member.Rank != RankInitiate {
		t.Errorf("Expected rank after promotion %d, got %d", RankInitiate, member.Rank)
	}

	// Test demotion
	demoterName := "TestDemoter"
	success = guild.DemoteGuildMember(characterID, demoterName, false)
	if !success {
		t.Error("Should be able to demote member")
	}

	member = guild.GetGuildMember(characterID)
	if member.Rank != RankRecruit {
		t.Errorf("Expected rank after demotion %d, got %d", RankRecruit, member.Rank)
	}
}

// TestGuildPointsSystem tests the guild points system
func TestGuildPointsSystem(t *testing.T) {
	guild := NewGuild()
	guild.SetID(1)

	// Add a member
	characterID := int32(1)
	inviterName := "TestInviter"
	joinDate := time.Now()
	
	success := guild.AddNewGuildMember(characterID, inviterName, joinDate, RankRecruit)
	if !success {
		t.Error("Should be able to add member")
	}

	// Get initial points
	member := guild.GetGuildMember(characterID)
	if member == nil {
		t.Fatal("Member should exist")
	}
	initialPoints := member.Points

	// Add points
	pointsToAdd := 100.0
	modifiedBy := "TestAdmin"
	comment := "Test point award"
	
	success = guild.AddPointsToGuildMember(characterID, pointsToAdd, modifiedBy, comment, false)
	if !success {
		t.Error("Should be able to add points to member")
	}

	// Check points were added
	member = guild.GetGuildMember(characterID)
	expectedPoints := initialPoints + pointsToAdd
	if member.Points != expectedPoints {
		t.Errorf("Expected points %f, got %f", expectedPoints, member.Points)
	}
}

// TestGuildConcurrency tests thread safety of guild operations
func TestGuildConcurrency(t *testing.T) {
	guild := NewGuild()
	guild.SetID(1)
	guild.SetName("Concurrent Test Guild", false)

	const numGoroutines = 20
	done := make(chan bool, numGoroutines)

	// Test concurrent reads
	for i := 0; i < numGoroutines; i++ {
		go func(id int) {
			_ = guild.GetID()
			_ = guild.GetName()
			_ = guild.GetLevel()
			_ = guild.GetMOTD()
			_ = guild.GetEXPCurrent()
			_ = guild.GetRecruitingShortDesc()
			_ = guild.GetRankName(RankMember)
			_ = guild.GetPermission(RankMember, PermissionInvite)
			done <- true
		}(i)
	}

	// Wait for all read operations
	for i := 0; i < numGoroutines; i++ {
		<-done
	}

	// Test concurrent member additions (smaller number to avoid conflicts)
	const memberGoroutines = 10
	for i := 0; i < memberGoroutines; i++ {
		go func(id int) {
			inviterName := fmt.Sprintf("Inviter%d", id)
			joinDate := time.Now()
			characterID := int32(100 + id)
			
			guild.AddNewGuildMember(characterID, inviterName, joinDate, RankRecruit)
			done <- true
		}(i)
	}

	// Wait for all member additions
	for i := 0; i < memberGoroutines; i++ {
		<-done
	}

	// Verify members were added
	members := guild.GetAllMembers()
	if len(members) != memberGoroutines {
		t.Logf("Expected %d members, got %d (some concurrent additions may have failed, which is acceptable)", memberGoroutines, len(members))
	}
}

// TestGuildEventFilters tests guild event filter system
func TestGuildEventFilters(t *testing.T) {
	guild := NewGuild()

	// Test setting event filters
	eventID := int8(EventMemberJoins)
	category := int8(EventFilterCategoryBroadcast)
	value := int8(1)
	
	success := guild.SetEventFilter(eventID, category, value, false, false)
	if !success {
		t.Error("Should be able to set event filter")
	}
	
	getValue := guild.GetEventFilter(eventID, category)
	if getValue != value {
		t.Errorf("Expected event filter value %d, got %d", value, getValue)
	}

	// Test removing event filter
	success = guild.SetEventFilter(eventID, category, 0, false, false)
	if !success {
		t.Error("Should be able to remove event filter")
	}
	
	getValue = guild.GetEventFilter(eventID, category)
	if getValue != 0 {
		t.Errorf("Expected event filter value 0 after removal, got %d", getValue)
	}
}

// TestGuildInfo tests the guild info structure
func TestGuildInfo(t *testing.T) {
	guild := NewGuild()
	guild.SetID(123)
	guild.SetName("Test Guild Info", false)
	guild.SetLevel(25, false)
	guild.SetMOTD("Test MOTD", false)

	info := guild.GetGuildInfo()
	
	if info.ID != 123 {
		t.Errorf("Expected guild info ID 123, got %d", info.ID)
	}
	
	if info.Name != "Test Guild Info" {
		t.Errorf("Expected guild info name 'Test Guild Info', got '%s'", info.Name)
	}
	
	if info.Level != 25 {
		t.Errorf("Expected guild info level 25, got %d", info.Level)
	}
	
	if info.MOTD != "Test MOTD" {
		t.Errorf("Expected guild info MOTD 'Test MOTD', got '%s'", info.MOTD)
	}
}